feat: change add knowledge ui
This commit is contained in:
@@ -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')
|
||||
|
||||
Reference in New Issue
Block a user