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
@@ -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);
}}
>
@@ -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>
@@ -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[];