From 30202db710c469cd73211d4f1aebe15daa4f1755 Mon Sep 17 00:00:00 2001 From: ChaoLiu Date: Mon, 18 Aug 2025 17:14:51 +0800 Subject: [PATCH] feat: implement AI connection test in frontend - Add test connection button with loading states in AI settings - Integrate connection test API with proper error handling - Update tag recommendation settings with improved UI Signed-off-by: ChaoLiu --- web/src/components/Settings/AISettings.tsx | 51 ++++++++++++++++--- .../Settings/TagRecommendationSection.tsx | 4 +- 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/web/src/components/Settings/AISettings.tsx b/web/src/components/Settings/AISettings.tsx index f1689e454..42af19fae 100644 --- a/web/src/components/Settings/AISettings.tsx +++ b/web/src/components/Settings/AISettings.tsx @@ -9,6 +9,7 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Separator } from "@/components/ui/separator"; import { Switch } from "@/components/ui/switch"; +import { workspaceServiceClient } from "@/grpcweb"; import { workspaceStore } from "@/store"; import { workspaceSettingNamePrefix } from "@/store/common"; import { WorkspaceSetting_AiSetting, WorkspaceSetting_Key } from "@/types/proto/api/v1/workspace_service"; @@ -20,6 +21,7 @@ const AISettings = observer(() => { const [originalSetting, setOriginalSetting] = useState(workspaceStore.state.aiSetting); const [aiSetting, setAiSetting] = useState(originalSetting); const [showApiKey, setShowApiKey] = useState(false); + const [isTestingConnection, setIsTestingConnection] = useState(false); const updatePartialSetting = (partial: Partial) => { setAiSetting(WorkspaceSetting_AiSetting.fromPartial({ ...aiSetting, ...partial })); @@ -62,6 +64,34 @@ const AISettings = observer(() => { const resetSetting = () => setAiSetting(originalSetting); + const testConnection = async () => { + if (!aiSetting.baseUrl || !aiSetting.apiKey || !aiSetting.model) { + toast.error(t("setting.ai-section.test-connection-incomplete")); + return; + } + + setIsTestingConnection(true); + try { + const response = await workspaceServiceClient.testAiConnection({ + baseUrl: aiSetting.baseUrl || "https://api.openai.com/v1", + apiKey: aiSetting.apiKey, + model: aiSetting.model, + timeoutSeconds: aiSetting.timeoutSeconds || 10, + }); + + if (response.success) { + toast.success(t("setting.ai-section.test-connection-success")); + } else { + toast.error(`${t("setting.ai-section.test-connection-failed")}: ${response.message}`); + } + } catch (error: any) { + console.error("AI connection test failed:", error); + toast.error(`${t("setting.ai-section.test-connection-failed")}: ${error.message || error}`); + } finally { + setIsTestingConnection(false); + } + }; + // 只比较全局AI配置的变化,不包括子功能配置 const globalSettingChanged = !isEqual( { @@ -188,13 +218,22 @@ const AISettings = observer(() => { {/* Action Buttons */} {aiSetting.enableAi && ( -
- - +
+ + +
)} diff --git a/web/src/components/Settings/TagRecommendationSection.tsx b/web/src/components/Settings/TagRecommendationSection.tsx index 98de5d098..fc802eb93 100644 --- a/web/src/components/Settings/TagRecommendationSection.tsx +++ b/web/src/components/Settings/TagRecommendationSection.tsx @@ -46,7 +46,7 @@ const TagRecommendationSection = observer(({ aiSetting, onSettingChange, disable systemPrompt: "", requestsPerMinute: 10, }); - + setOriginalTagConfig(newTagConfig); setTagConfig(newTagConfig); }, [aiSetting]); @@ -80,7 +80,7 @@ const TagRecommendationSection = observer(({ aiSetting, onSettingChange, disable name: `${workspaceSettingNamePrefix}${WorkspaceSetting_Key.AI}`, aiSetting: newAiSetting, }); - + setOriginalTagConfig(newTagConfig); setTagConfig(newTagConfig); onSettingChange(newAiSetting);