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
+10 -247
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,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<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: {