Refactor AssistantPage component to support agent platform integration

- Remove unused FastGpt and Dify form types, consolidating them into a single AgentPlatformForm.
- Update type definitions and state management to accommodate the new agent platform structure.
- Modify functions to handle agent platform forms and ensure proper integration with existing workflows.
- Enhance assistant type options and update related logic for improved clarity and maintainability.
This commit is contained in:
Xin Wang
2026-07-17 20:03:01 +08:00
parent e78dc4088a
commit 34c0d12d2a

View File

@@ -5,7 +5,6 @@ import {
Brain,
Check,
Copy,
Database,
MessageSquareText,
MoreHorizontal,
Pencil,
@@ -81,17 +80,11 @@ import type { AssistantForm } from "@/components/assistant-editor/types";
import { PromptEditor } from "@/components/assistant-editor/prompt-editor";
import { WorkflowPage } from "@/components/assistant-editor/workflow-page";
type FastGptForm = {
name: string;
agent: string;
asr: string;
voice: string;
enableInterrupt: boolean;
turnConfig: TurnConfig;
};
type AgentPlatform = "dify" | "fastgpt";
type DifyForm = {
type AgentPlatformForm = {
name: string;
platform: AgentPlatform;
agent: string;
asr: string;
voice: string;
@@ -113,6 +106,7 @@ type OpenCodeForm = {
};
type AssistantType = "提示词" | "工作流" | "Dify" | "FastGPT" | "OpenCode";
type BuildMethod = "提示词" | "工作流" | "智能体平台" | "OpenCode";
const assistantTypes: AssistantType[] = [
"提示词",
@@ -138,19 +132,20 @@ const typeToLabel: Record<ApiAssistantType, AssistantType> = {
fastgpt: "FastGPT",
opencode: "OpenCode",
};
const typeFromLabel: Record<AssistantType, ApiAssistantType> = {
const typeFromBuildMethod: Record<
Exclude<BuildMethod, "智能体平台">,
ApiAssistantType
> = {
: "prompt",
: "workflow",
Dify: "dify",
FastGPT: "fastgpt",
OpenCode: "opencode",
};
// 后端 type → 编辑器视图(工作流暂为占位页)
const typeToView = {
prompt: "create",
dify: "create-dify",
fastgpt: "create-fastgpt",
dify: "create-agent-platform",
fastgpt: "create-agent-platform",
opencode: "create-opencode",
workflow: "workflow",
} as const;
@@ -186,20 +181,10 @@ function blankPromptForm(name: string): AssistantForm {
};
}
function blankFastGptForm(name: string): FastGptForm {
return {
name,
agent: "",
asr: "",
voice: "",
enableInterrupt: true,
turnConfig: defaultTurnConfig(),
};
}
function blankDifyForm(name: string): DifyForm {
function blankAgentPlatformForm(name: string): AgentPlatformForm {
return {
name,
platform: "dify",
agent: "",
asr: "",
voice: "",
@@ -233,11 +218,11 @@ function formatTimestamp(iso?: string | null): string {
}
type AssistantTypeOption = {
type: AssistantType;
type: BuildMethod;
label: string;
description: string;
icon: React.ReactNode;
/** 提示词、工作流、Dify、FastGPT 已落地OpenCode 暂时显示即将上线 */
/** 提示词、工作流、智能体平台已落地OpenCode 暂时显示即将上线 */
available: boolean;
};
@@ -257,19 +242,13 @@ const assistantTypeOptions: AssistantTypeOption[] = [
available: true,
},
{
type: "Dify",
label: "使用 Dify 构建",
description: "对接 Dify 应用,复用其编排能力与知识库配置。",
type: "智能体平台",
label: "从智能体平台创建",
description:
"接入 Dify、FastGPT 等智能体平台,复用平台中的提示词、知识库和工作流配置。",
icon: <Boxes size={20} />,
available: true,
},
{
type: "FastGPT",
label: "使用 FastGPT 构建",
description: "对接 FastGPT 应用,复用其知识库问答与工作流能力。",
icon: <Database size={20} />,
available: true,
},
{
type: "OpenCode",
label: "使用 OpenCode 构建",
@@ -301,10 +280,10 @@ export function AssistantPage(props: AssistantPageProps) {
const editingId = props.mode === "edit" ? props.assistantId : null;
const [form, setForm] = useState<AssistantForm>(() => blankPromptForm(""));
const [fastGptForm, setFastGptForm] = useState<FastGptForm>(() =>
blankFastGptForm(""),
const [agentPlatformForm, setAgentPlatformForm] =
useState<AgentPlatformForm>(() =>
blankAgentPlatformForm(""),
);
const [difyForm, setDifyForm] = useState<DifyForm>(() => blankDifyForm(""));
const [openCodeForm, setOpenCodeForm] = useState<OpenCodeForm>(() =>
blankOpenCodeForm(""),
);
@@ -333,7 +312,9 @@ export function AssistantPage(props: AssistantPageProps) {
// choose 步骤的草稿:名称与已选类型,确认后直接建库并进入编辑页
// (工作流占位页也用它展示名称与类型)
const [draftName, setDraftName] = useState("");
const [draftType, setDraftType] = useState<AssistantType | null>(null);
const [draftType, setDraftType] = useState<BuildMethod | null>(null);
const [draftAgentPlatform, setDraftAgentPlatform] =
useState<AgentPlatform | null>(null);
// 引导页:创建请求进行中 / 创建失败
const [creating, setCreating] = useState(false);
const [createError, setCreateError] = useState<string | null>(null);
@@ -499,17 +480,29 @@ export function AssistantPage(props: AssistantPageProps) {
// 引导页确认:直接创建到数据库拿到 id再进入该助手的编辑页
async function confirmType() {
if (!draftName.trim() || !draftType || creating) {
if (
!draftName.trim() ||
!draftType ||
(draftType === "智能体平台" && !draftAgentPlatform) ||
creating
) {
return;
}
setCreating(true);
setCreateError(null);
try {
let assistantType: ApiAssistantType;
if (draftType === "智能体平台") {
if (!draftAgentPlatform) return;
assistantType = draftAgentPlatform;
} else {
assistantType = typeFromBuildMethod[draftType];
}
const created = await assistantsApi.create(
baseUpsert({
name: draftName.trim(),
type: typeFromLabel[draftType],
type: assistantType,
}),
);
router.push(`/assistants/${created.id}`);
@@ -573,13 +566,9 @@ export function AssistantPage(props: AssistantPageProps) {
await assistantsApi.update(editingId, payload);
if (view === "create") {
setSavedSnapshot(JSON.stringify(form));
} else if (view === "create-dify") {
const next = { ...difyForm };
setDifyForm(next);
setSavedSnapshot(JSON.stringify(next));
} else if (view === "create-fastgpt") {
const next = { ...fastGptForm };
setFastGptForm(next);
} else if (view === "create-agent-platform") {
const next = { ...agentPlatformForm };
setAgentPlatformForm(next);
setSavedSnapshot(JSON.stringify(next));
} else if (view === "create-opencode") {
const next = { ...openCodeForm };
@@ -628,61 +617,36 @@ export function AssistantPage(props: AssistantPageProps) {
);
}
// ---- Dify ----
function fillDifyForm(a: Assistant): DifyForm {
const next: DifyForm = {
// ---- 外部智能体平台Dify / FastGPT----
function fillAgentPlatformForm(a: Assistant): AgentPlatformForm {
const next: AgentPlatformForm = {
name: a.name,
platform: a.type === "fastgpt" ? "fastgpt" : "dify",
agent: a.modelResourceIds.Agent ?? "",
asr: a.modelResourceIds.ASR ?? "",
voice: a.modelResourceIds.TTS ?? "",
enableInterrupt: a.enableInterrupt,
turnConfig: normalizeTurnConfig(a.turnConfig),
};
setDifyForm(next);
setAgentPlatformForm(next);
return next;
}
function handleSaveDify() {
function handleSaveAgentPlatform() {
void save(
baseUpsert({
name: difyForm.name.trim(),
type: "dify",
enableInterrupt: difyForm.enableInterrupt,
turnConfig: difyForm.turnConfig,
name: agentPlatformForm.name.trim(),
type: agentPlatformForm.platform,
enableInterrupt: agentPlatformForm.enableInterrupt,
turnConfig: agentPlatformForm.turnConfig,
modelResourceIds: {
...(difyForm.agent ? { Agent: difyForm.agent } : {}),
...(difyForm.asr ? { ASR: difyForm.asr } : {}),
...(difyForm.voice ? { TTS: difyForm.voice } : {}),
},
}),
);
}
// ---- FastGPT ----
function fillFastGptForm(a: Assistant): FastGptForm {
const next: FastGptForm = {
name: a.name,
agent: a.modelResourceIds.Agent ?? "",
asr: a.modelResourceIds.ASR ?? "",
voice: a.modelResourceIds.TTS ?? "",
enableInterrupt: a.enableInterrupt,
turnConfig: normalizeTurnConfig(a.turnConfig),
};
setFastGptForm(next);
return next;
}
function handleSaveFastGpt() {
void save(
baseUpsert({
name: fastGptForm.name.trim(),
type: "fastgpt",
enableInterrupt: fastGptForm.enableInterrupt,
turnConfig: fastGptForm.turnConfig,
modelResourceIds: {
...(fastGptForm.agent ? { Agent: fastGptForm.agent } : {}),
...(fastGptForm.asr ? { ASR: fastGptForm.asr } : {}),
...(fastGptForm.voice ? { TTS: fastGptForm.voice } : {}),
...(agentPlatformForm.agent
? { Agent: agentPlatformForm.agent }
: {}),
...(agentPlatformForm.asr ? { ASR: agentPlatformForm.asr } : {}),
...(agentPlatformForm.voice
? { TTS: agentPlatformForm.voice }
: {}),
},
}),
);
@@ -717,10 +681,11 @@ export function AssistantPage(props: AssistantPageProps) {
if (cancelled) return;
if (assistant.type === "prompt") {
setSavedSnapshot(JSON.stringify(fillPromptForm(assistant)));
} else if (assistant.type === "fastgpt") {
setSavedSnapshot(JSON.stringify(fillFastGptForm(assistant)));
} else if (assistant.type === "dify") {
setSavedSnapshot(JSON.stringify(fillDifyForm(assistant)));
} else if (
assistant.type === "fastgpt" ||
assistant.type === "dify"
) {
setSavedSnapshot(JSON.stringify(fillAgentPlatformForm(assistant)));
} else if (assistant.type === "opencode") {
setSavedSnapshot(JSON.stringify(fillOpenCodeForm(assistant)));
} else {
@@ -845,21 +810,19 @@ export function AssistantPage(props: AssistantPageProps) {
const activeFormJson =
view === "create"
? JSON.stringify(form)
: view === "create-dify"
? JSON.stringify(difyForm)
: view === "create-fastgpt"
? JSON.stringify(fastGptForm)
: view === "create-opencode"
? JSON.stringify(openCodeForm)
: view === "workflow"
? JSON.stringify({
name: workflowName,
graph: workflowGraph,
settings: workflowSettings,
dynamicVariableDefinitions:
effectiveWorkflowDynamicVariableDefinitions,
})
: null;
: view === "create-agent-platform"
? JSON.stringify(agentPlatformForm)
: view === "create-opencode"
? JSON.stringify(openCodeForm)
: view === "workflow"
? JSON.stringify({
name: workflowName,
graph: workflowGraph,
settings: workflowSettings,
dynamicVariableDefinitions:
effectiveWorkflowDynamicVariableDefinitions,
})
: null;
const dirty =
activeFormJson !== null &&
savedSnapshot !== null &&
@@ -936,23 +899,21 @@ export function AssistantPage(props: AssistantPageProps) {
}));
}
function updateFastGptForm<K extends keyof FastGptForm>(
function updateAgentPlatformForm<K extends keyof AgentPlatformForm>(
key: K,
value: FastGptForm[K],
value: AgentPlatformForm[K],
) {
setFastGptForm((prev) => ({
setAgentPlatformForm((prev) => ({
...prev,
[key]: value,
}));
}
function updateDifyForm<K extends keyof DifyForm>(
key: K,
value: DifyForm[K],
) {
setDifyForm((prev) => ({
function handleAgentPlatformChange(platform: AgentPlatform) {
setAgentPlatformForm((prev) => ({
...prev,
[key]: value,
platform,
agent: prev.platform === platform ? prev.agent : "",
}));
}
@@ -1210,7 +1171,7 @@ export function AssistantPage(props: AssistantPageProps) {
<section className="flex flex-col gap-4">
<div className="text-sm font-medium text-foreground"></div>
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
{assistantTypeOptions.map((option) => {
const selected = draftType === option.type;
@@ -1258,6 +1219,40 @@ export function AssistantPage(props: AssistantPageProps) {
);
})}
</div>
{draftType === "智能体平台" && (
<div className="rounded-2xl border border-hairline bg-card p-5">
<div className="text-sm font-medium text-foreground">
</div>
<p className="mt-1.5 text-sm leading-6 text-muted-foreground">
</p>
<div className="mt-4 flex flex-wrap gap-3">
{(["dify", "fastgpt"] as const).map((platform) => {
const selected = draftAgentPlatform === platform;
const label = platform === "dify" ? "Dify" : "FastGPT";
return (
<Button
key={platform}
type="button"
variant={selected ? "default" : "outline"}
className={
selected
? "gap-2"
: "gap-2 border-hairline-strong text-muted-foreground hover:text-foreground"
}
onClick={() => setDraftAgentPlatform(platform)}
>
{selected && <Check size={14} />}
{label}
</Button>
);
})}
</div>
</div>
)}
</section>
<div className="flex items-center justify-end gap-3">
@@ -1276,7 +1271,12 @@ export function AssistantPage(props: AssistantPageProps) {
<Button
size="lg"
className="gap-2"
disabled={!draftName.trim() || !draftType || creating}
disabled={
!draftName.trim() ||
!draftType ||
(draftType === "智能体平台" && !draftAgentPlatform) ||
creating
}
onClick={() => void confirmType()}
>
{creating ? (
@@ -1336,108 +1336,18 @@ export function AssistantPage(props: AssistantPageProps) {
/>
);
}
if (view === "create-dify") {
if (view === "create-agent-platform") {
const platformLabel =
agentPlatformForm.platform === "dify" ? "Dify" : "FastGPT";
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={difyForm.name}
onChange={(value) => updateDifyForm("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 || !difyForm.name.trim() || !difyForm.agent
}
onClick={() => void handleSaveDify()}
>
{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={<Boxes size={15} />}
title="Dify 应用配置"
description="从「模型资源」中选择 Dify 应用。开场白、知识库、提示词等对话编排请在 Dify 平台配置,本页不重复设置。"
>
<ResourceSelectField
label="Dify 应用"
value={difyForm.agent}
onChange={(value) => updateDifyForm("agent", value)}
options={agentOptions("dify")}
noneLabel="请选择"
/>
</SectionCard>
<SectionCard
icon={<Brain size={15} />}
title="语音配置"
description="从「模型资源」中选择语音识别与语音合成。大模型、知识库与开场白由 Dify 应用提供,请前往 Dify 平台配置。"
>
<ResourceSelectField
label="语音识别"
value={difyForm.asr}
onChange={(value) => updateDifyForm("asr", value)}
options={credOptions("ASR")}
noneLabel="无"
/>
<ResourceSelectField
label="语音合成"
value={difyForm.voice}
onChange={(value) => updateDifyForm("voice", value)}
options={credOptions("TTS")}
noneLabel="无"
/>
</SectionCard>
<SectionCard
icon={<Sparkles size={15} />}
title="交互策略"
description="设置实时视频对话时的交互体验"
>
<TurnConfigEditor
enabled={difyForm.enableInterrupt}
config={difyForm.turnConfig}
onEnabledChange={(checked) => updateDifyForm("enableInterrupt", checked)}
onConfigChange={(config) => updateDifyForm("turnConfig", config)}
/>
</SectionCard>
</div>
<DebugDrawer assistantId={editingId} hasUnsavedChanges={dirty} />
</div>
</div>
);
}
if (view === "create-fastgpt") {
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={fastGptForm.name}
onChange={(value) => updateFastGptForm("name", value)}
value={agentPlatformForm.name}
onChange={(value) => updateAgentPlatformForm("name", value)}
/>
<AssistantIdentity assistantId={editingId} />
</div>
@@ -1453,10 +1363,10 @@ export function AssistantPage(props: AssistantPageProps) {
disabled={
saving ||
!dirty ||
!fastGptForm.name.trim() ||
!fastGptForm.agent
!agentPlatformForm.name.trim() ||
!agentPlatformForm.agent
}
onClick={() => void handleSaveFastGpt()}
onClick={() => void handleSaveAgentPlatform()}
>
{saving ? (
<Loader2 size={16} className="animate-spin" />
@@ -1471,15 +1381,44 @@ export function AssistantPage(props: AssistantPageProps) {
<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={<Database size={15} />}
title="FastGPT 应用配置"
description="从「模型资源」中选择 FastGPT 应用。开场白、知识库、提示词等对话编排请在 FastGPT 平台配置,本页不重复设置。"
icon={<Boxes size={15} />}
title="智能体平台配置"
description={`选择智能体平台及对应应用。开场白、知识库、提示词等对话编排请在 ${platformLabel} 平台配置,本页不重复设置。`}
>
<div>
<div className="mb-2 text-sm font-medium text-foreground">
</div>
<div className="flex flex-wrap gap-2">
{(["dify", "fastgpt"] as const).map((platform) => {
const selected = agentPlatformForm.platform === platform;
const label = platform === "dify" ? "Dify" : "FastGPT";
return (
<Button
key={platform}
type="button"
size="sm"
variant={selected ? "default" : "outline"}
className={
selected
? "gap-2"
: "gap-2 border-hairline-strong text-muted-foreground hover:text-foreground"
}
onClick={() => handleAgentPlatformChange(platform)}
>
{selected && <Check size={14} />}
{label}
</Button>
);
})}
</div>
</div>
<ResourceSelectField
label="FastGPT 应用"
value={fastGptForm.agent}
onChange={(value) => updateFastGptForm("agent", value)}
options={agentOptions("fastgpt")}
label={`${platformLabel} 应用`}
value={agentPlatformForm.agent}
onChange={(value) => updateAgentPlatformForm("agent", value)}
options={agentOptions(agentPlatformForm.platform)}
noneLabel="请选择"
/>
</SectionCard>
@@ -1487,19 +1426,19 @@ export function AssistantPage(props: AssistantPageProps) {
<SectionCard
icon={<Brain size={15} />}
title="语音配置"
description="从「模型资源」中选择语音识别与语音合成。大模型、知识库与开场白由 FastGPT 应用提供,请前往 FastGPT 平台配置。"
description={`从「模型资源」中选择语音识别与语音合成。大模型、知识库与开场白由 ${platformLabel} 应用提供,请前往 ${platformLabel} 平台配置。`}
>
<ResourceSelectField
label="语音识别"
value={fastGptForm.asr}
onChange={(value) => updateFastGptForm("asr", value)}
value={agentPlatformForm.asr}
onChange={(value) => updateAgentPlatformForm("asr", value)}
options={credOptions("ASR")}
noneLabel="无"
/>
<ResourceSelectField
label="语音合成"
value={fastGptForm.voice}
onChange={(value) => updateFastGptForm("voice", value)}
value={agentPlatformForm.voice}
onChange={(value) => updateAgentPlatformForm("voice", value)}
options={credOptions("TTS")}
noneLabel="无"
/>
@@ -1511,19 +1450,19 @@ export function AssistantPage(props: AssistantPageProps) {
description="设置实时视频对话时的交互体验"
>
<TurnConfigEditor
enabled={fastGptForm.enableInterrupt}
config={fastGptForm.turnConfig}
onEnabledChange={(checked) => updateFastGptForm("enableInterrupt", checked)}
onConfigChange={(config) => updateFastGptForm("turnConfig", config)}
enabled={agentPlatformForm.enableInterrupt}
config={agentPlatformForm.turnConfig}
onEnabledChange={(checked) =>
updateAgentPlatformForm("enableInterrupt", checked)
}
onConfigChange={(config) =>
updateAgentPlatformForm("turnConfig", config)
}
/>
</SectionCard>
</div>
<DebugDrawer
assistantId={editingId}
hasUnsavedChanges={dirty}
vision={openCodeForm.visionEnabled}
/>
<DebugDrawer assistantId={editingId} hasUnsavedChanges={dirty} />
</div>
</div>
);