feat: organize tools and enable qwen realtime calls
This commit is contained in:
@@ -8,13 +8,8 @@ import {
|
||||
ChevronLeft,
|
||||
Copy,
|
||||
Pencil,
|
||||
Plus,
|
||||
ServerCog,
|
||||
Settings2,
|
||||
Sparkles,
|
||||
Waypoints,
|
||||
Wrench,
|
||||
X,
|
||||
} from "lucide-react";
|
||||
|
||||
import { HelpHint } from "@/components/editor/section-card";
|
||||
@@ -36,17 +31,8 @@ import {
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import {
|
||||
Tabs,
|
||||
TabsContent,
|
||||
TabsList,
|
||||
TabsTrigger,
|
||||
} from "@/components/ui/tabs";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import type {
|
||||
KnowledgeRetrievalConfig,
|
||||
Tool,
|
||||
} from "@/lib/api";
|
||||
import type { KnowledgeRetrievalConfig } from "@/lib/api";
|
||||
|
||||
import type { RuntimeMode } from "./types";
|
||||
|
||||
@@ -493,171 +479,6 @@ export function KnowledgeRetrievalConfigDialog({
|
||||
);
|
||||
}
|
||||
|
||||
export function ToolPicker({
|
||||
tools,
|
||||
selectedIds,
|
||||
onChange,
|
||||
}: {
|
||||
tools: Tool[];
|
||||
selectedIds: string[];
|
||||
onChange: (ids: string[]) => void;
|
||||
}) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState<Tool["type"]>("system");
|
||||
const [draftIds, setDraftIds] = useState<string[]>(selectedIds);
|
||||
const selectedTools = selectedIds
|
||||
.map((id) => tools.find((tool) => tool.id === id))
|
||||
.filter((tool): tool is Tool => Boolean(tool));
|
||||
|
||||
function openPicker() {
|
||||
setDraftIds(selectedIds);
|
||||
setOpen(true);
|
||||
}
|
||||
|
||||
function toggleResourceTool(toolId: string) {
|
||||
setDraftIds((current) =>
|
||||
current.includes(toolId)
|
||||
? current.filter((id) => id !== toolId)
|
||||
: [...current, toolId],
|
||||
);
|
||||
}
|
||||
|
||||
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 (
|
||||
<>
|
||||
<div className="flex min-h-9 flex-wrap items-center gap-2">
|
||||
{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" ? (
|
||||
<Sparkles size={14} />
|
||||
) : tool.type === "mcp" ? (
|
||||
<ServerCog size={14} />
|
||||
) : (
|
||||
<Wrench size={14} />
|
||||
)}
|
||||
<span className="max-w-48 truncate">{tool.name}</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onChange(selectedIds.filter((id) => id !== tool.id))}
|
||||
className="text-muted-soft transition-colors hover:text-foreground"
|
||||
aria-label={`移除工具 ${tool.name}`}
|
||||
>
|
||||
<X size={13} />
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="icon-sm"
|
||||
className="border-hairline-strong text-muted-foreground hover:text-foreground"
|
||||
onClick={openPicker}
|
||||
aria-label="添加工具"
|
||||
title="添加工具"
|
||||
>
|
||||
<Plus size={15} />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogContent className="sm:max-w-xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle>选择工具</DialogTitle>
|
||||
<DialogDescription>
|
||||
按类型选择已经启用的工具资源。
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<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 resources = tools.filter((tool) => tool.type === tab.value);
|
||||
const isEmpty = resources.length === 0;
|
||||
|
||||
return (
|
||||
<TabsContent key={tab.value} value={tab.value} className="pt-3">
|
||||
{isEmpty ? (
|
||||
<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">
|
||||
{resources.map((tool) => {
|
||||
const checked = draftIds.includes(tool.id);
|
||||
return (
|
||||
<label
|
||||
key={tool.id}
|
||||
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={() => toggleResourceTool(tool.id)}
|
||||
className="size-4 accent-primary"
|
||||
/>
|
||||
<div className="min-w-0 flex-1">
|
||||
<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}
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</TabsContent>
|
||||
);
|
||||
})}
|
||||
</Tabs>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => setOpen(false)}>
|
||||
取消
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
onChange(draftIds);
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
确认
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function ToggleRow({
|
||||
icon,
|
||||
title,
|
||||
|
||||
Reference in New Issue
Block a user