Add workflow support and enhance runtime configuration in models and services
- Introduce RuntimeModelResource and RuntimeKnowledgeBase classes to manage workflow resources. - Update AssistantConfig to include workflow_model_resources and workflow_knowledge_bases for better integration. - Refactor validation and processing logic in routes and services to accommodate workflow types. - Implement dynamic variable support for workflow assistants and enhance graph normalization. - Add ToolExecutor for reusable tool execution across different assistant types. - Update various services to ensure compatibility with new workflow features and improve error handling.
This commit is contained in:
@@ -53,13 +53,6 @@ import {
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import {
|
||||
Sheet,
|
||||
SheetContent,
|
||||
SheetDescription,
|
||||
SheetHeader,
|
||||
SheetTitle,
|
||||
} from "@/components/ui/sheet";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@@ -478,10 +471,15 @@ export function AssistantPage(props: AssistantPageProps) {
|
||||
defaultGraph(),
|
||||
);
|
||||
const [workflowSettings, setWorkflowSettings] = useState<WorkflowSettings>({
|
||||
globalPrompt: defaultGraph().settings.globalPrompt,
|
||||
allowInterrupt: true,
|
||||
turnConfig: defaultTurnConfig(),
|
||||
});
|
||||
const [debugOpen, setDebugOpen] = useState(false);
|
||||
const [workflowDynamicVariableDefinitions, setWorkflowDynamicVariableDefinitions] =
|
||||
useState<Record<string, DynamicVariableDefinition>>({});
|
||||
const [workflowDebugOpen, setWorkflowDebugOpen] = useState(false);
|
||||
const [workflowEditingNodeId, setWorkflowEditingNodeId] = useState<string | null>(null);
|
||||
const [workflowEditingEdgeId, setWorkflowEditingEdgeId] = useState<string | null>(null);
|
||||
const [activeNodeId, setActiveNodeId] = useState<string | null>(null);
|
||||
const [dynamicVariablesOpen, setDynamicVariablesOpen] = useState(false);
|
||||
|
||||
@@ -708,6 +706,7 @@ export function AssistantPage(props: AssistantPageProps) {
|
||||
name: workflowName,
|
||||
graph: workflowGraph,
|
||||
settings: workflowSettings,
|
||||
dynamicVariableDefinitions: workflowDynamicVariableDefinitions,
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -849,19 +848,24 @@ export function AssistantPage(props: AssistantPageProps) {
|
||||
: defaultGraph();
|
||||
const wfSettings: WorkflowSettings = {
|
||||
llm: assistant.modelResourceIds.LLM,
|
||||
asr: assistant.modelResourceIds.ASR,
|
||||
tts: assistant.modelResourceIds.TTS,
|
||||
asr: graph.settings?.defaultAsrResourceId || assistant.modelResourceIds.ASR,
|
||||
tts: graph.settings?.defaultTtsResourceId || assistant.modelResourceIds.TTS,
|
||||
globalPrompt: graph.settings?.globalPrompt ?? "",
|
||||
allowInterrupt: assistant.enableInterrupt,
|
||||
turnConfig: assistant.turnConfig,
|
||||
};
|
||||
setWorkflowName(assistant.name);
|
||||
setWorkflowGraph(graph);
|
||||
setWorkflowSettings(wfSettings);
|
||||
setWorkflowDynamicVariableDefinitions(
|
||||
assistant.dynamicVariableDefinitions ?? {},
|
||||
);
|
||||
setSavedSnapshot(
|
||||
JSON.stringify({
|
||||
name: assistant.name,
|
||||
graph,
|
||||
settings: wfSettings,
|
||||
dynamicVariableDefinitions: assistant.dynamicVariableDefinitions ?? {},
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -913,6 +917,7 @@ export function AssistantPage(props: AssistantPageProps) {
|
||||
...(workflowSettings.tts ? { TTS: workflowSettings.tts } : {}),
|
||||
},
|
||||
graph: workflowGraph as unknown as Record<string, unknown>,
|
||||
dynamicVariableDefinitions: workflowDynamicVariableDefinitions,
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -932,6 +937,7 @@ export function AssistantPage(props: AssistantPageProps) {
|
||||
name: workflowName,
|
||||
graph: workflowGraph,
|
||||
settings: workflowSettings,
|
||||
dynamicVariableDefinitions: workflowDynamicVariableDefinitions,
|
||||
})
|
||||
: null;
|
||||
const dirty =
|
||||
@@ -1377,7 +1383,11 @@ export function AssistantPage(props: AssistantPageProps) {
|
||||
|
||||
<div className="flex shrink-0 gap-2">
|
||||
{saveError && (
|
||||
<span className="self-center text-xs text-destructive">
|
||||
<span
|
||||
role="alert"
|
||||
title={saveError}
|
||||
className="line-clamp-2 max-w-[min(42vw,560px)] self-center text-right text-sm leading-5 text-destructive"
|
||||
>
|
||||
{saveError}
|
||||
</span>
|
||||
)}
|
||||
@@ -1385,7 +1395,11 @@ export function AssistantPage(props: AssistantPageProps) {
|
||||
variant="outline"
|
||||
className="gap-2 border-hairline-strong text-foreground hover:bg-surface-strong"
|
||||
disabled={!editingId}
|
||||
onClick={() => setDebugOpen(true)}
|
||||
onClick={() => {
|
||||
setWorkflowEditingNodeId(null);
|
||||
setWorkflowEditingEdgeId(null);
|
||||
setWorkflowDebugOpen(true);
|
||||
}}
|
||||
>
|
||||
<Bug size={16} />
|
||||
调试
|
||||
@@ -1411,43 +1425,49 @@ export function AssistantPage(props: AssistantPageProps) {
|
||||
onChange={setWorkflowGraph}
|
||||
settings={workflowSettings}
|
||||
onSettingsChange={setWorkflowSettings}
|
||||
onOpenDynamicVariables={() => setDynamicVariablesOpen(true)}
|
||||
editingNodeId={workflowEditingNodeId}
|
||||
onEditingNodeIdChange={setWorkflowEditingNodeId}
|
||||
editingEdgeId={workflowEditingEdgeId}
|
||||
onEditingEdgeIdChange={setWorkflowEditingEdgeId}
|
||||
debugOpen={workflowDebugOpen}
|
||||
onDebugOpenChange={(open) => {
|
||||
setWorkflowDebugOpen(open);
|
||||
if (!open) setActiveNodeId(null);
|
||||
}}
|
||||
debugPanel={
|
||||
<DebugDrawer
|
||||
overlay
|
||||
assistantId={editingId}
|
||||
onClose={() => {
|
||||
setWorkflowDebugOpen(false);
|
||||
setActiveNodeId(null);
|
||||
}}
|
||||
hasUnsavedChanges={dirty}
|
||||
onNodeActive={setActiveNodeId}
|
||||
dynamicVariablesEnabled
|
||||
dynamicVariableDefinitions={workflowDynamicVariableDefinitions}
|
||||
/>
|
||||
}
|
||||
activeNodeId={activeNodeId}
|
||||
modelOptions={{
|
||||
llm: credOptions("LLM"),
|
||||
asr: credOptions("ASR"),
|
||||
tts: credOptions("TTS"),
|
||||
}}
|
||||
toolOptions={tools
|
||||
.filter((tool) => tool.status === "active")
|
||||
.map((tool) => ({ value: tool.id, label: tool.name }))}
|
||||
knowledgeOptions={kbOptions}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Sheet
|
||||
open={debugOpen}
|
||||
onOpenChange={(open) => {
|
||||
setDebugOpen(open);
|
||||
if (!open) setActiveNodeId(null);
|
||||
}}
|
||||
modal={false}
|
||||
>
|
||||
<SheetContent
|
||||
side="right"
|
||||
showOverlay={false}
|
||||
onInteractOutside={(e) => e.preventDefault()}
|
||||
className="w-[440px] gap-0 border-l border-hairline bg-card p-0 sm:max-w-[440px]"
|
||||
>
|
||||
<SheetHeader className="sr-only">
|
||||
<SheetTitle>语音调试</SheetTitle>
|
||||
<SheetDescription>
|
||||
与当前助手进行语音对话调试,画布会高亮正在激活的节点。
|
||||
</SheetDescription>
|
||||
</SheetHeader>
|
||||
<DebugDrawer
|
||||
assistantId={editingId}
|
||||
asSheet
|
||||
hasUnsavedChanges={dirty}
|
||||
onNodeActive={setActiveNodeId}
|
||||
/>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
<DynamicVariablesDialog
|
||||
open={dynamicVariablesOpen}
|
||||
onOpenChange={setDynamicVariablesOpen}
|
||||
definitions={workflowDynamicVariableDefinitions}
|
||||
onChange={setWorkflowDynamicVariableDefinitions}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -2132,7 +2152,8 @@ function SegmentedIconButton({
|
||||
|
||||
function DebugDrawer({
|
||||
assistantId,
|
||||
asSheet = false,
|
||||
overlay = false,
|
||||
onClose,
|
||||
hasUnsavedChanges = false,
|
||||
onNodeActive,
|
||||
vision = false,
|
||||
@@ -2140,7 +2161,8 @@ function DebugDrawer({
|
||||
dynamicVariableDefinitions = {},
|
||||
}: {
|
||||
assistantId: string | null;
|
||||
asSheet?: boolean;
|
||||
overlay?: boolean;
|
||||
onClose?: () => void;
|
||||
hasUnsavedChanges?: boolean;
|
||||
onNodeActive?: (nodeId: string | null) => void;
|
||||
vision?: boolean;
|
||||
@@ -2179,14 +2201,25 @@ function DebugDrawer({
|
||||
[camera, preview],
|
||||
);
|
||||
|
||||
const containerClass = asSheet
|
||||
? "flex h-full min-w-0 flex-1 flex-col overflow-hidden"
|
||||
: "hidden min-w-0 flex-1 flex-col overflow-hidden rounded-2xl border border-hairline bg-card shadow-sm lg:flex";
|
||||
|
||||
return (
|
||||
<aside className={containerClass}>
|
||||
<div className="flex shrink-0 items-center justify-between gap-3 border-b border-hairline px-5 py-3">
|
||||
<aside
|
||||
className={overlay
|
||||
? "flex h-full min-w-0 flex-1 flex-col overflow-hidden bg-card"
|
||||
: "hidden min-w-0 flex-1 flex-col overflow-hidden rounded-2xl border border-hairline bg-card shadow-sm lg:flex"}
|
||||
>
|
||||
<div className={`flex min-h-14 shrink-0 items-center justify-between gap-3 border-b border-hairline py-3 ${overlay ? "px-4" : "px-5"}`}>
|
||||
<div className="flex min-w-0 items-center gap-2.5">
|
||||
{overlay && onClose && (
|
||||
<button
|
||||
type="button"
|
||||
aria-label="关闭调试预览"
|
||||
title="关闭"
|
||||
className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full border border-hairline-strong bg-card text-muted-foreground shadow-sm transition-colors hover:text-foreground"
|
||||
onClick={onClose}
|
||||
>
|
||||
<X size={16} />
|
||||
</button>
|
||||
)}
|
||||
<div className="shrink-0 text-sm font-medium text-foreground">
|
||||
调试与预览
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user