feat: remove OpenCode assistant mode

This commit is contained in:
Xin Wang
2026-10-23 09:43:17 +08:00
parent 56b574d987
commit 81a6fdc30c
17 changed files with 78 additions and 313 deletions
+2 -4
View File
@@ -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"
+4 -4
View File
@@ -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)
+1 -8
View File
@@ -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();
-3
View File
@@ -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)
+2 -2
View File
@@ -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 = ""
+10 -1
View File
@@ -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} 副本",
+4 -4
View File
@@ -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"},
}
+1 -1
View File
@@ -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
-6
View File
@@ -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",
+38
View File
@@ -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()
+1 -1
View File
@@ -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(
+1 -2
View File
@@ -31,11 +31,10 @@ icon: plug
## 外部 Agent
创建助手时选择 Dify、FastGPT 或 OpenCode:
创建助手时选择 「智能体平台」,接入 Dify 或 FastGPT:
- Dify:填写服务地址和 API Key。
- FastGPT:额外填写应用 ID。
- OpenCode:填写服务地址、API Key 和可选提示词。
外部 Agent 的上下文、知识库和工具由对方服务处理。
+1 -1
View File
@@ -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 不会自动生成新的运行适配。
+1 -1
View File
@@ -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 可选摄像头与视觉处理 | 有条件可用 | 需要助手授权视觉、客户端协商视频轨道,并受运行模式限制。 |
+2 -239
View File
@@ -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<ApiAssistantType, AssistantType> = {
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<AgentPlatformForm>(() =>
blankAgentPlatformForm(""),
);
const [openCodeForm, setOpenCodeForm] = useState<OpenCodeForm>(() =>
blankOpenCodeForm(""),
);
const [assistants, setAssistants] = useState<Assistant[]>([]);
const [listLoading, setListLoading] = useState(true);
const [listError, setListError] = useState<string | null>(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,8 +729,6 @@ 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,
@@ -926,15 +831,6 @@ export function AssistantPage(props: AssistantPageProps) {
}));
}
function updateOpenCodeForm<K extends keyof OpenCodeForm>(
key: K,
value: OpenCodeForm[K],
) {
setOpenCodeForm((prev) => ({
...prev,
[key]: value,
}));
}
if (view === "loading") {
return (
<div className="flex h-full items-center justify-center">
@@ -1325,139 +1221,6 @@ export function AssistantPage(props: AssistantPageProps) {
);
}
if (view === "create-opencode") {
return (
<div className="-mt-6 flex h-full flex-col gap-4">
<div className="flex shrink-0 items-center justify-between gap-6 border-b border-hairline pb-3 pt-1">
<div className="flex min-w-0 items-center gap-2">
<EditorBackButton onClick={() => router.push("/assistants")} />
<EditableTitle
value={openCodeForm.name}
onChange={(value) => updateOpenCodeForm("name", value)}
/>
<AssistantIdentity assistantId={editingId} />
</div>
<div className="flex shrink-0 gap-2">
{saveError && (
<span className="self-center text-xs text-destructive">
{saveError}
</span>
)}
<Button
className="gap-2"
disabled={
saving ||
!dirty ||
!openCodeForm.name.trim() ||
!openCodeForm.agent
}
onClick={() => void handleSaveOpenCode()}
>
{saving ? (
<Loader2 size={16} className="animate-spin" />
) : (
<Save size={16} />
)}
保存
</Button>
</div>
</div>
<div className="flex min-h-0 flex-1 gap-4">
<div className="scrollbar-subtle min-w-0 flex-1 space-y-3 overflow-y-auto pr-1">
<SectionCard
icon={<Terminal size={15} />}
title="OpenCode 服务配置"
description="从「模型资源」中选择 OpenCode 服务资源。"
>
<ResourceSelectField
label="OpenCode 服务"
value={openCodeForm.agent}
onChange={(value) => updateOpenCodeForm("agent", value)}
options={agentOptions("opencode")}
noneLabel="请选择"
/>
</SectionCard>
<SectionCard
icon={<MessageSquareText size={15} />}
title="提示词"
description="描述助手的角色、能力和回答要求"
>
<TextAreaField
value={openCodeForm.prompt}
onChange={(value) => updateOpenCodeForm("prompt", value)}
placeholder="请输入提示词,描述助手的角色、能力和回答要求"
rows={8}
/>
</SectionCard>
<SectionCard
icon={<Brain size={15} />}
title="模型与语音配置"
description="配置 OpenCode 使用的大语言模型、语音识别与语音合成资源。"
>
<ToggleRow
title="视觉理解"
hint="开启后,开始对话时会允许助手按需理解当前视频画面。视觉模型选「模型自己」时,大语言模型本身必须支持图片输入。"
checked={openCodeForm.visionEnabled}
onChange={handleOpenCodeVisionEnabledChange}
/>
{openCodeForm.visionEnabled && (
<ResourceSelectField
label="视觉模型"
value={openCodeForm.visionModelResourceId}
onChange={(value) =>
updateOpenCodeForm("visionModelResourceId", value)
}
options={visionModelOptionsFor(openCodeForm.model)}
noneLabel="模型自己"
/>
)}
<ResourceSelectField
label="大语言模型"
value={openCodeForm.model}
onChange={handleOpenCodeModelChange}
options={credOptions("LLM")}
noneLabel="无"
/>
<ResourceSelectField
label="语音识别"
value={openCodeForm.asr}
onChange={(value) => updateOpenCodeForm("asr", value)}
options={credOptions("ASR")}
noneLabel="无"
/>
<ResourceSelectField
label="语音合成"
value={openCodeForm.voice}
onChange={(value) => updateOpenCodeForm("voice", value)}
options={credOptions("TTS")}
noneLabel="无"
/>
</SectionCard>
<SectionCard
icon={<Sparkles size={15} />}
title="交互策略"
description="设置实时视频对话时的交互体验"
>
<TurnConfigEditor
enabled={openCodeForm.enableInterrupt}
config={openCodeForm.turnConfig}
onEnabledChange={(checked) => updateOpenCodeForm("enableInterrupt", checked)}
onConfigChange={(config) => updateOpenCodeForm("turnConfig", config)}
/>
</SectionCard>
</div>
<DebugDrawer assistantId={editingId} hasUnsavedChanges={dirty} />
</div>
</div>
);
}
return (
<PromptEditor
assistantId={editingId}
@@ -8,7 +8,6 @@ import {
Loader2,
MessageSquareText,
Rocket,
Terminal,
Workflow,
} from "lucide-react";
@@ -16,7 +15,6 @@ import {
ListPageLayout,
ListPageSection,
} from "@/components/layout/list-page-layout";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import {
@@ -31,7 +29,7 @@ import {
} from "@/lib/api";
import { defaultTurnConfig } from "@/lib/turn-config";
type BuildMethod = "提示词" | "工作流" | "智能体平台" | "OpenCode";
type BuildMethod = "提示词" | "工作流" | "智能体平台";
const typeFromBuildMethod: Record<
Exclude<BuildMethod, "智能体平台">,
@@ -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: <MessageSquareText size={20} />,
available: true,
},
{
type: "工作流",
label: "使用工作流构建",
description: "用可视化编排串联多个节点,适合多步骤、带分支的复杂流程。",
icon: <Workflow size={20} />,
available: true,
},
{
type: "智能体平台",
@@ -72,14 +65,6 @@ const assistantTypeOptions: AssistantTypeOption[] = [
description:
"接入 Dify、FastGPT 等智能体平台,复用平台中的提示词、知识库和工作流配置。",
icon: <Boxes size={20} />,
available: true,
},
{
type: "OpenCode",
label: "使用 OpenCode 构建",
description: "对接 OpenCode 服务,通过提示词驱动代码助手并支持实时语音对话。",
icon: <Terminal size={20} />,
available: false,
},
];
@@ -203,16 +188,6 @@ export function CreateAssistantPage() {
title={option.label}
description={option.description}
onClick={() => setDraftType(option.type)}
unselectedTrailing={
!option.available ? (
<Badge
variant="secondary"
className="h-6 bg-surface-strong px-3 text-xs text-muted-foreground"
>
即将上线
</Badge>
) : undefined
}
/>
))}
</div>
+1 -2
View File
@@ -263,8 +263,7 @@ export type AssistantType =
| "prompt"
| "workflow"
| "dify"
| "fastgpt"
| "opencode";
| "fastgpt";
export type RuntimeMode = "pipeline" | "realtime";
export type TurnConfig = {
bargeIn: {