From be0da3449c3341cb28f27fe10029c9e91daab0c3 Mon Sep 17 00:00:00 2001 From: Xin Wang Date: Tue, 9 Jun 2026 12:59:07 +0800 Subject: [PATCH] Enhance OpenCode form handling in AssistantPage - Introduce a new model field in the OpenCode form to manage language model selection. - Refactor the form handling logic to improve data loading and error management for OpenCode assistants. - Update UI components to utilize ResourceSelectField for model and voice configuration, enhancing user experience. - Clear form fields when creating new OpenCode entries to ensure a fresh start for users. --- .../src/components/pages/AssistantPage.tsx | 136 +++++++++++------- 1 file changed, 88 insertions(+), 48 deletions(-) diff --git a/frontend/src/components/pages/AssistantPage.tsx b/frontend/src/components/pages/AssistantPage.tsx index 21f278e..cd6be22 100644 --- a/frontend/src/components/pages/AssistantPage.tsx +++ b/frontend/src/components/pages/AssistantPage.tsx @@ -113,6 +113,7 @@ type OpenCodeForm = { prompt: string; apiUrl: string; apiKey: string; + model: string; asr: string; voice: string; enableInterrupt: boolean; @@ -243,6 +244,7 @@ export function AssistantPage() { "你是一个代码助手的语音交互界面,请用简洁、口语化的方式回答用户关于代码与工程的问题。", apiUrl: "http://localhost:4096", apiKey: "", + model: "", asr: "", voice: "", enableInterrupt: true, @@ -385,8 +387,15 @@ export function AssistantPage() { setListError(error instanceof Error ? error.message : "加载助手失败"); } } else if (assistant.type === "OpenCode") { - updateOpenCodeForm("name", assistant.name); - setView("create-opencode"); + void loadResources(); + setSaveError(null); + setEditingId(assistant.id); + try { + fillOpenCodeForm(await assistantsApi.get(assistant.id)); + setView("create-opencode"); + } catch (error) { + setListError(error instanceof Error ? error.message : "加载助手失败"); + } } else { // 工作流:暂时显示占位页 setDraftName(assistant.name); @@ -417,6 +426,7 @@ export function AssistantPage() { appId: "", apiUrl: "", apiKey: "", + model: "", asr: "", voice: "", enableInterrupt: true, @@ -437,8 +447,19 @@ export function AssistantPage() { }); setView("create-dify"); } else if (draftType === "OpenCode") { - // OpenCode 类型:进入 OpenCode 构建表单,并把已填的名称带过去 - updateOpenCodeForm("name", draftName.trim()); + // OpenCode 类型:新建,清空表单 + 带入名称 + void loadResources(); + setEditingId(null); + setSaveError(null); + setOpenCodeForm({ + name: draftName.trim(), + prompt: "", + apiUrl: "", + apiKey: "", + asr: "", + voice: "", + enableInterrupt: true, + }); setView("create-opencode"); } else { // 工作流:暂时显示占位页 @@ -529,6 +550,7 @@ export function AssistantPage() { apiUrl: a.apiUrl, // 编辑时不把打码占位符放入输入框;空值写回后端表示保留旧 key apiKey: "", + model: a.llmCredentialId ?? "", asr: a.asrCredentialId ?? "", voice: a.ttsCredentialId ?? "", enableInterrupt: a.enableInterrupt, @@ -578,6 +600,36 @@ export function AssistantPage() { ); } + // ---- OpenCode ---- + function fillOpenCodeForm(a: Assistant) { + setOpenCodeForm({ + name: a.name, + prompt: a.prompt, + apiUrl: a.apiUrl, + // 编辑时不把打码占位符放入输入框;空值写回后端表示保留旧 key + apiKey: "", + asr: a.asrCredentialId ?? "", + voice: a.ttsCredentialId ?? "", + enableInterrupt: a.enableInterrupt, + }); + } + + function handleSaveOpenCode() { + void save( + baseUpsert({ + name: openCodeForm.name.trim(), + type: "opencode", + enableInterrupt: openCodeForm.enableInterrupt, + llmCredentialId: openCodeForm.model || null, + asrCredentialId: openCodeForm.asr || null, + ttsCredentialId: openCodeForm.voice || null, + prompt: openCodeForm.prompt, + apiUrl: openCodeForm.apiUrl, + apiKey: openCodeForm.apiKey, + }), + ); + } + const listItems: AssistantListItem[] = assistants.map((a) => ({ id: a.id, name: a.name, @@ -1300,8 +1352,21 @@ export function AssistantPage() {
-
@@ -1325,7 +1390,7 @@ export function AssistantPage() { value={openCodeForm.apiKey} onChange={(value) => updateOpenCodeForm("apiKey", value)} placeholder="请输入 OpenCode API Key" - storedValueMask="" + storedValueMask={storedApiKeyMask} /> @@ -1344,20 +1409,29 @@ export function AssistantPage() { } - title="语音配置" - description="配置本平台的语音识别与播报音色。" + title="模型与语音配置" + description="配置 OpenCode 使用的大语言模型、语音识别与语音合成资源。" > - updateOpenCodeForm("model", value)} + options={credOptions("LLM")} + noneLabel="无" + /> + updateOpenCodeForm("asr", value)} - options={["SenseVoice", "Paraformer", "Whisper", "FunASR"]} + options={credOptions("ASR")} + noneLabel="无" /> - updateOpenCodeForm("voice", value)} - options={["晓宁", "晓美", "晓宇", "晓晨"]} + options={credOptions("TTS")} + noneLabel="无" /> @@ -1997,40 +2071,6 @@ function TextAreaField({ ); } -function SelectField({ - label, - value, - options, - onChange, -}: { - label?: string; - value: string; - options: string[]; - onChange: (value: string) => void; -}) { - return ( -
- {label && ( -
{label}
- )} - - -
- ); -} - // Radix Select 不允许空字符串 value,用哨兵表示"未选/无" const NONE_VALUE = "__none__";