From 6c453ead590a76bb200e6d433326fd9a15657701 Mon Sep 17 00:00:00 2001 From: MarkLo Date: Sun, 7 Dec 2025 22:35:18 +0800 Subject: [PATCH] --- frontend/components/analysis/AnalysisForm.tsx | 118 ++++++++++++++++-- frontend/package.json | 2 +- frontend/pnpm-lock.yaml | 2 +- 3 files changed, 112 insertions(+), 10 deletions(-) diff --git a/frontend/components/analysis/AnalysisForm.tsx b/frontend/components/analysis/AnalysisForm.tsx index 571c93dc..2b9cb332 100644 --- a/frontend/components/analysis/AnalysisForm.tsx +++ b/frontend/components/analysis/AnalysisForm.tsx @@ -52,6 +52,10 @@ const formSchema = z.object({ research_depth: z.number().int().min(1).max(5), quick_think_llm: z.string().min(1, "請選擇快速思維模型"), deep_think_llm: z.string().min(1, "請選擇深層思維模型"), + + // Custom model names (when "custom" is selected) + custom_quick_think_model: z.string().optional(), + custom_deep_think_model: z.string().optional(), // API Configuration (hidden from UI, populated from localStorage) quick_think_base_url: z @@ -97,6 +101,8 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) { research_depth: 3, // 預設中等層級 quick_think_llm: "gpt-5-mini-2025-08-07", deep_think_llm: "gpt-5-mini-2025-08-07", + custom_quick_think_model: "", + custom_deep_think_model: "", quick_think_base_url: "https://api.openai.com/v1", deep_think_base_url: "https://api.openai.com/v1", quick_think_api_key: "", @@ -110,22 +116,34 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) { // Load API settings from localStorage and update when models change const quickThinkLlm = form.watch("quick_think_llm"); const deepThinkLlm = form.watch("deep_think_llm"); + const isQuickThinkCustom = quickThinkLlm === "custom"; + const isDeepThinkCustom = deepThinkLlm === "custom"; useEffect(() => { const savedSettings = getApiSettings(); - // Set base URLs based on selected models (custom URL takes precedence) - form.setValue("quick_think_base_url", getBaseUrlForModel(quickThinkLlm, savedSettings.custom_base_url)); - form.setValue("deep_think_base_url", getBaseUrlForModel(deepThinkLlm, savedSettings.custom_base_url)); - form.setValue("embedding_base_url", savedSettings.custom_base_url || "https://api.openai.com/v1"); + // For custom models, always use custom base URL and API key + if (isQuickThinkCustom) { + form.setValue("quick_think_base_url", savedSettings.custom_base_url || ""); + form.setValue("quick_think_api_key", savedSettings.custom_api_key || ""); + } else { + form.setValue("quick_think_base_url", getBaseUrlForModel(quickThinkLlm, savedSettings.custom_base_url)); + form.setValue("quick_think_api_key", getApiKeyForModel(quickThinkLlm, savedSettings)); + } + + if (isDeepThinkCustom) { + form.setValue("deep_think_base_url", savedSettings.custom_base_url || ""); + form.setValue("deep_think_api_key", savedSettings.custom_api_key || ""); + } else { + form.setValue("deep_think_base_url", getBaseUrlForModel(deepThinkLlm, savedSettings.custom_base_url)); + form.setValue("deep_think_api_key", getApiKeyForModel(deepThinkLlm, savedSettings)); + } - // Set API keys based on selected models - form.setValue("quick_think_api_key", getApiKeyForModel(quickThinkLlm, savedSettings)); - form.setValue("deep_think_api_key", getApiKeyForModel(deepThinkLlm, savedSettings)); + form.setValue("embedding_base_url", savedSettings.custom_base_url || "https://api.openai.com/v1"); form.setValue("embedding_api_key", savedSettings.custom_api_key || savedSettings.openai_api_key); form.setValue("alpha_vantage_api_key", savedSettings.alpha_vantage_api_key); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [quickThinkLlm, deepThinkLlm]); + }, [quickThinkLlm, deepThinkLlm, isQuickThinkCustom, isDeepThinkCustom]); // 全選/取消全選 const toggleSelectAll = () => { @@ -141,8 +159,36 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) { }; function handleSubmit(values: z.infer) { + // Use custom model names if "custom" is selected + const finalQuickThinkLlm = values.quick_think_llm === "custom" + ? values.custom_quick_think_model || "" + : values.quick_think_llm; + + const finalDeepThinkLlm = values.deep_think_llm === "custom" + ? values.custom_deep_think_model || "" + : values.deep_think_llm; + + // Validate custom model names + if (values.quick_think_llm === "custom" && !values.custom_quick_think_model) { + form.setError("custom_quick_think_model", { + type: "manual", + message: "請輸入快速思維模型的完整名稱" + }); + return; + } + + if (values.deep_think_llm === "custom" && !values.custom_deep_think_model) { + form.setError("custom_deep_think_model", { + type: "manual", + message: "請輸入深層思維模型的完整名稱" + }); + return; + } + const request: AnalysisRequest = { ...values, + quick_think_llm: finalQuickThinkLlm, + deep_think_llm: finalDeepThinkLlm, }; onSubmit(request); } @@ -406,6 +452,11 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) { Qwen: Flash + + {/* Custom Model */} + + Other(自訂模型) + 快速回應模型 @@ -413,6 +464,29 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) { )} /> + + {/* Custom Quick Think Model Input */} + {isQuickThinkCustom && ( + ( + + 自訂快速思維模型名稱 + + + + + 請輸入完整的模型名稱(此模型將使用自訂端點) + + + + )} + /> + )} Qwen: Flash + + {/* Custom Model */} + + Other(自訂模型) + 複雜推理模型 @@ -524,6 +603,29 @@ export function AnalysisForm({ onSubmit, loading = false }: AnalysisFormProps) { )} /> + + {/* Custom Deep Think Model Input */} + {isDeepThinkCustom && ( + ( + + 自訂深層思維模型名稱 + + + + + 請輸入完整的模型名稱(此模型將使用自訂端點) + + + + )} + /> + )} diff --git a/frontend/package.json b/frontend/package.json index 70f9c3c6..ea75fd1c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -28,7 +28,7 @@ "react": "19.2.0", "react-day-picker": "^9.11.3", "react-dom": "19.2.0", - "react-hook-form": "^7.67.0", + "react-hook-form": "^7.68.0", "react-markdown": "^10.1.0", "recharts": "^3.4.1", "rehype-raw": "^7.0.0", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 97b69c5c..1346c61f 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -66,7 +66,7 @@ importers: specifier: 19.2.0 version: 19.2.0(react@19.2.0) react-hook-form: - specifier: ^7.67.0 + specifier: ^7.68.0 version: 7.68.0(react@19.2.0) react-markdown: specifier: ^10.1.0