feat: change add knowledge ui

This commit is contained in:
eeee0717
2025-06-03 21:50:34 +08:00
parent 777eb48e6e
commit 1c9bba353b
@@ -1,33 +1,29 @@
import { InfoCircleOutlined, SettingOutlined, WarningOutlined } from '@ant-design/icons'
import { TopView } from '@renderer/components/TopView'
import { DEFAULT_KNOWLEDGE_DOCUMENT_COUNT } from '@renderer/config/constant'
import { getEmbeddingMaxContext } from '@renderer/config/embedings'
import { isEmbeddingModel, isRerankModel } from '@renderer/config/models'
import { NOT_SUPPORTED_REANK_PROVIDERS } from '@renderer/config/providers'
// import { SUPPORTED_REANK_PROVIDERS } from '@renderer/config/providers'
import { useKnowledgeBases } from '@renderer/hooks/useKnowledge'
import { usePreprocessProviders } from '@renderer/hooks/usePreprocess'
import { useProviders } from '@renderer/hooks/useProvider'
import { SettingHelpText } from '@renderer/pages/settings'
import AiProvider from '@renderer/providers/AiProvider'
import { getKnowledgeBaseParams } from '@renderer/services/KnowledgeService'
import { getModelUniqId } from '@renderer/services/ModelService'
import { Model } from '@renderer/types'
import { KnowledgeBase, Model, PreprocessProvider } from '@renderer/types'
import { getErrorMessage } from '@renderer/utils/error'
import { Form, Input, Modal, Select, Slider } from 'antd'
import { Alert, Input, InputNumber, Modal, Select, Slider, Tabs, TabsProps, Tooltip } from 'antd'
import { find, sortBy } from 'lodash'
import { nanoid } from 'nanoid'
import { useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
interface ShowParams {
title: string
}
interface FormData {
name: string
model: string
rerankModel: string | undefined
documentCount: number | undefined
}
interface Props extends ShowParams {
resolve: (data: any) => void
}
@@ -35,10 +31,13 @@ interface Props extends ShowParams {
const PopupContainer: React.FC<Props> = ({ title, resolve }) => {
const [open, setOpen] = useState(true)
const [loading, setLoading] = useState(false)
const [form] = Form.useForm<FormData>()
const { t } = useTranslation()
const { providers } = useProviders()
const { addKnowledgeBase } = useKnowledgeBases()
const [newBase, setNewBase] = useState<KnowledgeBase>({} as KnowledgeBase)
const { preprocessProviders } = usePreprocessProviders()
const [selectedProvider, setSelectedProvider] = useState<PreprocessProvider | undefined>(undefined)
const embeddingModels = useMemo(() => {
return providers
@@ -92,12 +91,10 @@ const PopupContainer: React.FC<Props> = ({ title, resolve }) => {
const onOk = async () => {
try {
const values = await form.validateFields()
const selectedEmbeddingModel = find(embeddingModels, JSON.parse(values.model)) as Model
// const values = await form.validateFields()
const selectedEmbeddingModel = find(embeddingModels, newBase.model) as Model
const selectedRerankModel = values.rerankModel
? (find(rerankModels, JSON.parse(values.rerankModel)) as Model)
: undefined
const selectedRerankModel = newBase.rerankModel ? (find(rerankModels, newBase.rerankModel) as Model) : undefined
if (selectedEmbeddingModel) {
setLoading(true)
@@ -119,24 +116,25 @@ const PopupContainer: React.FC<Props> = ({ title, resolve }) => {
return
}
const newBase = {
const _newBase = {
...newBase,
id: nanoid(),
name: values.name,
name: newBase.name,
model: selectedEmbeddingModel,
rerankModel: selectedRerankModel,
dimensions,
documentCount: values.documentCount || DEFAULT_KNOWLEDGE_DOCUMENT_COUNT,
documentCount: newBase.documentCount || DEFAULT_KNOWLEDGE_DOCUMENT_COUNT,
items: [],
created_at: Date.now(),
updated_at: Date.now(),
version: 1
}
await window.api.knowledgeBase.create(getKnowledgeBaseParams(newBase))
await window.api.knowledgeBase.create(getKnowledgeBaseParams(_newBase))
addKnowledgeBase(newBase as any)
addKnowledgeBase(_newBase as any)
setOpen(false)
resolve(newBase)
resolve(_newBase)
}
} catch (error) {
console.error('Validation failed:', error)
@@ -151,8 +149,176 @@ const PopupContainer: React.FC<Props> = ({ title, resolve }) => {
resolve(null)
}
const settingItems: TabsProps['items'] = [
{
key: '1',
label: t('settings.general'),
children: (
<SettingsPanel>
<SettingsItem>
<div className="settings-label">{t('common.name')}</div>
<Input
placeholder={t('common.name')}
onChange={(e) => {
if (e.target.value) {
setNewBase({ ...newBase, name: e.target.value })
}
}}
/>
</SettingsItem>
<SettingsItem>
<div className="settings-label">
{t('models.embedding_model')}
<Tooltip title={t('models.embedding_model_tooltip')} placement="right">
<InfoCircleOutlined style={{ marginLeft: 8 }} />
</Tooltip>
</div>
<Select
style={{ width: '100%' }}
options={embeddingSelectOptions}
placeholder={t('settings.models.empty')}
/>
</SettingsItem>
<SettingsItem>
<div className="settings-label">
{t('models.rerank_model')}
<Tooltip title={t('models.rerank_model_tooltip')} placement="right">
<InfoCircleOutlined style={{ marginLeft: 8 }} />
</Tooltip>
</div>
<Select
style={{ width: '100%' }}
options={rerankSelectOptions}
placeholder={t('settings.models.empty')}
onChange={(value) => {
const rerankModel = value
? providers.flatMap((p) => p.models).find((m) => getModelUniqId(m) === value)
: undefined
setNewBase({ ...newBase, rerankModel })
}}
allowClear
/>
</SettingsItem>
<SettingsItem>
<div className="settings-label">{t('settings.tool.preprocess.title')}</div>
<Select
value={selectedProvider?.id}
style={{ width: '100%' }}
onChange={(value: string) => {
const provider = preprocessProviders.find((p) => p.id === value)
if (!provider) return
setSelectedProvider(provider)
setNewBase({ ...newBase, preprocessProvider: provider })
}}
placeholder={t('settings.tool.preprocess.provider_placeholder')}
options={preprocessProviders.filter((p) => p.apiKey !== '').map((p) => ({ value: p.id, label: p.name }))}
allowClear
/>
</SettingsItem>
<SettingsItem>
<div className="settings-label">
{t('knowledge.document_count')}
<Tooltip title={t('knowledge.document_count_help')}>
<InfoCircleOutlined style={{ marginLeft: 8 }} />
</Tooltip>
</div>
<Slider
style={{ width: '100%' }}
min={1}
max={30}
step={1}
defaultValue={DEFAULT_KNOWLEDGE_DOCUMENT_COUNT}
marks={{ 1: '1', 6: t('knowledge.document_count_default'), 30: '30' }}
onChange={(value) => setNewBase({ ...newBase, documentCount: value })}
/>
</SettingsItem>
</SettingsPanel>
),
icon: <SettingOutlined />
},
{
key: '2',
label: t('settings.advanced.title'),
children: (
<SettingsPanel>
<SettingsItem>
<div className="settings-label">
{t('knowledge.chunk_size')}
<Tooltip title={t('knowledge.chunk_size_tooltip')} placement="right">
<InfoCircleOutlined style={{ marginLeft: 8 }} />
</Tooltip>
</div>
<InputNumber
style={{ width: '100%' }}
min={100}
value={newBase.chunkSize}
placeholder={t('knowledge.chunk_size_placeholder')}
onChange={(value) => {
const maxContext = getEmbeddingMaxContext(newBase.model.id)
if (!value || !maxContext || value <= maxContext) {
setNewBase({ ...newBase, chunkSize: value || undefined })
}
}}
/>
</SettingsItem>
<SettingsItem>
<div className="settings-label">
{t('knowledge.chunk_overlap')}
<Tooltip title={t('knowledge.chunk_overlap_tooltip')} placement="right">
<InfoCircleOutlined style={{ marginLeft: 8 }} />
</Tooltip>
</div>
<InputNumber
style={{ width: '100%' }}
min={0}
value={newBase.chunkOverlap}
placeholder={t('knowledge.chunk_overlap_placeholder')}
onChange={async (value) => {
if (!value || (newBase.chunkSize && newBase.chunkSize > value)) {
setNewBase({ ...newBase, chunkOverlap: value || undefined })
}
await window.message.error(t('message.error.chunk_overlap_too_large'))
}}
/>
</SettingsItem>
<SettingsItem>
<div className="settings-label">
{t('knowledge.threshold')}
<Tooltip title={t('knowledge.threshold_tooltip')} placement="right">
<InfoCircleOutlined style={{ marginLeft: 8 }} />
</Tooltip>
</div>
<InputNumber
style={{ width: '100%' }}
step={0.1}
min={0}
max={1}
value={newBase.threshold}
placeholder={t('knowledge.threshold_placeholder')}
onChange={(value) => setNewBase({ ...newBase, threshold: value || undefined })}
/>
</SettingsItem>
<Alert
message={t('knowledge.chunk_size_change_warning')}
type="warning"
showIcon
icon={<WarningOutlined />}
/>
</SettingsPanel>
),
icon: <SettingOutlined />
}
]
return (
<Modal
<SettingsModal
title={title}
open={open}
onOk={onOk}
@@ -162,7 +328,7 @@ const PopupContainer: React.FC<Props> = ({ title, resolve }) => {
destroyOnClose
centered
okButtonProps={{ loading }}>
<Form form={form} layout="vertical">
{/* <Form form={form} layout="vertical">
<Form.Item
name="name"
label={t('common.name')}
@@ -203,11 +369,50 @@ const PopupContainer: React.FC<Props> = ({ title, resolve }) => {
marks={{ 1: '1', 6: t('knowledge.document_count_default'), 30: '30' }}
/>
</Form.Item>
</Form>
</Modal>
</Form> */}
<div>
<Tabs style={{ minHeight: '50vh' }} defaultActiveKey="1" tabPosition={'left'} items={settingItems} />
</div>
</SettingsModal>
)
}
const SettingsPanel = styled.div`
padding: 0 16px;
`
const SettingsItem = styled.div`
margin-bottom: 24px;
.settings-label {
font-size: 14px;
margin-bottom: 8px;
display: flex;
align-items: center;
}
`
const SettingsModal = styled(Modal)`
.ant-modal {
width: auto !important;
height: auto !important;
}
.ant-modal-content {
min-height: 60vh;
width: 50vw;
display: flex;
flex-direction: column;
.ant-modal-body {
flex: 1;
max-height: auto;
}
}
.ant-tabs-tab {
padding-inline-start: 0px !important;
}
`
export default class AddKnowledgePopup {
static hide() {
TopView.hide('AddKnowledgePopup')