mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-29 14:31:35 +08:00
157 lines
5.1 KiB
TypeScript
157 lines
5.1 KiB
TypeScript
import { getEmbeddingMaxContext } from '@renderer/config/embedings'
|
|
import { usePreprocessProviders } from '@renderer/hooks/usePreprocess'
|
|
import { useProviders } from '@renderer/hooks/useProvider'
|
|
import { getModelUniqId } from '@renderer/services/ModelService'
|
|
import { KnowledgeBase } from '@renderer/types'
|
|
import { nanoid } from 'nanoid'
|
|
import { useCallback, useEffect, useMemo, useState } from 'react'
|
|
import { useTranslation } from 'react-i18next'
|
|
|
|
const createInitialKnowledgeBase = (): KnowledgeBase => ({
|
|
id: nanoid(),
|
|
name: '',
|
|
model: null as any, // model is required, but will be set by user interaction
|
|
items: [],
|
|
created_at: Date.now(),
|
|
updated_at: Date.now(),
|
|
version: 1
|
|
})
|
|
|
|
/**
|
|
* A hook that manages the state and handlers for a knowledge base form.
|
|
*
|
|
* The hook provides:
|
|
* - A state object `newBase` that tracks the current form values.
|
|
* - A function `setNewBase` to update the form state.
|
|
* - A set of handlers for various form actions:
|
|
* - `handleEmbeddingModelChange`: Updates the embedding model.
|
|
* - `handleRerankModelChange`: Updates the rerank model.
|
|
* - `handleDimensionChange`: Updates the dimensions.
|
|
* - `handleDocPreprocessChange`: Updates the document preprocess provider.
|
|
* - `handleChunkSizeChange`: Updates the chunk size.
|
|
* - `handleChunkOverlapChange`: Updates the chunk overlap.
|
|
* - `handleThresholdChange`: Updates the threshold.
|
|
* @param base - The base knowledge base to use as the initial state. If not provided, an empty base will be used.
|
|
* @returns An object containing the new base state, a function to update the base, and handlers for various form actions.
|
|
* Also includes provider data for dropdown options and selected provider.
|
|
*/
|
|
export const useKnowledgeBaseForm = (base?: KnowledgeBase) => {
|
|
const { t } = useTranslation()
|
|
const [newBase, setNewBase] = useState<KnowledgeBase>(base || createInitialKnowledgeBase())
|
|
const { providers } = useProviders()
|
|
const { preprocessProviders } = usePreprocessProviders()
|
|
|
|
useEffect(() => {
|
|
if (base) {
|
|
setNewBase(base)
|
|
}
|
|
}, [base])
|
|
|
|
const selectedDocPreprocessProvider = useMemo(
|
|
() => newBase.preprocessProvider?.provider,
|
|
[newBase.preprocessProvider]
|
|
)
|
|
|
|
const docPreprocessSelectOptions = useMemo(() => {
|
|
const preprocessOptions = {
|
|
label: t('settings.tool.preprocess.provider'),
|
|
title: t('settings.tool.preprocess.provider'),
|
|
options: preprocessProviders
|
|
.filter((p) => p.apiKey !== '' || p.id === 'mineru')
|
|
.map((p) => ({ value: p.id, label: p.name }))
|
|
}
|
|
return [preprocessOptions]
|
|
}, [preprocessProviders, t])
|
|
|
|
const handleEmbeddingModelChange = useCallback(
|
|
(value: string) => {
|
|
const model = providers.flatMap((p) => p.models).find((m) => getModelUniqId(m) === value)
|
|
if (model) {
|
|
setNewBase((prev) => ({ ...prev, model }))
|
|
}
|
|
},
|
|
[providers]
|
|
)
|
|
|
|
const handleRerankModelChange = useCallback(
|
|
(value: string) => {
|
|
const rerankModel = value
|
|
? providers.flatMap((p) => p.models).find((m) => getModelUniqId(m) === value)
|
|
: undefined
|
|
setNewBase((prev) => ({ ...prev, rerankModel }))
|
|
},
|
|
[providers]
|
|
)
|
|
|
|
const handleDimensionChange = useCallback((value: number | null) => {
|
|
setNewBase((prev) => ({ ...prev, dimensions: value || undefined }))
|
|
}, [])
|
|
|
|
const handleDocPreprocessChange = useCallback(
|
|
(value: string) => {
|
|
const provider = preprocessProviders.find((p) => p.id === value)
|
|
if (!provider) {
|
|
setNewBase((prev) => ({ ...prev, preprocessProvider: undefined }))
|
|
return
|
|
}
|
|
setNewBase((prev) => ({
|
|
...prev,
|
|
preprocessProvider: {
|
|
type: 'preprocess',
|
|
provider
|
|
}
|
|
}))
|
|
},
|
|
[preprocessProviders]
|
|
)
|
|
|
|
const handleChunkSizeChange = useCallback(
|
|
(value: number | null) => {
|
|
const modelId = newBase.model?.id || base?.model?.id
|
|
if (!modelId) return
|
|
const maxContext = getEmbeddingMaxContext(modelId)
|
|
if (!value || !maxContext || value <= maxContext) {
|
|
setNewBase((prev) => ({ ...prev, chunkSize: value || undefined }))
|
|
}
|
|
},
|
|
[newBase.model, base?.model]
|
|
)
|
|
|
|
const handleChunkOverlapChange = useCallback(
|
|
(value: number | null) => {
|
|
if (!value || (newBase.chunkSize && newBase.chunkSize > value)) {
|
|
setNewBase((prev) => ({ ...prev, chunkOverlap: value || undefined }))
|
|
} else {
|
|
window.toast.error(t('message.error.chunk_overlap_too_large'))
|
|
}
|
|
},
|
|
[newBase.chunkSize, t]
|
|
)
|
|
|
|
const handleThresholdChange = useCallback(
|
|
(value: number | null) => {
|
|
setNewBase((prev) => ({ ...prev, threshold: value || undefined }))
|
|
},
|
|
[setNewBase]
|
|
)
|
|
|
|
const handlers = {
|
|
handleEmbeddingModelChange,
|
|
handleRerankModelChange,
|
|
handleDimensionChange,
|
|
handleDocPreprocessChange,
|
|
handleChunkSizeChange,
|
|
handleChunkOverlapChange,
|
|
handleThresholdChange
|
|
}
|
|
|
|
const providerData = {
|
|
providers,
|
|
preprocessProviders,
|
|
selectedDocPreprocessProvider,
|
|
docPreprocessSelectOptions
|
|
}
|
|
|
|
return { newBase, setNewBase, handlers, providerData }
|
|
}
|