From 81a6fdc30c87baf9903d699bd86de93a8060017a Mon Sep 17 00:00:00 2001 From: Xin Wang Date: Fri, 23 Oct 2026 09:43:17 +0800 Subject: [PATCH] feat: remove OpenCode assistant mode --- backend/contracts/openapi.json | 6 +- backend/db/models.py | 8 +- backend/db/seed_assistants.sql | 9 +- backend/db/seed_model_resources.sql | 3 - backend/models.py | 4 +- backend/routes/assistants.py | 11 +- backend/schemas.py | 8 +- backend/services/config_resolver.py | 2 +- backend/services/interface_catalog.py | 6 - backend/tests/test_assistant_types.py | 38 +++ backend/tests/test_brains.py | 2 +- docs-developer/integrations.mdx | 3 +- docs-user/features.mdx | 2 +- docs-user/status.mdx | 2 +- .../src/components/pages/AssistantPage.tsx | 257 +----------------- .../components/pages/CreateAssistantPage.tsx | 27 +- frontend/src/lib/api.ts | 3 +- 17 files changed, 78 insertions(+), 313 deletions(-) create mode 100644 backend/tests/test_assistant_types.py diff --git a/backend/contracts/openapi.json b/backend/contracts/openapi.json index 46407e8..7866f8e 100644 --- a/backend/contracts/openapi.json +++ b/backend/contracts/openapi.json @@ -364,8 +364,7 @@ "prompt", "workflow", "dify", - "fastgpt", - "opencode" + "fastgpt" ], "title": "Type", "type": "string" @@ -534,8 +533,7 @@ "prompt", "workflow", "dify", - "fastgpt", - "opencode" + "fastgpt" ], "title": "Type", "type": "string" diff --git a/backend/db/models.py b/backend/db/models.py index 55018d4..3a54378 100644 --- a/backend/db/models.py +++ b/backend/db/models.py @@ -148,7 +148,7 @@ class Assistant(Base): id: Mapped[str] = mapped_column(String(40), primary_key=True) # asst_xxx name: Mapped[str] = mapped_column(String(128)) - # prompt|workflow|dify|fastgpt|opencode;创建后可改 + # prompt|workflow|dify|fastgpt;创建后可改 type: Mapped[str] = mapped_column(String(16), index=True, default="prompt") runtime_mode: Mapped[str] = mapped_column(String(16), default="pipeline") greeting: Mapped[str] = mapped_column(String(2048), default="") @@ -174,12 +174,12 @@ class Assistant(Base): ) # ---- 瘦类型专属字段(真列,稀疏:按 type 用其中几列) ---- - prompt: Mapped[str] = mapped_column(Text, default="") # prompt / opencode + prompt: Mapped[str] = mapped_column(Text, default="") # prompt dynamic_variable_definitions: Mapped[dict] = mapped_column(JSON, default=dict) built_in_tools: Mapped[dict] = mapped_column(JSONB, default=dict) analysis_config: Mapped[dict] = mapped_column(JSONB, default=dict) - api_url: Mapped[str] = mapped_column(String(512), default="") # dify / fastgpt / opencode - api_key: Mapped[str] = mapped_column(String(512), default="") # dify / fastgpt / opencode(打码/哨兵) + api_url: Mapped[str] = mapped_column(String(512), default="") # dify / fastgpt + api_key: Mapped[str] = mapped_column(String(512), default="") # dify / fastgpt(打码/哨兵) app_id: Mapped[str] = mapped_column(String(128), default="") # fastgpt # workflow 专属:图(nodes/edges)。要版本化时再迁出到 assistant_workflow 表 graph: Mapped[dict] = mapped_column(JSON, default=dict) diff --git a/backend/db/seed_assistants.sql b/backend/db/seed_assistants.sql index 4893e67..8bf365e 100644 --- a/backend/db/seed_assistants.sql +++ b/backend/db/seed_assistants.sql @@ -22,10 +22,6 @@ INSERT INTO assistants ( ('asst_004', 'FastGPT 售后助手', 'fastgpt', 'pipeline', '', TRUE, FALSE, NULL, NULL, '', 'https://api.fastgpt.in/api/v1/chat/completions', 'fastgpt-demo-key', 'app-fastgpt-001', '{}', - '{"execution_mode":"sequential","opening_mode":"interruptible","entry_mode":"wait_user","actions":[],"opening_message":null}'), - ('asst_005', 'OpenCode 代码助手', 'opencode', 'pipeline', '', TRUE, - FALSE, NULL, - NULL, '你是一个代码助手的语音界面,用简洁口语回答工程问题。', 'http://localhost:4096', 'opencode-demo-key', '', '{}', '{"execution_mode":"sequential","opening_mode":"interruptible","entry_mode":"wait_user","actions":[],"opening_message":null}') ON CONFLICT (id) DO NOTHING; @@ -40,10 +36,7 @@ VALUES ('asst_003', 'Agent', 'model_014', '{}'), ('asst_004', 'ASR', 'model_002', '{}'), ('asst_004', 'TTS', 'model_004', '{}'), - ('asst_004', 'Agent', 'model_015', '{}'), - ('asst_005', 'ASR', 'model_002', '{}'), - ('asst_005', 'TTS', 'model_004', '{}'), - ('asst_005', 'Agent', 'model_016', '{}') + ('asst_004', 'Agent', 'model_015', '{}') ON CONFLICT (assistant_id, capability) DO UPDATE SET model_resource_id = EXCLUDED.model_resource_id, updated_at = now(); diff --git a/backend/db/seed_model_resources.sql b/backend/db/seed_model_resources.sql index 89d7cd6..243ca42 100644 --- a/backend/db/seed_model_resources.sql +++ b/backend/db/seed_model_resources.sql @@ -48,9 +48,6 @@ VALUES ('model_015', 'FastGPT 售后应用', 'Agent', 'fastgpt', '{"apiUrl":"https://api.fastgpt.in/api/v1/chat/completions","appId":"app-fastgpt-001"}', '{"apiKey":"replace-me"}', FALSE, TRUE, FALSE), - ('model_016', 'OpenCode 服务', 'Agent', 'opencode', - '{"apiUrl":"http://localhost:4096"}', - '{"apiKey":"replace-me"}', FALSE, TRUE, FALSE), ('model_017', 'Qwen-Audio 3.0 Realtime', 'Realtime', 'qwen-audio-realtime', '{"modelId":"qwen-audio-3.0-realtime-flash","apiUrl":"wss://replace-with-workspace-id.cn-beijing.maas.aliyuncs.com/api-ws/v1/realtime","voice":"longanqian","turnDetection":"server_vad","vadThreshold":0.5,"silenceDurationMs":800,"maxHistoryTurns":20}', '{"apiKey":"replace-me"}', FALSE, TRUE, FALSE) diff --git a/backend/models.py b/backend/models.py index 7b8d572..20cc259 100644 --- a/backend/models.py +++ b/backend/models.py @@ -74,7 +74,7 @@ class AssistantConfig(BaseModel): """运行时配置:前端可见部分(name/prompt/...) + 服务端注入部分(*_api_key/*_base_url)。""" name: str = "未命名助手" - # prompt|workflow|dify|fastgpt|opencode;决定由哪种「大脑」驱动对话 + # prompt|workflow|dify|fastgpt;决定由哪种「大脑」驱动对话 type: str = "prompt" greeting: str = "您好,我是 AI 视频助手,请问有什么可以帮您?" prompt: str = "你是一个有帮助的助手。" @@ -154,7 +154,7 @@ class AssistantConfig(BaseModel): workflow_model_resources: dict[str, RuntimeModelResource] = Field(default_factory=dict) workflow_knowledge_bases: dict[str, RuntimeKnowledgeBase] = Field(default_factory=dict) - # 外部托管类型(fastgpt/dify/opencode)的连接信息:context/KB/tools 由对方服务端接管。 + # 外部托管类型(fastgpt/dify)的连接信息:context/KB/tools 由对方服务端接管。 dify_api_url: str = "" dify_api_key: str = "" fastgpt_api_url: str = "" diff --git a/backend/routes/assistants.py b/backend/routes/assistants.py index 6776dd6..0fbf8cb 100644 --- a/backend/routes/assistants.py +++ b/backend/routes/assistants.py @@ -17,6 +17,7 @@ from fastapi import APIRouter, Depends, HTTPException from schemas import ( AssistantOut, AssistantUpsert, + SUPPORTED_ASSISTANT_TYPES, ) from services.auth import require_admin from services.masking import mask, resolve_incoming_key @@ -607,6 +608,8 @@ async def _tool_bindings(session: AsyncSession, assistant_id: str) -> list[dict] async def _to_out(session: AsyncSession, assistant: Assistant) -> AssistantOut: + if assistant.type not in SUPPORTED_ASSISTANT_TYPES: + raise HTTPException(410, "该助手类型已下线") return AssistantOut( id=assistant.id, name=assistant.name, @@ -647,7 +650,11 @@ async def _to_out(session: AsyncSession, assistant: Assistant) -> AssistantOut: @router.get("", response_model=list[AssistantOut]) async def list_assistants(session: AsyncSession = Depends(get_session)): rows = ( - await session.execute(select(Assistant).order_by(Assistant.updated_at.desc())) + await session.execute( + select(Assistant) + .where(Assistant.type.in_(SUPPORTED_ASSISTANT_TYPES)) + .order_by(Assistant.updated_at.desc()) + ) ).scalars().all() return [await _to_out(session, assistant) for assistant in rows] @@ -703,6 +710,8 @@ async def duplicate_assistant( source = await session.get(Assistant, assistant_id) if not source: raise HTTPException(404, "助手不存在") + if source.type not in SUPPORTED_ASSISTANT_TYPES: + raise HTTPException(410, "该助手类型已下线") assistant = Assistant( id=f"asst_{uuid.uuid4().hex[:12]}", name=f"{source.name} 副本", diff --git a/backend/schemas.py b/backend/schemas.py index 982c906..30b745a 100644 --- a/backend/schemas.py +++ b/backend/schemas.py @@ -9,7 +9,7 @@ from __future__ import annotations from datetime import UTC, datetime import re -from typing import Annotated, Any, Literal, Union +from typing import Annotated, Any, Literal, Union, get_args from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator from pydantic.alias_generators import to_camel @@ -17,7 +17,8 @@ from pydantic.alias_generators import to_camel RuntimeMode = Literal["pipeline", "realtime"] VisionPromptMode = Literal["fixed", "runtime", "guided"] ModelType = Literal["LLM", "ASR", "TTS", "Realtime", "Embedding", "Agent"] -AssistantType = Literal["prompt", "workflow", "dify", "fastgpt", "opencode"] +AssistantType = Literal["prompt", "workflow", "dify", "fastgpt"] +SUPPORTED_ASSISTANT_TYPES = get_args(AssistantType) TurnEndStrategy = Literal["silence", "smart_turn"] KnowledgeRetrievalMode = Literal["automatic", "on_demand"] ToolType = Literal["http", "mcp", "client"] @@ -34,7 +35,7 @@ PromptOpeningMode = Literal["interruptible", "playback", "confirmation"] PrimitiveValue = str | int | float | bool # 外部应用类型:其 config.apiKey 是该助手私有密钥,读时打码 / 写时哨兵 -EXTERNAL_TYPES = {"dify", "fastgpt", "opencode"} +EXTERNAL_TYPES = {"dify", "fastgpt"} # Prompt 和 Workflow 支持 realtime;外部托管大脑只走 pipeline。 # 与 services.brains 各 BrainSpec.supported_runtime_modes 对齐(此处独立声明, @@ -131,7 +132,6 @@ ALLOWED_FIELDS: dict[str, set[str]] = { "workflow": {"graph"}, "dify": {"api_url", "api_key"}, "fastgpt": {"app_id", "api_url", "api_key"}, - "opencode": {"prompt", "api_url", "api_key"}, } diff --git a/backend/services/config_resolver.py b/backend/services/config_resolver.py index c342629..7130255 100644 --- a/backend/services/config_resolver.py +++ b/backend/services/config_resolver.py @@ -53,7 +53,7 @@ async def _agent_resource_for( assistant_id: str, interface_type: str, ) -> ModelResource | None: - if interface_type not in {"dify", "fastgpt", "opencode"}: + if interface_type not in {"dify", "fastgpt"}: return None binding = await session.get(AssistantModelBinding, (assistant_id, "Agent")) resource_id = binding.model_resource_id if binding else None diff --git a/backend/services/interface_catalog.py b/backend/services/interface_catalog.py index a83305c..8bb0a34 100644 --- a/backend/services/interface_catalog.py +++ b/backend/services/interface_catalog.py @@ -95,12 +95,6 @@ INTERFACE_DEFINITIONS: list[dict] = [ "capability": "Agent", "fields": AGENT_COMMON + [field("appId", "App ID", required=True)], }, - { - "interface_type": "opencode", - "name": "OpenCode Agent", - "capability": "Agent", - "fields": AGENT_COMMON, - }, { "interface_type": "stepfun-realtime", "name": "StepFun StepAudio Realtime", diff --git a/backend/tests/test_assistant_types.py b/backend/tests/test_assistant_types.py new file mode 100644 index 0000000..e463ec6 --- /dev/null +++ b/backend/tests/test_assistant_types.py @@ -0,0 +1,38 @@ +"""Removed assistant types must fail cleanly without changing stored data.""" + +import unittest +from types import SimpleNamespace +from unittest.mock import AsyncMock + +from fastapi import HTTPException +from pydantic import ValidationError + +from routes.assistants import duplicate_assistant, get_assistant +from schemas import AssistantUpsert + + +class AssistantTypeTests(unittest.TestCase): + def test_removed_type_is_rejected(self): + with self.assertRaises(ValidationError): + AssistantUpsert(name="旧助手", type="opencode") + + def test_remaining_types_can_be_created(self): + for assistant_type in ("prompt", "workflow", "dify", "fastgpt"): + with self.subTest(type=assistant_type): + assistant = AssistantUpsert(name="助手", type=assistant_type) + self.assertEqual(assistant.type, assistant_type) + + +class RetiredAssistantTests(unittest.IsolatedAsyncioTestCase): + async def test_old_record_returns_gone_without_writes(self): + for operation in (get_assistant, duplicate_assistant): + with self.subTest(operation=operation.__name__): + session = AsyncMock() + session.get.return_value = SimpleNamespace(type="opencode") + with self.assertRaises(HTTPException) as error: + await operation("legacy_assistant", session) + self.assertEqual(error.exception.status_code, 410) + session.execute.assert_not_called() + session.add.assert_not_called() + session.commit.assert_not_called() + session.delete.assert_not_called() diff --git a/backend/tests/test_brains.py b/backend/tests/test_brains.py index 008d1f2..92fae13 100644 --- a/backend/tests/test_brains.py +++ b/backend/tests/test_brains.py @@ -185,7 +185,7 @@ class BrainRegistryTests(unittest.TestCase): def test_unknown_brain_does_not_fallback_to_prompt(self): with self.assertRaisesRegex(ValueError, "尚未实现"): - build_brain(AssistantConfig(type="opencode")) + build_brain(AssistantConfig(type="unknown")) def test_workflow_realtime_is_accepted_at_schema_boundary(self): assistant = AssistantUpsert( diff --git a/docs-developer/integrations.mdx b/docs-developer/integrations.mdx index 1a87642..cfcba58 100644 --- a/docs-developer/integrations.mdx +++ b/docs-developer/integrations.mdx @@ -31,11 +31,10 @@ icon: plug ## 外部 Agent -创建助手时选择 Dify、FastGPT 或 OpenCode: +创建助手时选择 「智能体平台」,接入 Dify 或 FastGPT: - Dify:填写服务地址和 API Key。 - FastGPT:额外填写应用 ID。 -- OpenCode:填写服务地址、API Key 和可选提示词。 外部 Agent 的上下文、知识库和工具由对方服务处理。 diff --git a/docs-user/features.mdx b/docs-user/features.mdx index 97725be..a109905 100644 --- a/docs-user/features.mdx +++ b/docs-user/features.mdx @@ -14,7 +14,7 @@ icon: sliders-horizontal | --- | --- | --- | --- | | Prompt | 已实现 | 已实现,有 Provider 限制 | 提示词驱动,可绑定模型及受支持的工具。 | | Workflow | 已实现 | 已实现,有 Provider 限制 | 用节点编排对话、状态和动作。 | -| Dify / FastGPT / OpenCode | 已实现 | 未实现 | 对话处理交给外部 Agent,语音输入输出仍由平台管线处理。 | +| Dify / FastGPT | 已实现 | 未实现 | 对话处理交给外部 Agent,语音输入输出仍由平台管线处理。 | 创建助手时,**Pipeline** 需要分别绑定 ASR、LLM、TTS;外部 Agent 助手将 LLM 环节交给外部服务。**Realtime** 绑定一个端到端语音模型资源。模型资源按 `interface_type` 选择适配,填写一个看似兼容的 URL 不会自动生成新的运行适配。 diff --git a/docs-user/status.mdx b/docs-user/status.mdx index 102ca20..3ff7a85 100644 --- a/docs-user/status.mdx +++ b/docs-user/status.mdx @@ -15,7 +15,7 @@ icon: list-checks | Pipecat Pipeline:ASR → LLM/外部 Agent → TTS | 已实现 | 需要匹配的模型接口、有效凭证和实际联调。 | | Pipecat Pipeline 的 Silero VAD、静音结束、Local Smart Turn、打断 | 已实现 | 可调整参数与策略;模型实现目前固定。 | | StepFun StepAudio、Qwen Audio Realtime | 已实现 | Provider 的轮次、工具与视觉能力各不相同。 | -| Prompt、Workflow、Dify/FastGPT/OpenCode 助手 | 已实现 | 外部 Agent 只支持 Pipeline;公开 `/v1/models` 当前不列出 OpenCode。 | +| Prompt、Workflow、Dify/FastGPT 助手 | 已实现 | 外部 Agent 只支持 Pipeline。 | | OpenAI Realtime 兼容子集的 WebSocket/WebRTC | 已实现 | 是对外协议适配,不代表所有 OpenAI 字段或底层 OpenAI 模型可用。 | | Responses 文本接口 | 已实现 | 面向可通过该接口调用的 Pipeline 助手。 | | WebRTC 可选摄像头与视觉处理 | 有条件可用 | 需要助手授权视觉、客户端协商视频轨道,并受运行模式限制。 | diff --git a/frontend/src/components/pages/AssistantPage.tsx b/frontend/src/components/pages/AssistantPage.tsx index ca7795a..186f541 100644 --- a/frontend/src/components/pages/AssistantPage.tsx +++ b/frontend/src/components/pages/AssistantPage.tsx @@ -4,7 +4,6 @@ import { Boxes, Brain, Copy, - MessageSquareText, MoreHorizontal, Pencil, Plus, @@ -14,7 +13,6 @@ import { ChevronDown, LayoutTemplate, Save, - Terminal, Loader2, } from "lucide-react"; @@ -76,8 +74,6 @@ import { EditableTitle, EditorBackButton, ResourceSelectField, - TextAreaField, - ToggleRow, } from "@/components/assistant-editor/editor-controls"; import type { AssistantForm } from "@/components/assistant-editor/types"; import { PromptEditor } from "@/components/assistant-editor/prompt-editor"; @@ -95,27 +91,13 @@ type AgentPlatformForm = { turnConfig: TurnConfig; }; -type OpenCodeForm = { - name: string; - prompt: string; - agent: string; - model: string; - asr: string; - voice: string; - enableInterrupt: boolean; - turnConfig: TurnConfig; - visionEnabled: boolean; - visionModelResourceId: string; -}; - -type AssistantType = "提示词" | "工作流" | "Dify" | "FastGPT" | "OpenCode"; +type AssistantType = "提示词" | "工作流" | "Dify" | "FastGPT"; const assistantTypes: AssistantType[] = [ "提示词", "工作流", "Dify", "FastGPT", - "OpenCode", ]; function defaultKnowledgeRetrievalConfig(): KnowledgeRetrievalConfig { @@ -132,7 +114,6 @@ const typeToLabel: Record = { workflow: "工作流", dify: "Dify", fastgpt: "FastGPT", - opencode: "OpenCode", }; // 后端 type → 编辑器视图(工作流暂为占位页) @@ -140,7 +121,6 @@ const typeToView = { prompt: "create", dify: "create-agent-platform", fastgpt: "create-agent-platform", - opencode: "create-opencode", workflow: "workflow", } as const; @@ -201,20 +181,6 @@ function blankAgentPlatformForm(name: string): AgentPlatformForm { }; } -function blankOpenCodeForm(name: string): OpenCodeForm { - return { - name, - prompt: "", - agent: "", - model: "", - asr: "", - voice: "", - enableInterrupt: true, - turnConfig: defaultTurnConfig(), - visionEnabled: false, - visionModelResourceId: "", - }; -} function formatTimestamp(iso?: string | null): string { if (!iso) return ""; const d = new Date(iso); @@ -251,9 +217,6 @@ export function AssistantPage(props: AssistantPageProps) { useState(() => blankAgentPlatformForm(""), ); - const [openCodeForm, setOpenCodeForm] = useState(() => - blankOpenCodeForm(""), - ); const [assistants, setAssistants] = useState([]); const [listLoading, setListLoading] = useState(true); const [listError, setListError] = useState(null); @@ -366,7 +329,7 @@ export function AssistantPage(props: AssistantPageProps) { label: c.name, supportImageInput: c.supportImageInput, })); - const agentOptions = (interfaceType: "dify" | "fastgpt" | "opencode") => + const agentOptions = (interfaceType: AgentPlatform) => modelResources .filter( (resource) => @@ -405,18 +368,6 @@ export function AssistantPage(props: AssistantPageProps) { } } - function handleOpenCodeVisionEnabledChange(enabled: boolean) { - updateOpenCodeForm("visionEnabled", enabled); - if (!enabled) updateOpenCodeForm("visionModelResourceId", ""); - } - - function handleOpenCodeModelChange(value: string) { - updateOpenCodeForm("model", value); - if (openCodeForm.visionModelResourceId === value) { - updateOpenCodeForm("visionModelResourceId", ""); - } - } - function startCreate() { router.push("/assistants/new"); } @@ -541,10 +492,6 @@ export function AssistantPage(props: AssistantPageProps) { const next = { ...agentPlatformForm }; setAgentPlatformForm(next); setSavedSnapshot(JSON.stringify(next)); - } else if (view === "create-opencode") { - const next = { ...openCodeForm }; - setOpenCodeForm(next); - setSavedSnapshot(JSON.stringify(next)); } else if (view === "workflow") { setSavedSnapshot( JSON.stringify({ @@ -640,24 +587,6 @@ export function AssistantPage(props: AssistantPageProps) { ); } - // ---- OpenCode ---- - function fillOpenCodeForm(a: Assistant): OpenCodeForm { - const next: OpenCodeForm = { - name: a.name, - prompt: a.prompt, - agent: a.modelResourceIds.Agent ?? "", - model: a.modelResourceIds.LLM ?? "", - asr: a.modelResourceIds.ASR ?? "", - voice: a.modelResourceIds.TTS ?? "", - enableInterrupt: a.enableInterrupt, - turnConfig: normalizeTurnConfig(a.turnConfig), - visionEnabled: a.visionEnabled, - visionModelResourceId: a.visionModelResourceId ?? "", - }; - setOpenCodeForm(next); - return next; - } - // 编辑模式:按路由中的 id 拉取助手,回填对应类型的表单后再切换视图 useEffect(() => { if (props.mode !== "edit" || !editingId) return; @@ -674,8 +603,6 @@ export function AssistantPage(props: AssistantPageProps) { assistant.type === "dify" ) { setSavedSnapshot(JSON.stringify(fillAgentPlatformForm(assistant))); - } else if (assistant.type === "opencode") { - setSavedSnapshot(JSON.stringify(fillOpenCodeForm(assistant))); } else { // 工作流:回填名称与图(空图回落到默认 开始→智能体→结束) const graph = @@ -770,26 +697,6 @@ export function AssistantPage(props: AssistantPageProps) { setWorkflowSettings, ]); - function handleSaveOpenCode() { - void save( - baseUpsert({ - name: openCodeForm.name.trim(), - type: "opencode", - enableInterrupt: openCodeForm.enableInterrupt, - turnConfig: openCodeForm.turnConfig, - visionEnabled: openCodeForm.visionEnabled, - visionModelResourceId: openCodeForm.visionModelResourceId || null, - modelResourceIds: { - ...(openCodeForm.agent ? { Agent: openCodeForm.agent } : {}), - ...(openCodeForm.model ? { LLM: openCodeForm.model } : {}), - ...(openCodeForm.asr ? { ASR: openCodeForm.asr } : {}), - ...(openCodeForm.voice ? { TTS: openCodeForm.voice } : {}), - }, - prompt: openCodeForm.prompt, - }), - ); - } - function handleSaveWorkflow() { void save( baseUpsert({ @@ -822,16 +729,14 @@ export function AssistantPage(props: AssistantPageProps) { ? JSON.stringify(form) : view === "create-agent-platform" ? JSON.stringify(agentPlatformForm) - : view === "create-opencode" - ? JSON.stringify(openCodeForm) - : view === "workflow" - ? JSON.stringify({ - name: workflowName, - graph: workflowGraph, - dynamicVariableDefinitions: - effectiveWorkflowDynamicVariableDefinitions, - }) - : null; + : view === "workflow" + ? JSON.stringify({ + name: workflowName, + graph: workflowGraph, + dynamicVariableDefinitions: + effectiveWorkflowDynamicVariableDefinitions, + }) + : null; const dirty = activeFormJson !== null && savedSnapshot !== null && @@ -926,15 +831,6 @@ export function AssistantPage(props: AssistantPageProps) { })); } - function updateOpenCodeForm( - key: K, - value: OpenCodeForm[K], - ) { - setOpenCodeForm((prev) => ({ - ...prev, - [key]: value, - })); - } if (view === "loading") { return (
@@ -1325,139 +1221,6 @@ export function AssistantPage(props: AssistantPageProps) { ); } - if (view === "create-opencode") { - return ( -
-
-
- router.push("/assistants")} /> - updateOpenCodeForm("name", value)} - /> - -
- -
- {saveError && ( - - {saveError} - - )} - -
-
- -
-
- } - title="OpenCode 服务配置" - description="从「模型资源」中选择 OpenCode 服务资源。" - > - updateOpenCodeForm("agent", value)} - options={agentOptions("opencode")} - noneLabel="请选择" - /> - - - } - title="提示词" - description="描述助手的角色、能力和回答要求" - > - updateOpenCodeForm("prompt", value)} - placeholder="请输入提示词,描述助手的角色、能力和回答要求" - rows={8} - /> - - - } - title="模型与语音配置" - description="配置 OpenCode 使用的大语言模型、语音识别与语音合成资源。" - > - - {openCodeForm.visionEnabled && ( - - updateOpenCodeForm("visionModelResourceId", value) - } - options={visionModelOptionsFor(openCodeForm.model)} - noneLabel="模型自己" - /> - )} - - updateOpenCodeForm("asr", value)} - options={credOptions("ASR")} - noneLabel="无" - /> - updateOpenCodeForm("voice", value)} - options={credOptions("TTS")} - noneLabel="无" - /> - - - } - title="交互策略" - description="设置实时视频对话时的交互体验" - > - updateOpenCodeForm("enableInterrupt", checked)} - onConfigChange={(config) => updateOpenCodeForm("turnConfig", config)} - /> - -
- - -
-
- ); - } - return ( , @@ -39,7 +37,6 @@ const typeFromBuildMethod: Record< > = { 提示词: "prompt", 工作流: "workflow", - OpenCode: "opencode", }; type AssistantTypeOption = { @@ -47,8 +44,6 @@ type AssistantTypeOption = { label: string; description: string; icon: React.ReactNode; - /** 提示词、工作流、智能体平台已落地;OpenCode 暂时显示即将上线 */ - available: boolean; }; const assistantTypeOptions: AssistantTypeOption[] = [ @@ -57,14 +52,12 @@ const assistantTypeOptions: AssistantTypeOption[] = [ label: "使用提示词构建", description: "通过提示词、模型与语音快速搭建对话助手,适合大多数场景。", icon: , - available: true, }, { type: "工作流", label: "使用工作流构建", description: "用可视化编排串联多个节点,适合多步骤、带分支的复杂流程。", icon: , - available: true, }, { type: "智能体平台", @@ -72,14 +65,6 @@ const assistantTypeOptions: AssistantTypeOption[] = [ description: "接入 Dify、FastGPT 等智能体平台,复用平台中的提示词、知识库和工作流配置。", icon: , - available: true, - }, - { - type: "OpenCode", - label: "使用 OpenCode 构建", - description: "对接 OpenCode 服务,通过提示词驱动代码助手并支持实时语音对话。", - icon: , - available: false, }, ]; @@ -203,16 +188,6 @@ export function CreateAssistantPage() { title={option.label} description={option.description} onClick={() => setDraftType(option.type)} - unselectedTrailing={ - !option.available ? ( - - 即将上线 - - ) : undefined - } /> ))}
diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index 02eafa6..483f914 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -263,8 +263,7 @@ export type AssistantType = | "prompt" | "workflow" | "dify" - | "fastgpt" - | "opencode"; + | "fastgpt"; export type RuntimeMode = "pipeline" | "realtime"; export type TurnConfig = { bargeIn: {