feat: add tools to Multi-Agent Visualization

This commit is contained in:
Li Xin
2025-04-30 09:32:12 +08:00
parent 1e1540a9af
commit 4fd6e0c4e0

View File

@@ -25,7 +25,6 @@ import {
} from "lucide-react"; } from "lucide-react";
import { import {
useCallback, useCallback,
useEffect,
useState, useState,
type Dispatch, type Dispatch,
type SetStateAction, type SetStateAction,
@@ -35,7 +34,8 @@ import "@xyflow/react/dist/style.css";
import { ShineBorder } from "~/components/magicui/shine-border"; import { ShineBorder } from "~/components/magicui/shine-border";
import { Button } from "~/components/ui/button"; import { Button } from "~/components/ui/button";
import { useIntersectionObserver } from "~/hooks/use-intersection-observer"; import { useIntersectionObserver } from "~/hooks/use-intersection-observer";
import { cn } from "~/lib/utils";
import { Tooltip } from "./tooltip";
const ROW_HEIGHT = 75; const ROW_HEIGHT = 75;
const ROW_1 = 0; const ROW_1 = 0;
@@ -261,7 +261,7 @@ const WORKFLOW_STEPS = [
}, },
{ {
description: "The Reporter will prepare a report summarizing the results.", description: "The Reporter will prepare a report summarizing the results.",
activeNodes: ["Reporter", "End"], activeNodes: ["End", "Reporter"],
activeEdges: ["Reporter->End"], activeEdges: ["Reporter->End"],
}, },
]; ];
@@ -298,6 +298,10 @@ function useWorkflowRun(
data: { data: {
...node.data, ...node.data,
active: step.activeNodes.includes(node.id), active: step.activeNodes.includes(node.id),
stepDescription:
step.activeNodes.indexOf(node.id) === step.activeNodes.length - 1
? step.description
: undefined,
}, },
})); }));
}); });
@@ -309,7 +313,7 @@ function useWorkflowRun(
})); }));
}); });
await sleep(1000); await sleep(step.description.split(" ").length * 360);
} }
clearAnimation(); clearAnimation();
setIsRunning(false); setIsRunning(false);
@@ -327,14 +331,14 @@ export function MultiAgentVisualization() {
const [edges, setEdges] = useEdgesState(initialEdges); const [edges, setEdges] = useEdgesState(initialEdges);
const { run, isRunning } = useWorkflowRun(setNodes, setEdges); const { run, isRunning } = useWorkflowRun(setNodes, setEdges);
const [hasAutoRunned, setHasAutoRunned] = useState(false); const [hasAutoRun, setHasAutoRun] = useState(false);
const { ref } = useIntersectionObserver({ const { ref } = useIntersectionObserver({
rootMargin: "0%", rootMargin: "0%",
threshold: 0, threshold: 0,
onChange: (isIntersecting) => { onChange: (isIntersecting) => {
if (isIntersecting && !hasAutoRunned) { if (isIntersecting && !hasAutoRun) {
setHasAutoRunned(true); setHasAutoRun(true);
void run(); void run();
} }
}, },
@@ -360,26 +364,26 @@ export function MultiAgentVisualization() {
className="[mask-image:radial-gradient(800px_circle_at_center,white,transparent)]" className="[mask-image:radial-gradient(800px_circle_at_center,white,transparent)]"
bgColor="var(--background)" bgColor="var(--background)"
/> />
<Button
variant="ghost"
size="icon"
className="absolute top-0 left-0 z-10"
disabled={isRunning}
onClick={() => {
void run();
}}
>
<RotateCcw
className={cn({
"animate-spin": isRunning,
})}
/>
</Button>
<div <div
id="auto-run-animation-trigger" id="auto-run-animation-trigger"
ref={ref} ref={ref}
className="absolute bottom-0 left-[50%] h-px w-px" className="absolute bottom-0 left-[50%] h-px w-px"
/> />
<div className="absolute top-0 right-0 left-0 z-200 flex items-center justify-center">
{!isRunning && (
<Button
className="translate-x-[22vw]"
variant="outline"
size="sm"
onClick={() => {
void run();
}}
>
<RotateCcw />
Replay
</Button>
)}
</div>
</ReactFlow> </ReactFlow>
); );
} }
@@ -420,7 +424,12 @@ function AgentNode({
data, data,
id, id,
}: { }: {
data: { icon?: LucideIcon; label: string; active: boolean }; data: {
icon?: LucideIcon;
label: string;
active: boolean;
stepDescription?: string;
};
id: string; id: string;
}) { }) {
return ( return (
@@ -431,15 +440,22 @@ function AgentNode({
className="rounded-[2px]" className="rounded-[2px]"
/> />
)} )}
<div <Tooltip
id={id} className="max-w-50"
className="relative flex w-full items-center justify-center text-xs" open={data.active && !!data.stepDescription}
title={data.stepDescription}
sideOffset={20}
> >
<div className="flex items-center gap-2"> <div
{data.icon && <data.icon className="h-[1rem] w-[1rem]" />} id={id}
<span>{data.label}</span> className="relative flex w-full items-center justify-center text-xs"
>
<div className="flex items-center gap-2">
{data.icon && <data.icon className="h-[1rem] w-[1rem]" />}
<span>{data.label}</span>
</div>
</div> </div>
</div> </Tooltip>
<Handle <Handle
className="invisible" className="invisible"
type="source" type="source"