import { isEqual } from "lodash-es"; import { EyeIcon, EyeOffIcon } from "lucide-react"; import { observer } from "mobx-react-lite"; import { useState } from "react"; import { toast } from "react-hot-toast"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; 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 { workspaceStore } from "@/store"; import { workspaceSettingNamePrefix } from "@/store/common"; import { WorkspaceSetting_AiSetting, WorkspaceSetting_Key } from "@/types/proto/api/v1/workspace_service"; import { useTranslate } from "@/utils/i18n"; import TagRecommendationSection from "./TagRecommendationSection"; const AISettings = observer(() => { const t = useTranslate(); const [originalSetting, setOriginalSetting] = useState(workspaceStore.state.aiSetting); const [aiSetting, setAiSetting] = useState(originalSetting); const [showApiKey, setShowApiKey] = useState(false); const updatePartialSetting = (partial: Partial) => { setAiSetting(WorkspaceSetting_AiSetting.fromPartial({ ...aiSetting, ...partial })); }; const saveSetting = async (settingToSave: WorkspaceSetting_AiSetting) => { try { await workspaceStore.updateWorkspaceSetting({ name: `${workspaceSettingNamePrefix}${WorkspaceSetting_Key.AI}`, aiSetting: settingToSave, }); setOriginalSetting(settingToSave); setAiSetting(settingToSave); toast.success(t("message.update-succeed")); } catch (error: any) { console.error(error); toast.error(error.response?.data?.message || error.message || t("message.update-failed")); } }; const updateEnableAI = async (enabled: boolean) => { const newSetting = WorkspaceSetting_AiSetting.fromPartial({ ...aiSetting, enableAi: enabled }); await saveSetting(newSetting); }; const updateSetting = async () => { if (aiSetting.enableAi && (!aiSetting.apiKey || !aiSetting.model)) { toast.error(t("setting.ai-section.api-key-model-required")); return; } const settingToSave = WorkspaceSetting_AiSetting.fromPartial({ ...aiSetting, baseUrl: aiSetting.baseUrl || "https://api.openai.com/v1", timeoutSeconds: aiSetting.timeoutSeconds || 10, }); await saveSetting(settingToSave); }; const resetSetting = () => setAiSetting(originalSetting); // 只比较全局AI配置的变化,不包括子功能配置 const globalSettingChanged = !isEqual( { enableAi: originalSetting.enableAi, baseUrl: originalSetting.baseUrl, apiKey: originalSetting.apiKey, model: originalSetting.model, timeoutSeconds: originalSetting.timeoutSeconds, }, { enableAi: aiSetting.enableAi, baseUrl: aiSetting.baseUrl, apiKey: aiSetting.apiKey, model: aiSetting.model, timeoutSeconds: aiSetting.timeoutSeconds, }, ); const handleTagRecommendationChange = (newSetting: WorkspaceSetting_AiSetting) => { setOriginalSetting(newSetting); setAiSetting(newSetting); }; return (
{/* Global AI Settings */}
{t("setting.ai-section.title")} {aiSetting.enableAi ? t("common.enabled") : t("common.disabled")}

{t("setting.ai-section.description")}

{/* Enable AI Toggle */}
{t("setting.ai-section.enable-ai-description")}
{/* AI Global Configuration Fields */} {aiSetting.enableAi && ( <>
updatePartialSetting({ baseUrl: e.target.value })} /> {t("setting.ai-section.base-url-description")}
updatePartialSetting({ apiKey: e.target.value })} autoComplete="off" style={ showApiKey ? {} : ({ WebkitTextSecurity: "disc", fontFamily: "text-security-disc, -webkit-small-control", } as React.CSSProperties) } className="pr-10" />
{t("setting.ai-section.api-key-description")}
updatePartialSetting({ model: e.target.value })} /> {t("setting.ai-section.model-description")}
updatePartialSetting({ timeoutSeconds: parseInt(e.target.value) || 10 })} /> {t("setting.ai-section.timeout-description")}
)}
{/* Action Buttons */} {aiSetting.enableAi && (
)}
{/* AI Features Section */} {aiSetting.enableAi && ( <>
{t("setting.ai-features")}
)}
); }); export default AISettings;