From 76288377c36ed17d1d63f99027b67109c1c9d4cf Mon Sep 17 00:00:00 2001 From: duanfuxiang Date: Tue, 18 Mar 2025 09:39:25 +0800 Subject: [PATCH] update models settings, add custom openai compatible --- src/core/llm/manager.ts | 4 + .../components/ProviderModelsPicker.tsx | 107 +++++++++++------- 2 files changed, 67 insertions(+), 44 deletions(-) diff --git a/src/core/llm/manager.ts b/src/core/llm/manager.ts index 3c7b97d..fdf488c 100644 --- a/src/core/llm/manager.ts +++ b/src/core/llm/manager.ts @@ -44,6 +44,7 @@ class LLMManager implements LLMManagerInterface { private siliconflowProvider: OpenAICompatibleProvider private alibabaQwenProvider: OpenAICompatibleProvider private ollamaProvider: OllamaProvider + private openaiCompatibleProvider: OpenAICompatibleProvider private isInfioEnabled: boolean constructor(settings: InfioSettings) { @@ -57,6 +58,7 @@ class LLMManager implements LLMManagerInterface { this.googleProvider = new GeminiProvider(settings.googleProvider.apiKey) this.groqProvider = new GroqProvider(settings.groqProvider.apiKey) this.ollamaProvider = new OllamaProvider(settings.groqProvider.baseUrl) + this.openaiCompatibleProvider = new OpenAICompatibleProvider(settings.openaicompatibleProvider.apiKey, settings.openaicompatibleProvider.baseUrl) this.isInfioEnabled = !!settings.infioProvider.apiKey } @@ -160,6 +162,8 @@ class LLMManager implements LLMManagerInterface { return await this.groqProvider.streamResponse(model, request, options) case ApiProvider.Ollama: return await this.ollamaProvider.streamResponse(model, request, options) + case ApiProvider.OpenAICompatible: + return await this.openaiCompatibleProvider.streamResponse(model, request, options) } } } diff --git a/src/settings/components/ProviderModelsPicker.tsx b/src/settings/components/ProviderModelsPicker.tsx index 76ccea4..9f394ed 100644 --- a/src/settings/components/ProviderModelsPicker.tsx +++ b/src/settings/components/ProviderModelsPicker.tsx @@ -160,10 +160,10 @@ export const ComboBoxComponent: React.FC = ({ isEmbedding = false, updateModel, }) => { - // 提供商选择状态 + // provider state const [modelProvider, setModelProvider] = useState(provider); - // 搜索输入状态 + // search state const [searchTerm, setSearchTerm] = useState(""); const [isOpen, setIsOpen] = useState(false); const [selectedIndex, setSelectedIndex] = useState(0); @@ -175,12 +175,12 @@ export const ComboBoxComponent: React.FC = ({ // Replace useMemo with useEffect for async fetching useEffect(() => { const fetchModelIds = async () => { - const ids = isEmbedding - ? GetEmbeddingProviderModelIds(modelProvider) + const ids = isEmbedding + ? GetEmbeddingProviderModelIds(modelProvider) : await GetProviderModelIds(modelProvider); setModelIds(ids); }; - + fetchModelIds(); }, [modelProvider, isEmbedding]); @@ -191,7 +191,7 @@ export const ComboBoxComponent: React.FC = ({ })) }, [modelIds]) - // 初始化 fuse,用于模糊搜索,简单配置 threshold 可按需调整 + // fuse, used for fuzzy search, simple configuration threshold can be adjusted as needed const fuse: Fuse = useMemo(() => { return new Fuse(searchableItems, { keys: ["html"], @@ -218,7 +218,7 @@ export const ComboBoxComponent: React.FC = ({ const listRef = useRef(null); const itemRefs = useRef>([]); - // 当选中项发生变化时,滚动到可视区域内 + // when selected index changes, scroll to visible area useEffect(() => { if (itemRefs.current[selectedIndex]) { itemRefs.current[selectedIndex]?.scrollIntoView({ @@ -234,7 +234,7 @@ export const ComboBoxComponent: React.FC = ({
- {modelId} + [{modelProvider}]{modelId}
= ({ ))} - { - setSearchTerm(e.target.value); - setSelectedIndex(0); - }} - onKeyDown={(e) => { - switch (e.key) { - case "ArrowDown": - e.preventDefault(); - setSelectedIndex((prev) => - Math.min(prev + 1, filteredOptions.length - 1) - ); - break; - case "ArrowUp": - e.preventDefault(); - setSelectedIndex((prev) => Math.max(prev - 1, 0)); - break; - case "Enter": { - e.preventDefault(); - const selectedOption = filteredOptions[selectedIndex]; - if (selectedOption) { - updateModel(modelProvider, selectedOption.id); + {modelIds.length > 0 ? ( + { + setSearchTerm(e.target.value); + setSelectedIndex(0); + }} + onKeyDown={(e) => { + switch (e.key) { + case "ArrowDown": + e.preventDefault(); + setSelectedIndex((prev) => + Math.min(prev + 1, filteredOptions.length - 1) + ); + break; + case "ArrowUp": + e.preventDefault(); + setSelectedIndex((prev) => Math.max(prev - 1, 0)); + break; + case "Enter": { + e.preventDefault(); + const selectedOption = filteredOptions[selectedIndex]; + if (selectedOption) { + updateModel(modelProvider, selectedOption.id); + setSearchTerm(""); + setIsOpen(false); + } + break; + } + case "Escape": + e.preventDefault(); + setIsOpen(false); setSearchTerm(""); + break; + } + }} + /> + ) : ( + { + setSearchTerm(e.target.value); + }} + onKeyDown={(e) => { + if (e.key === "Enter") { + e.preventDefault(); + updateModel(modelProvider, searchTerm); setIsOpen(false); } - break; - } - case "Escape": - e.preventDefault(); - setIsOpen(false); - setSearchTerm(""); - break; - } - }} - /> + }} + /> + )} {filteredOptions.map((option, index) => (