refactor: unify system tools as resources

This commit is contained in:
Xin Wang
2026-08-04 17:05:26 +08:00
parent d1b05f16c7
commit 74a8be2357
26 changed files with 788 additions and 507 deletions

View File

@@ -8,7 +8,6 @@ import {
ChevronLeft,
Copy,
Pencil,
PhoneOff,
Plus,
ServerCog,
Settings2,
@@ -19,7 +18,6 @@ import {
} from "lucide-react";
import { HelpHint } from "@/components/editor/section-card";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
Dialog,
@@ -47,10 +45,8 @@ import {
import { Textarea } from "@/components/ui/textarea";
import type {
KnowledgeRetrievalConfig,
SystemToolKind,
Tool,
} from "@/lib/api";
import { SYSTEM_TOOL_OPTIONS } from "@/lib/system-tools";
import type { RuntimeMode } from "./types";
@@ -487,34 +483,20 @@ export function ToolPicker({
tools,
selectedIds,
onChange,
selectedSystemTools,
onSystemToolsChange,
showBuiltInSystemTools,
}: {
tools: Tool[];
selectedIds: string[];
onChange: (ids: string[]) => void;
selectedSystemTools: SystemToolKind[];
onSystemToolsChange: (tools: SystemToolKind[]) => void;
showBuiltInSystemTools: boolean;
}) {
const [open, setOpen] = useState(false);
const [activeTab, setActiveTab] = useState<Tool["type"]>("system");
const [draftIds, setDraftIds] = useState<string[]>(selectedIds);
const [draftSystemTools, setDraftSystemTools] =
useState<SystemToolKind[]>(selectedSystemTools);
const selectedTools = selectedIds
.map((id) => tools.find((tool) => tool.id === id))
.filter((tool): tool is Tool => Boolean(tool));
const selectedBuiltInTools = selectedSystemTools
.map((kind) => SYSTEM_TOOL_OPTIONS.find((option) => option.value === kind))
.filter((option): option is (typeof SYSTEM_TOOL_OPTIONS)[number] =>
Boolean(option),
);
function openPicker() {
setDraftIds(selectedIds);
setDraftSystemTools(selectedSystemTools);
setOpen(true);
}
@@ -526,14 +508,6 @@ export function ToolPicker({
);
}
function toggleBuiltInTool(kind: SystemToolKind) {
setDraftSystemTools((current) =>
current.includes(kind)
? current.filter((item) => item !== kind)
: [...current, kind],
);
}
const tabs: Array<{ value: Tool["type"]; label: string }> = [
{ value: "system", label: "System" },
{ value: "http", label: "HTTP" },
@@ -544,34 +518,13 @@ export function ToolPicker({
return (
<>
<div className="flex min-h-9 flex-wrap items-center gap-2">
{selectedBuiltInTools.map((tool) => (
<div
key={`built-in-${tool.value}`}
className="flex h-8 items-center gap-2 rounded-lg border border-hairline-strong bg-background px-2.5 text-sm"
>
<Sparkles size={14} />
<span className="max-w-48 truncate">{tool.label}</span>
<button
type="button"
onClick={() =>
onSystemToolsChange(
selectedSystemTools.filter((kind) => kind !== tool.value),
)
}
className="text-muted-soft transition-colors hover:text-foreground"
aria-label={`移除系统工具 ${tool.label}`}
>
<X size={13} />
</button>
</div>
))}
{selectedTools.map((tool) => (
<div
key={tool.id}
className="flex h-8 items-center gap-2 rounded-lg border border-hairline-strong bg-background px-2.5 text-sm"
>
{tool.type === "system" ? (
<PhoneOff size={14} />
<Sparkles size={14} />
) : tool.type === "mcp" ? (
<ServerCog size={14} />
) : (
@@ -606,7 +559,7 @@ export function ToolPicker({
<DialogHeader>
<DialogTitle></DialogTitle>
<DialogDescription>
</DialogDescription>
</DialogHeader>
@@ -631,9 +584,7 @@ export function ToolPicker({
{tabs.map((tab) => {
const resources = tools.filter((tool) => tool.type === tab.value);
const hasBuiltIns =
tab.value === "system" && showBuiltInSystemTools;
const isEmpty = resources.length === 0 && !hasBuiltIns;
const isEmpty = resources.length === 0;
return (
<TabsContent key={tab.value} value={tab.value} className="pt-3">
@@ -643,35 +594,6 @@ export function ToolPicker({
</div>
) : (
<div className="max-h-[280px] divide-y divide-hairline overflow-y-auto rounded-xl border border-hairline">
{hasBuiltIns &&
SYSTEM_TOOL_OPTIONS.map((option) => {
const checked = draftSystemTools.includes(option.value);
return (
<label
key={`built-in-${option.value}`}
className="flex h-14 cursor-pointer items-center gap-3 px-4 transition-colors hover:bg-surface-strong/40"
>
<input
type="checkbox"
checked={checked}
onChange={() => toggleBuiltInTool(option.value)}
className="size-4 accent-primary"
/>
<div className="min-w-0 flex-1">
<div className="flex items-center gap-2">
<span className="truncate font-medium text-foreground">
{option.label}
</span>
<Badge variant="secondary"></Badge>
</div>
<div className="mt-0.5 truncate text-xs text-muted-foreground">
{option.description}
</div>
</div>
</label>
);
})}
{resources.map((tool) => {
const checked = draftIds.includes(tool.id);
return (
@@ -686,13 +608,8 @@ export function ToolPicker({
className="size-4 accent-primary"
/>
<div className="min-w-0 flex-1">
<div className="flex items-center gap-2">
<span className="truncate font-medium text-foreground">
{tool.name}
</span>
{tool.type === "system" && (
<Badge variant="secondary"></Badge>
)}
<div className="truncate font-medium text-foreground">
{tool.name}
</div>
<div className="mt-0.5 truncate font-mono text-xs text-muted-foreground">
{tool.functionName}
@@ -715,7 +632,6 @@ export function ToolPicker({
<Button
onClick={() => {
onChange(draftIds);
onSystemToolsChange(draftSystemTools);
setOpen(false);
}}
>

View File

@@ -506,8 +506,15 @@ export function PromptEditor({
openingMessage: null,
});
}
if (runtimeMode === "realtime" && form.systemTools.length) {
updateForm("systemTools", []);
if (runtimeMode === "realtime") {
updateForm(
"toolIds",
form.toolIds.filter(
(id) =>
tools.find((tool) => tool.id === id)?.type !==
"system",
),
);
}
}}
/>
@@ -602,14 +609,13 @@ export function PromptEditor({
description="配置该提示词助手可以调用的工具"
>
<ToolPicker
tools={tools.filter((tool) => tool.status === "active")}
tools={tools.filter(
(tool) =>
tool.status === "active" &&
(form.runtimeMode === "pipeline" || tool.type !== "system"),
)}
selectedIds={form.toolIds}
onChange={(toolIds) => updateForm("toolIds", toolIds)}
selectedSystemTools={form.systemTools}
onSystemToolsChange={(systemTools) =>
updateForm("systemTools", systemTools)
}
showBuiltInSystemTools={form.runtimeMode === "pipeline"}
/>
</SectionCard>
</section>

View File

@@ -2,7 +2,6 @@ import type {
DynamicVariableDefinition,
KnowledgeRetrievalConfig,
StartupConfig,
SystemToolKind,
TurnConfig,
} from "@/lib/api";
@@ -23,7 +22,6 @@ export type AssistantForm = {
enableInterrupt: boolean;
turnConfig: TurnConfig;
startup: StartupConfig;
systemTools: SystemToolKind[];
visionEnabled: boolean;
visionModelResourceId: string;
toolIds: string[];

View File

@@ -184,7 +184,6 @@ function blankPromptForm(name: string): AssistantForm {
actions: [],
openingMessage: null,
},
systemTools: [],
visionEnabled: false,
visionModelResourceId: "",
toolIds: [],
@@ -480,7 +479,6 @@ export function AssistantPage(props: AssistantPageProps) {
actions: a.startup?.actions ?? [],
openingMessage: a.startup?.openingMessage ?? null,
},
systemTools: a.systemTools ?? [],
visionEnabled: a.visionEnabled,
visionModelResourceId: a.visionModelResourceId ?? "",
toolIds: a.toolIds ?? [],
@@ -557,7 +555,6 @@ export function AssistantPage(props: AssistantPageProps) {
actions: [],
openingMessage: null,
},
systemTools: [],
visionEnabled: false,
visionModelResourceId: null,
modelResourceIds: {},
@@ -617,7 +614,6 @@ export function AssistantPage(props: AssistantPageProps) {
enableInterrupt: form.enableInterrupt,
turnConfig: form.turnConfig,
startup: form.startup,
systemTools: form.runtimeMode === "pipeline" ? form.systemTools : [],
visionEnabled: form.visionEnabled,
visionModelResourceId: form.visionModelResourceId || null,
modelResourceIds: {
@@ -1304,10 +1300,16 @@ export function AssistantPage(props: AssistantPageProps) {
vision: visionModelOptionsFor(""),
}}
toolOptions={tools
.filter(
(tool) => tool.status === "active" && tool.type !== "system",
)
.map((tool) => ({ value: tool.id, label: tool.name }))}
.filter((tool) => tool.status === "active")
.map((tool) => ({
value: tool.id,
label: tool.name,
toolType: tool.type,
systemKind:
tool.definition.type === "system"
? tool.definition.config.kind
: undefined,
}))}
knowledgeOptions={kbOptions}
onBack={() => router.push("/assistants")}
onSave={() => void handleSaveWorkflow()}

View File

@@ -57,12 +57,14 @@ import {
type ClientToolResponseWaitMode,
type HttpToolDefinition,
type McpServer,
type SystemToolKind,
type Tool,
type ToolParameter,
type ToolExecutionMode,
type ToolStatus,
type ToolUpsert,
} from "@/lib/api";
import { SYSTEM_TOOL_OPTIONS } from "@/lib/system-tools";
type ToolKind = "system" | "http" | "client";
type HttpMethod = HttpToolDefinition["config"]["method"];
@@ -86,6 +88,7 @@ type ToolForm = {
type: ToolKind;
description: string;
status: ToolStatus;
systemKind: SystemToolKind;
messageType: "none" | "custom";
customMessage: string;
captureReason: boolean;
@@ -115,6 +118,7 @@ function blankForm(): ToolForm {
type: "system",
description: "",
status: "active",
systemKind: "end_conversation",
messageType: "none",
customMessage: "",
captureReason: true,
@@ -148,6 +152,7 @@ function formFromTool(tool: Tool): ToolForm {
base.description = tool.description;
base.status = tool.status;
if (tool.definition.type === "system") {
base.systemKind = tool.definition.config.kind;
base.messageType = tool.definition.config.messageType;
base.customMessage = tool.definition.config.customMessage;
base.captureReason = tool.definition.config.captureReason;
@@ -246,9 +251,13 @@ function payloadFromForm(form: ToolForm): ToolUpsert {
schemaVersion: 1,
type: "system",
config: {
kind: "end_conversation",
kind: form.systemKind,
messageType: form.messageType,
customMessage: form.messageType === "custom" ? form.customMessage : "",
customMessage:
form.systemKind === "end_conversation" &&
form.messageType === "custom"
? form.customMessage
: "",
captureReason: form.captureReason,
},
},
@@ -871,52 +880,80 @@ function SystemToolFields({
return (
<div className="space-y-4">
<Field label="系统动作">
<Select value="end_conversation" disabled>
<Select
value={form.systemKind}
onValueChange={(systemKind: SystemToolKind) =>
setForm((current) => ({
...current,
systemKind,
functionName: systemKind,
}))
}
>
<SelectTrigger className="w-full border-hairline-strong bg-background">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="end_conversation"></SelectItem>
{SYSTEM_TOOL_OPTIONS.map((option) => (
<SelectItem key={option.value} value={option.value}>
{option.label}
</SelectItem>
))}
</SelectContent>
</Select>
</Field>
<Field label="结束语">
<Select
value={form.messageType}
onValueChange={(messageType: "none" | "custom") =>
setForm((current) => ({ ...current, messageType }))
}
>
<SelectTrigger className="w-full border-hairline-strong bg-background"><SelectValue /></SelectTrigger>
<SelectContent>
<SelectItem value="none"></SelectItem>
<SelectItem value="custom"></SelectItem>
</SelectContent>
</Select>
</Field>
{form.messageType === "custom" && (
<Field label="自定义结束语">
<Textarea
value={form.customMessage}
onChange={(event) =>
setForm((current) => ({ ...current, customMessage: event.target.value }))
}
rows={3}
/>
</Field>
<p className="text-xs leading-5 text-muted-foreground">
{SYSTEM_TOOL_OPTIONS.find((option) => option.value === form.systemKind)
?.description ?? "由平台执行的会话控制能力。"}
</p>
{form.systemKind === "end_conversation" && (
<>
<Field label="结束语">
<Select
value={form.messageType}
onValueChange={(messageType: "none" | "custom") =>
setForm((current) => ({ ...current, messageType }))
}
>
<SelectTrigger className="w-full border-hairline-strong bg-background">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="none"></SelectItem>
<SelectItem value="custom"></SelectItem>
</SelectContent>
</Select>
</Field>
{form.messageType === "custom" && (
<Field label="自定义结束语">
<Textarea
value={form.customMessage}
onChange={(event) =>
setForm((current) => ({
...current,
customMessage: event.target.value,
}))
}
rows={3}
/>
</Field>
)}
<div className="flex items-center justify-between gap-4 rounded-lg border border-hairline-strong px-4 py-3">
<div>
<div className="font-medium text-foreground"></div>
<div className="mt-0.5 text-xs text-muted-foreground">
reason
</div>
</div>
<Switch
checked={form.captureReason}
onCheckedChange={(captureReason) =>
setForm((current) => ({ ...current, captureReason }))
}
/>
</div>
</>
)}
<div className="flex items-center justify-between gap-4 rounded-lg border border-hairline-strong px-4 py-3">
<div>
<div className="font-medium text-foreground"></div>
<div className="mt-0.5 text-xs text-muted-foreground"> reason </div>
</div>
<Switch
checked={form.captureReason}
onCheckedChange={(captureReason) =>
setForm((current) => ({ ...current, captureReason }))
}
/>
</div>
</div>
);
}

View File

@@ -72,7 +72,6 @@ function defaultNodeData(spec: RuntimeNodeSpec): WorkflowNodeData {
contextPolicy: "inherit",
inheritGlobalConfig: true,
entryMode: "wait_user",
systemTools: [],
stateVariableNames: [],
});
} else if (spec.type === "action") {

View File

@@ -48,7 +48,7 @@ export function ActionNodePanel({
<NodeSelect
label="执行工具"
value={(draft.toolId as string) || ""}
options={toolOptions}
options={toolOptions.filter((option) => option.toolType !== "system")}
onChange={(value) => set("toolId", value || "")}
noneLabel="请选择工具"
/>

View File

@@ -16,8 +16,7 @@ import { VisionConfigSection } from "@/components/editor/vision-config-section";
import { TurnConfigEditor } from "@/components/turn-config-editor";
import { Switch } from "@/components/ui/switch";
import { Textarea } from "@/components/ui/textarea";
import type { KnowledgeRetrievalConfig, SystemToolKind } from "@/lib/api";
import { SYSTEM_TOOL_OPTIONS } from "@/lib/system-tools";
import type { KnowledgeRetrievalConfig } from "@/lib/api";
import { normalizeTurnConfig } from "@/lib/turn-config";
import { NodeSelect, ToolOptionPicker } from "./controls";
@@ -104,18 +103,14 @@ export function AgentNodePanel({
turnConfig: agentTurnConfig,
});
};
const toggleSystemTool = (kind: SystemToolKind, enabled: boolean) => {
const current = draft.systemTools ?? [];
const systemTools = enabled
? [...new Set([...current, kind])]
: current.filter((item) => item !== kind);
setPatch({
systemTools,
...(!enabled && kind === "update_state"
? { stateVariableNames: [] }
: {}),
});
};
const selectedToolIds = inheritsGlobal
? workflowSettings.toolIds
: draft.toolIds ?? [];
const updateStateEnabled = selectedToolIds.some(
(toolId) =>
toolOptions.find((option) => option.value === toolId)?.systemKind ===
"update_state",
);
const toggleStateVariable = (name: string, enabled: boolean) => {
const current = draft.stateVariableNames ?? [];
set(
@@ -133,7 +128,9 @@ export function AgentNodePanel({
{ id: "scope", label: "配置范围" },
{ id: "prompt", label: inheritsGlobal ? "任务" : "提示词" },
{ id: "entry", label: "进入行为" },
{ id: "system-tools", label: "系统工具" },
...(updateStateEnabled
? [{ id: "state-scope", label: "状态更新权限" }]
: []),
...(!inheritsGlobal
? [
{ id: "models", label: "模型与语音" },
@@ -211,77 +208,43 @@ export function AgentNodePanel({
</SectionCard>
</PanelAnchor>
<PanelAnchor id="system-tools">
<SectionCard
icon={<Sparkles size={15} />}
title="系统工具"
description="只对当前 Agent 生效的内置会话控制能力"
>
<div className="space-y-3">
{SYSTEM_TOOL_OPTIONS.map((option) => {
const enabled = (draft.systemTools ?? []).includes(option.value);
return (
<div
key={option.value}
className="rounded-xl border border-hairline bg-canvas-soft p-3.5"
>
<div className="flex items-start justify-between gap-4">
<div className="min-w-0">
<div className="text-sm font-medium text-foreground">
{option.label}
</div>
<p className="mt-1 text-xs leading-5 text-muted-foreground">
{option.description}
</p>
</div>
{updateStateEnabled && (
<PanelAnchor id="state-scope">
<SectionCard
icon={<Sparkles size={15} />}
title="状态更新权限"
description="更新状态工具只允许写入这里授权的动态变量"
>
{dynamicVariableOptions.length ? (
<div className="space-y-2">
{dynamicVariableOptions.map((variable) => (
<div
key={variable.value}
className="flex items-center justify-between gap-3 rounded-lg border border-hairline bg-background px-3 py-2"
>
<span className="truncate text-xs text-foreground">
{variable.label}
</span>
<Switch
checked={enabled}
checked={(draft.stateVariableNames ?? []).includes(
variable.value,
)}
onCheckedChange={(checked) =>
toggleSystemTool(option.value, checked)
toggleStateVariable(variable.value, checked)
}
aria-label={`启用${option.label}`}
aria-label={`允许更新${variable.value}`}
/>
</div>
{option.value === "update_state" && enabled && (
<div className="mt-3 border-t border-hairline pt-3">
<div className="mb-2 text-xs font-medium text-foreground">
</div>
{dynamicVariableOptions.length ? (
<div className="space-y-2">
{dynamicVariableOptions.map((variable) => (
<div
key={variable.value}
className="flex items-center justify-between gap-3 rounded-lg border border-hairline bg-background px-3 py-2"
>
<span className="truncate text-xs text-foreground">
{variable.label}
</span>
<Switch
checked={(draft.stateVariableNames ?? []).includes(
variable.value,
)}
onCheckedChange={(checked) =>
toggleStateVariable(variable.value, checked)
}
aria-label={`允许更新${variable.value}`}
/>
</div>
))}
</div>
) : (
<p className="text-xs leading-5 text-muted-foreground">
</p>
)}
</div>
)}
</div>
);
})}
</div>
</SectionCard>
</PanelAnchor>
))}
</div>
) : (
<p className="text-xs leading-5 text-muted-foreground">
</p>
)}
</SectionCard>
</PanelAnchor>
)}
{!inheritsGlobal && (
<>

View File

@@ -1,6 +1,6 @@
"use client";
import { Plus, Wrench, X } from "lucide-react";
import { Plus, ServerCog, Sparkles, Wrench, X } from "lucide-react";
import { useState } from "react";
import { Button } from "@/components/ui/button";
@@ -19,6 +19,13 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from "@/components/ui/tabs";
import type { Tool } from "@/lib/api";
import type { ModelOption } from "../types";
@@ -70,10 +77,17 @@ export function ToolOptionPicker({
onChange: (ids: string[]) => void;
}) {
const [open, setOpen] = useState(false);
const [activeTab, setActiveTab] = useState<Tool["type"]>("system");
const [draftIds, setDraftIds] = useState<string[]>(selectedIds);
const selected = selectedIds
.map((id) => options.find((option) => option.value === id))
.filter((option): option is ModelOption => Boolean(option));
const tabs: Array<{ value: Tool["type"]; label: string }> = [
{ value: "system", label: "System" },
{ value: "http", label: "HTTP" },
{ value: "client", label: "Client" },
{ value: "mcp", label: "MCP" },
];
return (
<>
@@ -83,7 +97,13 @@ export function ToolOptionPicker({
key={option.value}
className="flex h-8 items-center gap-2 rounded-lg border border-hairline-strong bg-background px-2.5 text-sm"
>
<Wrench size={14} />
{option.toolType === "system" ? (
<Sparkles size={14} />
) : option.toolType === "mcp" ? (
<ServerCog size={14} />
) : (
<Wrench size={14} />
)}
<span className="max-w-48 truncate">{option.label}</span>
<button
type="button"
@@ -119,39 +139,69 @@ export function ToolOptionPicker({
<DialogTitle></DialogTitle>
<DialogDescription></DialogDescription>
</DialogHeader>
{options.length === 0 ? (
<div className="rounded-xl border border-dashed border-hairline-strong px-4 py-10 text-center text-sm text-muted-foreground">
</div>
) : (
<div className="max-h-80 divide-y divide-hairline overflow-y-auto rounded-xl border border-hairline">
{options.map((option) => {
const checked = draftIds.includes(option.value);
return (
<label
key={option.value}
className="flex cursor-pointer items-center gap-3 px-4 py-3 transition-colors hover:bg-surface-strong/40"
>
<input
type="checkbox"
checked={checked}
onChange={() =>
setDraftIds((current) =>
checked
? current.filter((id) => id !== option.value)
: [...current, option.value],
)
}
className="size-4 accent-primary"
/>
<span className="truncate font-medium text-foreground">
{option.label}
</span>
</label>
);
})}
</div>
)}
<Tabs
value={activeTab}
onValueChange={(value) => setActiveTab(value as Tool["type"])}
>
<TabsList
variant="line"
className="w-full justify-start border-b border-hairline px-1"
>
{tabs.map((tab) => (
<TabsTrigger
key={tab.value}
value={tab.value}
className="flex-none px-4"
>
{tab.label}
</TabsTrigger>
))}
</TabsList>
{tabs.map((tab) => {
const rows = options.filter(
(option) => (option.toolType ?? "http") === tab.value,
);
return (
<TabsContent key={tab.value} value={tab.value} className="pt-3">
{rows.length === 0 ? (
<div className="rounded-xl border border-dashed border-hairline-strong px-4 py-10 text-center text-sm text-muted-foreground">
{tab.label}
</div>
) : (
<div className="max-h-[280px] divide-y divide-hairline overflow-y-auto rounded-xl border border-hairline">
{rows.map((option) => {
const checked = draftIds.includes(option.value);
return (
<label
key={option.value}
className="flex h-14 cursor-pointer items-center gap-3 px-4 transition-colors hover:bg-surface-strong/40"
>
<input
type="checkbox"
checked={checked}
onChange={() =>
setDraftIds((current) =>
checked
? current.filter(
(id) => id !== option.value,
)
: [...current, option.value],
)
}
className="size-4 accent-primary"
/>
<span className="truncate font-medium text-foreground">
{option.label}
</span>
</label>
);
})}
</div>
)}
</TabsContent>
);
})}
</Tabs>
<DialogFooter>
<Button variant="outline" onClick={() => setOpen(false)}>
@@ -214,4 +264,3 @@ export function NodeSelect({
</div>
);
}

View File

@@ -3,7 +3,7 @@
import * as LucideIcons from "lucide-react";
import { Circle, type LucideIcon } from "lucide-react";
import type { NodeSpecDto, SystemToolKind, TurnConfig } from "@/lib/api";
import type { NodeSpecDto, TurnConfig } from "@/lib/api";
import { defaultTurnConfig } from "@/lib/turn-config";
export type WorkflowNodeType =
@@ -41,7 +41,6 @@ export type WorkflowNodeData = {
contextPolicy?: ContextPolicy;
inheritGlobalConfig?: boolean;
entryMode?: AgentEntryMode;
systemTools?: SystemToolKind[];
stateVariableNames?: string[];
toolIds?: string[];
knowledgeBaseId?: string;
@@ -283,7 +282,6 @@ export function defaultGraph(): WorkflowGraph {
contextPolicy: "inherit",
inheritGlobalConfig: true,
entryMode: "wait_user",
systemTools: [],
stateVariableNames: [],
},
},

View File

@@ -1,6 +1,11 @@
import type { ReactNode } from "react";
import type { KnowledgeRetrievalConfig, TurnConfig } from "@/lib/api";
import type {
KnowledgeRetrievalConfig,
SystemToolKind,
Tool,
TurnConfig,
} from "@/lib/api";
import type { WorkflowGraph } from "./specs";
@@ -18,7 +23,13 @@ export type WorkflowSettings = {
turnConfig: TurnConfig;
};
export type ModelOption = { value: string; label: string; disabled?: boolean };
export type ModelOption = {
value: string;
label: string;
disabled?: boolean;
toolType?: Tool["type"];
systemKind?: SystemToolKind;
};
export type WorkflowEditorProps = {
value?: WorkflowGraph;

View File

@@ -232,7 +232,6 @@ export type Assistant = {
enableInterrupt: boolean;
turnConfig: TurnConfig;
startup: StartupConfig;
systemTools: SystemToolKind[];
visionEnabled: boolean;
visionModelResourceId: string | null;
modelResourceIds: Partial<Record<ModelType, string>>;
@@ -365,7 +364,7 @@ export type SystemToolDefinition = {
schemaVersion: number;
type: "system";
config: {
kind: "end_conversation";
kind: SystemToolKind;
messageType: "none" | "custom";
customMessage: string;
captureReason: boolean;