2026-07-14 12:59:41 +08:00
|
|
|
"use client";
|
|
|
|
|
|
|
|
|
|
import { Bug, Loader2, Save } from "lucide-react";
|
|
|
|
|
|
|
|
|
|
import { DebugDrawer } from "@/components/assistant-editor/debug-preview";
|
2026-08-03 09:58:01 +08:00
|
|
|
import { DynamicVariablesPanel } from "@/components/assistant-editor/dynamic-variables";
|
2026-07-14 12:59:41 +08:00
|
|
|
import {
|
|
|
|
|
AssistantIdentity,
|
|
|
|
|
EditableTitle,
|
|
|
|
|
EditorBackButton,
|
|
|
|
|
} from "@/components/assistant-editor/editor-controls";
|
|
|
|
|
import { Button } from "@/components/ui/button";
|
|
|
|
|
import { WorkflowEditor } from "@/components/workflow/WorkflowEditor";
|
|
|
|
|
import type { WorkflowSettings } from "@/components/workflow/types";
|
2026-07-18 00:00:28 +08:00
|
|
|
import {
|
|
|
|
|
workflowUsesVision,
|
|
|
|
|
type WorkflowGraph,
|
|
|
|
|
} from "@/components/workflow/specs";
|
2026-07-14 12:59:41 +08:00
|
|
|
import type { DynamicVariableDefinition } from "@/lib/api";
|
|
|
|
|
|
|
|
|
|
type ResourceOption = { value: string; label: string };
|
|
|
|
|
|
|
|
|
|
type WorkflowPageProps = {
|
|
|
|
|
assistantId: string | null;
|
|
|
|
|
workflowName: string;
|
|
|
|
|
workflowGraph: WorkflowGraph;
|
|
|
|
|
workflowSettings: WorkflowSettings;
|
|
|
|
|
dynamicVariableDefinitions: Record<string, DynamicVariableDefinition>;
|
|
|
|
|
workflowDebugOpen: boolean;
|
|
|
|
|
workflowSettingsOpen: boolean;
|
|
|
|
|
workflowEditingNodeId: string | null;
|
|
|
|
|
workflowEditingEdgeId: string | null;
|
|
|
|
|
activeNodeId: string | null;
|
|
|
|
|
dynamicVariablesOpen: boolean;
|
|
|
|
|
saving: boolean;
|
|
|
|
|
dirty: boolean;
|
|
|
|
|
saveError: string | null;
|
|
|
|
|
modelOptions: {
|
|
|
|
|
llm: ResourceOption[];
|
|
|
|
|
asr: ResourceOption[];
|
|
|
|
|
tts: ResourceOption[];
|
2026-07-18 00:00:28 +08:00
|
|
|
vision: ResourceOption[];
|
2026-07-14 12:59:41 +08:00
|
|
|
};
|
|
|
|
|
toolOptions: ResourceOption[];
|
|
|
|
|
knowledgeOptions: ResourceOption[];
|
|
|
|
|
onBack: () => void;
|
|
|
|
|
onSave: () => void;
|
|
|
|
|
setWorkflowName: (name: string) => void;
|
|
|
|
|
setWorkflowGraph: (graph: WorkflowGraph) => void;
|
|
|
|
|
setWorkflowSettings: (settings: WorkflowSettings) => void;
|
|
|
|
|
setWorkflowDynamicVariableDefinitions: (
|
|
|
|
|
definitions: Record<string, DynamicVariableDefinition>,
|
|
|
|
|
) => void;
|
|
|
|
|
setWorkflowDebugOpen: (open: boolean) => void;
|
|
|
|
|
setWorkflowSettingsOpen: (open: boolean) => void;
|
|
|
|
|
setWorkflowEditingNodeId: (nodeId: string | null) => void;
|
|
|
|
|
setWorkflowEditingEdgeId: (edgeId: string | null) => void;
|
|
|
|
|
setActiveNodeId: (nodeId: string | null) => void;
|
|
|
|
|
setDynamicVariablesOpen: (open: boolean) => void;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export function WorkflowPage({
|
|
|
|
|
assistantId,
|
|
|
|
|
workflowName,
|
|
|
|
|
workflowGraph,
|
|
|
|
|
workflowSettings,
|
|
|
|
|
dynamicVariableDefinitions,
|
|
|
|
|
workflowDebugOpen,
|
|
|
|
|
workflowSettingsOpen,
|
|
|
|
|
workflowEditingNodeId,
|
|
|
|
|
workflowEditingEdgeId,
|
|
|
|
|
activeNodeId,
|
|
|
|
|
dynamicVariablesOpen,
|
|
|
|
|
saving,
|
|
|
|
|
dirty,
|
|
|
|
|
saveError,
|
|
|
|
|
modelOptions,
|
|
|
|
|
toolOptions,
|
|
|
|
|
knowledgeOptions: kbOptions,
|
|
|
|
|
onBack,
|
|
|
|
|
onSave,
|
|
|
|
|
setWorkflowName,
|
|
|
|
|
setWorkflowGraph,
|
|
|
|
|
setWorkflowSettings,
|
|
|
|
|
setWorkflowDynamicVariableDefinitions,
|
|
|
|
|
setWorkflowDebugOpen,
|
|
|
|
|
setWorkflowSettingsOpen,
|
|
|
|
|
setWorkflowEditingNodeId,
|
|
|
|
|
setWorkflowEditingEdgeId,
|
|
|
|
|
setActiveNodeId,
|
|
|
|
|
setDynamicVariablesOpen,
|
|
|
|
|
}: WorkflowPageProps) {
|
|
|
|
|
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={onBack} />
|
|
|
|
|
<EditableTitle value={workflowName} onChange={setWorkflowName} />
|
|
|
|
|
<AssistantIdentity assistantId={assistantId} />
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="flex shrink-0 gap-2">
|
|
|
|
|
{saveError && (
|
|
|
|
|
<span
|
|
|
|
|
role="alert"
|
|
|
|
|
title={saveError}
|
|
|
|
|
className="line-clamp-1 max-w-[min(42vw,560px)] self-center text-right text-sm leading-5 text-destructive"
|
|
|
|
|
>
|
|
|
|
|
{saveError}
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
<Button
|
|
|
|
|
variant="outline"
|
|
|
|
|
className="gap-2 border-hairline-strong text-foreground hover:bg-surface-strong"
|
|
|
|
|
disabled={!assistantId}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
setWorkflowSettingsOpen(false);
|
2026-08-03 09:58:01 +08:00
|
|
|
setDynamicVariablesOpen(false);
|
2026-07-14 12:59:41 +08:00
|
|
|
setWorkflowEditingNodeId(null);
|
|
|
|
|
setWorkflowEditingEdgeId(null);
|
|
|
|
|
setWorkflowDebugOpen(true);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<Bug size={16} />
|
|
|
|
|
调试
|
|
|
|
|
</Button>
|
|
|
|
|
<Button
|
|
|
|
|
className="gap-2"
|
|
|
|
|
disabled={saving || !dirty || !workflowName.trim()}
|
|
|
|
|
onClick={onSave}
|
|
|
|
|
>
|
|
|
|
|
{saving ? (
|
|
|
|
|
<Loader2 size={16} className="animate-spin" />
|
|
|
|
|
) : (
|
|
|
|
|
<Save size={16} />
|
|
|
|
|
)}
|
|
|
|
|
保存
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="min-h-0 flex-1">
|
|
|
|
|
<WorkflowEditor
|
|
|
|
|
value={workflowGraph}
|
|
|
|
|
onChange={setWorkflowGraph}
|
|
|
|
|
settings={workflowSettings}
|
|
|
|
|
onSettingsChange={setWorkflowSettings}
|
2026-08-03 09:58:01 +08:00
|
|
|
dynamicVariablesOpen={dynamicVariablesOpen}
|
|
|
|
|
onDynamicVariablesOpenChange={setDynamicVariablesOpen}
|
|
|
|
|
dynamicVariablesPanel={
|
|
|
|
|
<DynamicVariablesPanel
|
|
|
|
|
definitions={dynamicVariableDefinitions}
|
|
|
|
|
onChange={setWorkflowDynamicVariableDefinitions}
|
|
|
|
|
/>
|
|
|
|
|
}
|
2026-07-14 12:59:41 +08:00
|
|
|
editingNodeId={workflowEditingNodeId}
|
|
|
|
|
onEditingNodeIdChange={setWorkflowEditingNodeId}
|
|
|
|
|
editingEdgeId={workflowEditingEdgeId}
|
|
|
|
|
onEditingEdgeIdChange={setWorkflowEditingEdgeId}
|
|
|
|
|
settingsOpen={workflowSettingsOpen}
|
|
|
|
|
onSettingsOpenChange={setWorkflowSettingsOpen}
|
|
|
|
|
debugOpen={workflowDebugOpen}
|
|
|
|
|
onDebugOpenChange={(open) => {
|
|
|
|
|
setWorkflowDebugOpen(open);
|
|
|
|
|
if (!open) setActiveNodeId(null);
|
|
|
|
|
}}
|
|
|
|
|
debugPanel={
|
|
|
|
|
<DebugDrawer
|
|
|
|
|
overlay
|
|
|
|
|
assistantId={assistantId}
|
2026-07-18 00:00:28 +08:00
|
|
|
vision={workflowUsesVision(workflowGraph)}
|
2026-07-14 12:59:41 +08:00
|
|
|
onClose={() => {
|
|
|
|
|
setWorkflowDebugOpen(false);
|
|
|
|
|
setActiveNodeId(null);
|
|
|
|
|
}}
|
|
|
|
|
hasUnsavedChanges={dirty}
|
|
|
|
|
onNodeActive={setActiveNodeId}
|
|
|
|
|
dynamicVariablesEnabled
|
|
|
|
|
dynamicVariableDefinitions={
|
|
|
|
|
dynamicVariableDefinitions
|
|
|
|
|
}
|
|
|
|
|
/>
|
|
|
|
|
}
|
|
|
|
|
activeNodeId={activeNodeId}
|
|
|
|
|
modelOptions={modelOptions}
|
|
|
|
|
toolOptions={toolOptions}
|
|
|
|
|
knowledgeOptions={kbOptions}
|
2026-08-04 15:27:39 +08:00
|
|
|
dynamicVariableOptions={Object.entries(
|
|
|
|
|
dynamicVariableDefinitions,
|
|
|
|
|
).map(([value, definition]) => ({
|
|
|
|
|
value,
|
|
|
|
|
label: `${value} · ${definition.type}`,
|
|
|
|
|
}))}
|
2026-07-14 12:59:41 +08:00
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|