mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-05-31 19:15:17 +02:00
add visualiser for agents
This commit is contained in:
parent
6895e54425
commit
d19e84815d
5 changed files with 1582 additions and 34 deletions
|
|
@ -0,0 +1,184 @@
|
||||||
|
import React, { useEffect, useRef } from "react";
|
||||||
|
import mermaid from "mermaid";
|
||||||
|
import { Workflow } from "../../../lib/types/workflow_types";
|
||||||
|
|
||||||
|
function sanitizeId(name: string): string {
|
||||||
|
return name.replace(/[^a-zA-Z0-9\s_-]/g, "").replace(/[\s-]+/g, "_");
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateMermaidFromWorkflow(workflow: any, isDark: boolean): string {
|
||||||
|
const startAgentName = workflow.startAgent;
|
||||||
|
const agents: any[] = workflow.agents || [];
|
||||||
|
const tools: any[] = workflow.tools || [];
|
||||||
|
|
||||||
|
// Light and dark mode colors
|
||||||
|
const toolFillLight = '#ede9fe';
|
||||||
|
const toolStrokeLight = '#a78bfa';
|
||||||
|
const toolFillDark = '#312e81';
|
||||||
|
const toolStrokeDark = '#a78bfa';
|
||||||
|
const agentFillLight = '#EBF5FB';
|
||||||
|
const agentStrokeLight = '#85C1E9';
|
||||||
|
const agentFillDark = '#1e293b';
|
||||||
|
const agentStrokeDark = '#a78bfa';
|
||||||
|
const startFillLight = '#FEF9E7';
|
||||||
|
const startStrokeLight = '#F8C471';
|
||||||
|
const startFillDark = '#92400e';
|
||||||
|
const startStrokeDark = '#f59e0b';
|
||||||
|
const entryFillLight = '#22C55E';
|
||||||
|
const entryStrokeLight = '#16A34A';
|
||||||
|
const entryFillDark = '#22c55e';
|
||||||
|
const entryStrokeDark = '#4ade80';
|
||||||
|
const textLight = '#34495E';
|
||||||
|
const textDark = '#fff';
|
||||||
|
|
||||||
|
const mermaidCode = [
|
||||||
|
"graph LR",
|
||||||
|
// Agent node style
|
||||||
|
` classDef agent fill:${isDark ? agentFillDark : agentFillLight},stroke:${isDark ? agentStrokeDark : agentStrokeLight},stroke-width:3px,color:${isDark ? textDark : textLight},font-size:16px,radius:12px`,
|
||||||
|
// Tool node style
|
||||||
|
` classDef tool fill:${isDark ? toolFillDark : toolFillLight},stroke:${toolStrokeLight},stroke-width:3px,color:${isDark ? textDark : textLight},font-size:16px,radius:12px`,
|
||||||
|
// Start agent node style
|
||||||
|
` classDef startAgent fill:${isDark ? startFillDark : startFillLight},stroke:${isDark ? startStrokeDark : startStrokeLight},stroke-width:3px,color:${isDark ? textDark : textLight},font-size:18px,radius:12px`,
|
||||||
|
// Entry node style
|
||||||
|
` classDef entry fill:${isDark ? entryFillDark : entryFillLight},stroke:${isDark ? entryStrokeDark : entryStrokeLight},stroke-width:3px,color:${isDark ? textDark : '#fff'},font-size:16px,radius:12px`
|
||||||
|
];
|
||||||
|
|
||||||
|
if (startAgentName) {
|
||||||
|
const startAgentId = sanitizeId(startAgentName);
|
||||||
|
mermaidCode.push(`\n %% -- Entry Point --`);
|
||||||
|
mermaidCode.push(` Entry([Start]) --> ${startAgentId}`);
|
||||||
|
mermaidCode.push(` class Entry entry`);
|
||||||
|
}
|
||||||
|
|
||||||
|
mermaidCode.push(`\n %% -- Agent Nodes --`);
|
||||||
|
for (const agent of agents) {
|
||||||
|
const agentName = agent.name;
|
||||||
|
const agentId = sanitizeId(agentName);
|
||||||
|
const nodeLabel = `🤖 ${agentName}`;
|
||||||
|
mermaidCode.push(` ${agentId}([\"${nodeLabel}\"])`);
|
||||||
|
if (agentName === startAgentName) {
|
||||||
|
mermaidCode.push(` class ${agentId} startAgent`);
|
||||||
|
} else {
|
||||||
|
mermaidCode.push(` class ${agentId} agent`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Tool Nodes ---
|
||||||
|
// 1. Collect all tool names from workflow.tools
|
||||||
|
const toolNamesFromArray = new Set(tools.map((tool: any) => tool.name));
|
||||||
|
// 2. Collect all tool names mentioned in agent instructions
|
||||||
|
const agentMentionPattern = /\[@agent:([^\]]+)\]\(#mention[^\)]*\)/g;
|
||||||
|
const toolMentionPattern = /\[@tool:([^\]]+)\]\(#mention[^\)]*\)/g;
|
||||||
|
const toolNamesFromMentions = new Set<string>();
|
||||||
|
for (const agent of agents) {
|
||||||
|
const instructions = agent.instructions || "";
|
||||||
|
let match: RegExpExecArray | null;
|
||||||
|
while ((match = toolMentionPattern.exec(instructions))) {
|
||||||
|
toolNamesFromMentions.add(match[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 3. Union of all tool names
|
||||||
|
const allToolNames = new Set([...toolNamesFromArray, ...toolNamesFromMentions]);
|
||||||
|
// 4. Generate tool nodes for all
|
||||||
|
mermaidCode.push(`\n %% -- Tool Nodes --`);
|
||||||
|
for (const toolName of allToolNames) {
|
||||||
|
const toolId = sanitizeId(toolName);
|
||||||
|
mermaidCode.push(` ${toolId}([\"🛠️ ${toolName}\"])`);
|
||||||
|
mermaidCode.push(` class ${toolId} tool`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Connections ---
|
||||||
|
mermaidCode.push(`\n %% -- Connections --`);
|
||||||
|
for (const agent of agents) {
|
||||||
|
const currentAgentId = sanitizeId(agent.name);
|
||||||
|
const instructions = agent.instructions || "";
|
||||||
|
|
||||||
|
const calledAgents = new Set<string>();
|
||||||
|
let match: RegExpExecArray | null;
|
||||||
|
while ((match = agentMentionPattern.exec(instructions))) {
|
||||||
|
calledAgents.add(match[1]);
|
||||||
|
}
|
||||||
|
for (const calledAgent of Array.from(calledAgents)) {
|
||||||
|
const calledAgentId = sanitizeId(calledAgent);
|
||||||
|
mermaidCode.push(` ${currentAgentId} -- \"delegates to\" --> ${calledAgentId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const calledTools = new Set<string>();
|
||||||
|
while ((match = toolMentionPattern.exec(instructions))) {
|
||||||
|
calledTools.add(match[1]);
|
||||||
|
}
|
||||||
|
for (const calledTool of Array.from(calledTools)) {
|
||||||
|
const calledToolId = sanitizeId(calledTool);
|
||||||
|
mermaidCode.push(` ${currentAgentId} -- \"uses\" --> ${calledToolId}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mermaidCode.join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCssVarValue(varName: string, fallback: string) {
|
||||||
|
if (typeof window === 'undefined') return fallback;
|
||||||
|
let value = getComputedStyle(document.documentElement).getPropertyValue(varName).trim();
|
||||||
|
// If the value looks like HSL (e.g. '0 0% 9%' or '0 0% 3.9%' or '0 0% 9% / 1'), wrap it in hsl()
|
||||||
|
if (/^[\d.]+\s+[\d.]+%\s+[\d.]+%(\s*\/\s*[\d.]+)?$/.test(value)) {
|
||||||
|
value = `hsl(${value})`;
|
||||||
|
}
|
||||||
|
return value || fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AgentGraphVisualizer = ({ workflow }: { workflow: any }) => {
|
||||||
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (ref.current && workflow) {
|
||||||
|
// Only check theme on mount/render
|
||||||
|
const isDark = document.documentElement.classList.contains('dark');
|
||||||
|
mermaid.initialize({
|
||||||
|
startOnLoad: true,
|
||||||
|
theme: isDark ? 'dark' : 'default',
|
||||||
|
themeVariables: {
|
||||||
|
background: getCssVarValue('--background', isDark ? '#18181b' : '#fff'),
|
||||||
|
primaryColor: isDark ? '#a78bfa' : getCssVarValue('--primary', '#4f46e5'),
|
||||||
|
primaryTextColor: isDark ? '#fff' : getCssVarValue('--foreground', '#18181b'),
|
||||||
|
fontSize: '20px',
|
||||||
|
nodeTextColor: isDark ? '#fff' : getCssVarValue('--foreground', '#18181b'),
|
||||||
|
edgeLabelBackground: isDark ? 'transparent' : getCssVarValue('--background', '#fff'),
|
||||||
|
clusterBkg: getCssVarValue('--background', isDark ? '#18181b' : '#fff'),
|
||||||
|
clusterBorder: isDark ? '#a78bfa' : getCssVarValue('--border', '#e5e7eb'),
|
||||||
|
lineColor: isDark ? '#a78bfa' : '#6366f1',
|
||||||
|
arrowheadColor: isDark ? '#a78bfa' : '#6366f1',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
ref.current.innerHTML = generateMermaidFromWorkflow(workflow, isDark);
|
||||||
|
ref.current.className = "mermaid";
|
||||||
|
mermaid.init(undefined, ref.current);
|
||||||
|
}
|
||||||
|
}, [workflow]);
|
||||||
|
|
||||||
|
// Center the graph vertically and horizontally
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
minHeight: 0,
|
||||||
|
background: "var(--background)",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "flex-start",
|
||||||
|
justifyContent: "center",
|
||||||
|
overflow: "auto",
|
||||||
|
padding: "16px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
style={{
|
||||||
|
width: "100%",
|
||||||
|
height: "fit-content",
|
||||||
|
minHeight: 0,
|
||||||
|
fontSize: 20,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -2,7 +2,7 @@ import { z } from "zod";
|
||||||
import { WorkflowPrompt, WorkflowAgent, WorkflowTool } from "../../../lib/types/workflow_types";
|
import { WorkflowPrompt, WorkflowAgent, WorkflowTool } from "../../../lib/types/workflow_types";
|
||||||
import { Dropdown, DropdownItem, DropdownTrigger, DropdownMenu } from "@heroui/react";
|
import { Dropdown, DropdownItem, DropdownTrigger, DropdownMenu } from "@heroui/react";
|
||||||
import { useRef, useEffect, useState } from "react";
|
import { useRef, useEffect, useState } from "react";
|
||||||
import { EllipsisVerticalIcon, ImportIcon, PlusIcon, Brain, Boxes, Wrench, PenLine, Library, ChevronDown, ChevronRight, ServerIcon, Component, ScrollText, GripVertical, Users, Cog, CheckCircle2 } from "lucide-react";
|
import { EllipsisVerticalIcon, ImportIcon, PlusIcon, Brain, Boxes, Wrench, PenLine, Library, ChevronDown, ChevronRight, ServerIcon, Component, ScrollText, GripVertical, Users, Cog, CheckCircle2, Eye } from "lucide-react";
|
||||||
import { DndContext, DragEndEvent, closestCenter, KeyboardSensor, PointerSensor, useSensor, useSensors } from '@dnd-kit/core';
|
import { DndContext, DragEndEvent, closestCenter, KeyboardSensor, PointerSensor, useSensor, useSensors } from '@dnd-kit/core';
|
||||||
import { SortableContext, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy } from '@dnd-kit/sortable';
|
import { SortableContext, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy } from '@dnd-kit/sortable';
|
||||||
import { CSS } from '@dnd-kit/utilities';
|
import { CSS } from '@dnd-kit/utilities';
|
||||||
|
|
@ -36,7 +36,7 @@ interface EntityListProps {
|
||||||
projectTools: z.infer<typeof WorkflowTool>[];
|
projectTools: z.infer<typeof WorkflowTool>[];
|
||||||
prompts: z.infer<typeof WorkflowPrompt>[];
|
prompts: z.infer<typeof WorkflowPrompt>[];
|
||||||
selectedEntity: {
|
selectedEntity: {
|
||||||
type: "agent" | "tool" | "prompt";
|
type: "agent" | "tool" | "prompt" | "visualise";
|
||||||
name: string;
|
name: string;
|
||||||
} | null;
|
} | null;
|
||||||
startAgentName: string | null;
|
startAgentName: string | null;
|
||||||
|
|
@ -51,6 +51,7 @@ interface EntityListProps {
|
||||||
onDeleteAgent: (name: string) => void;
|
onDeleteAgent: (name: string) => void;
|
||||||
onDeleteTool: (name: string) => void;
|
onDeleteTool: (name: string) => void;
|
||||||
onDeletePrompt: (name: string) => void;
|
onDeletePrompt: (name: string) => void;
|
||||||
|
onShowVisualise: (name: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EmptyStateProps {
|
interface EmptyStateProps {
|
||||||
|
|
@ -99,7 +100,7 @@ const ListItemWithMenu = ({
|
||||||
)}>
|
)}>
|
||||||
{dragHandle}
|
{dragHandle}
|
||||||
<button
|
<button
|
||||||
ref={selectedRef}
|
ref={selectedRef as React.RefObject<HTMLButtonElement>}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
"flex-1 flex items-center gap-2 text-sm text-left",
|
"flex-1 flex items-center gap-2 text-sm text-left",
|
||||||
{
|
{
|
||||||
|
|
@ -143,7 +144,7 @@ interface ServerCardProps {
|
||||||
serverName: string;
|
serverName: string;
|
||||||
tools: z.infer<typeof WorkflowTool>[];
|
tools: z.infer<typeof WorkflowTool>[];
|
||||||
selectedEntity: {
|
selectedEntity: {
|
||||||
type: "agent" | "tool" | "prompt";
|
type: "agent" | "tool" | "prompt" | "visualise";
|
||||||
name: string;
|
name: string;
|
||||||
} | null;
|
} | null;
|
||||||
onSelectTool: (name: string) => void;
|
onSelectTool: (name: string) => void;
|
||||||
|
|
@ -233,6 +234,7 @@ export function EntityList({
|
||||||
onDeletePrompt,
|
onDeletePrompt,
|
||||||
projectId,
|
projectId,
|
||||||
onReorderAgents,
|
onReorderAgents,
|
||||||
|
onShowVisualise,
|
||||||
}: EntityListProps & {
|
}: EntityListProps & {
|
||||||
projectId: string,
|
projectId: string,
|
||||||
onReorderAgents: (agents: z.infer<typeof WorkflowAgent>[]) => void
|
onReorderAgents: (agents: z.infer<typeof WorkflowAgent>[]) => void
|
||||||
|
|
@ -246,7 +248,7 @@ export function EntityList({
|
||||||
};
|
};
|
||||||
// Merge workflow tools with project tools
|
// Merge workflow tools with project tools
|
||||||
const mergedTools = [...tools, ...projectTools];
|
const mergedTools = [...tools, ...projectTools];
|
||||||
const selectedRef = useRef<HTMLButtonElement | null>(null);
|
const selectedRef = useRef<HTMLButtonElement>(null);
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
const [containerHeight, setContainerHeight] = useState<number>(0);
|
const [containerHeight, setContainerHeight] = useState<number>(0);
|
||||||
|
|
||||||
|
|
@ -390,20 +392,35 @@ export function EntityList({
|
||||||
<Brain className="w-4 h-4" />
|
<Brain className="w-4 h-4" />
|
||||||
<span>Agents</span>
|
<span>Agents</span>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<div className="flex items-center gap-1">
|
||||||
variant="secondary"
|
<Button
|
||||||
size="sm"
|
variant="secondary"
|
||||||
onClick={(e) => {
|
size="sm"
|
||||||
e.stopPropagation();
|
onClick={(e) => {
|
||||||
setExpandedPanels(prev => ({ ...prev, agents: true }));
|
e.stopPropagation();
|
||||||
setShowAgentTypeModal(true);
|
onShowVisualise("visualise");
|
||||||
}}
|
}}
|
||||||
className={`group ${buttonClasses}`}
|
className={`group ${buttonClasses}`}
|
||||||
showHoverContent={true}
|
showHoverContent={true}
|
||||||
hoverContent="Add Agent"
|
hoverContent="Visualise Agents"
|
||||||
>
|
>
|
||||||
<PlusIcon className="w-4 h-4" />
|
<Eye className="w-4 h-4" />
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
size="sm"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setExpandedPanels(prev => ({ ...prev, agents: true }));
|
||||||
|
setShowAgentTypeModal(true);
|
||||||
|
}}
|
||||||
|
className={`group ${buttonClasses}`}
|
||||||
|
showHoverContent={true}
|
||||||
|
hoverContent="Add Agent"
|
||||||
|
>
|
||||||
|
<PlusIcon className="w-4 h-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|
@ -743,7 +760,7 @@ function EntityDropdown({
|
||||||
interface ComposioCardProps {
|
interface ComposioCardProps {
|
||||||
card: ComposioToolkit;
|
card: ComposioToolkit;
|
||||||
selectedEntity: {
|
selectedEntity: {
|
||||||
type: "agent" | "tool" | "prompt";
|
type: "agent" | "tool" | "prompt" | "visualise";
|
||||||
name: string;
|
name: string;
|
||||||
} | null;
|
} | null;
|
||||||
onSelectTool: (name: string) => void;
|
onSelectTool: (name: string) => void;
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,13 @@ import { Copilot } from "../copilot/app";
|
||||||
import { publishWorkflow, renameWorkflow, saveWorkflow } from "../../../actions/workflow_actions";
|
import { publishWorkflow, renameWorkflow, saveWorkflow } from "../../../actions/workflow_actions";
|
||||||
import { PublishedBadge } from "./published_badge";
|
import { PublishedBadge } from "./published_badge";
|
||||||
import { BackIcon, HamburgerIcon, WorkflowIcon } from "../../../lib/components/icons";
|
import { BackIcon, HamburgerIcon, WorkflowIcon } from "../../../lib/components/icons";
|
||||||
import { CopyIcon, ImportIcon, Layers2Icon, RadioIcon, RedoIcon, ServerIcon, Sparkles, UndoIcon, RocketIcon, PenLine, AlertTriangle, DownloadIcon } from "lucide-react";
|
import { CopyIcon, ImportIcon, Layers2Icon, RadioIcon, RedoIcon, ServerIcon, Sparkles, UndoIcon, RocketIcon, PenLine, AlertTriangle, DownloadIcon, XIcon } from "lucide-react";
|
||||||
import { EntityList } from "./entity_list";
|
import { EntityList } from "./entity_list";
|
||||||
import { ProductTour } from "@/components/common/product-tour";
|
import { ProductTour } from "@/components/common/product-tour";
|
||||||
import { ModelsResponse } from "@/app/lib/types/billing_types";
|
import { ModelsResponse } from "@/app/lib/types/billing_types";
|
||||||
|
import { AgentGraphVisualizer } from "../entities/AgentGraphVisualizer";
|
||||||
|
import { Panel } from "@/components/common/panel-common";
|
||||||
|
import { Button as CustomButton } from "@/components/ui/button";
|
||||||
|
|
||||||
enablePatches();
|
enablePatches();
|
||||||
|
|
||||||
|
|
@ -41,7 +44,7 @@ interface StateItem {
|
||||||
publishedWorkflowId: string | null;
|
publishedWorkflowId: string | null;
|
||||||
publishing: boolean;
|
publishing: boolean;
|
||||||
selection: {
|
selection: {
|
||||||
type: "agent" | "tool" | "prompt";
|
type: "agent" | "tool" | "prompt" | "visualise";
|
||||||
name: string;
|
name: string;
|
||||||
} | null;
|
} | null;
|
||||||
saving: boolean;
|
saving: boolean;
|
||||||
|
|
@ -138,6 +141,10 @@ export type Action = {
|
||||||
} | {
|
} | {
|
||||||
type: "reorder_agents";
|
type: "reorder_agents";
|
||||||
agents: z.infer<typeof WorkflowAgent>[];
|
agents: z.infer<typeof WorkflowAgent>[];
|
||||||
|
} | {
|
||||||
|
type: "show_visualise";
|
||||||
|
} | {
|
||||||
|
type: "hide_visualise";
|
||||||
};
|
};
|
||||||
|
|
||||||
function reducer(state: State, action: Action): State {
|
function reducer(state: State, action: Action): State {
|
||||||
|
|
@ -232,6 +239,18 @@ function reducer(state: State, action: Action): State {
|
||||||
currentIndex: state.currentIndex + 1,
|
currentIndex: state.currentIndex + 1,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
case "show_visualise": {
|
||||||
|
newState = produce(state, draft => {
|
||||||
|
draft.present.selection = { type: "visualise", name: "visualise" };
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "hide_visualise": {
|
||||||
|
newState = produce(state, draft => {
|
||||||
|
draft.present.selection = null;
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
const [nextState, patches, inversePatches] = produceWithPatches(
|
const [nextState, patches, inversePatches] = produceWithPatches(
|
||||||
state.present,
|
state.present,
|
||||||
|
|
@ -700,6 +719,14 @@ export function WorkflowEditor({
|
||||||
function handleUnselectPrompt() {
|
function handleUnselectPrompt() {
|
||||||
dispatch({ type: "unselect_prompt" });
|
dispatch({ type: "unselect_prompt" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleShowVisualise() {
|
||||||
|
dispatch({ type: "show_visualise" });
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleHideVisualise() {
|
||||||
|
dispatch({ type: "hide_visualise" });
|
||||||
|
}
|
||||||
|
|
||||||
function handleAddAgent(agent: Partial<z.infer<typeof WorkflowAgent>> = {}) {
|
function handleAddAgent(agent: Partial<z.infer<typeof WorkflowAgent>> = {}) {
|
||||||
const agentWithModel = {
|
const agentWithModel = {
|
||||||
|
|
@ -993,7 +1020,14 @@ export function WorkflowEditor({
|
||||||
tools={state.present.workflow.tools}
|
tools={state.present.workflow.tools}
|
||||||
projectTools={projectTools}
|
projectTools={projectTools}
|
||||||
prompts={state.present.workflow.prompts}
|
prompts={state.present.workflow.prompts}
|
||||||
selectedEntity={state.present.selection}
|
selectedEntity={
|
||||||
|
state.present.selection &&
|
||||||
|
(state.present.selection.type === "agent" ||
|
||||||
|
state.present.selection.type === "tool" ||
|
||||||
|
state.present.selection.type === "prompt")
|
||||||
|
? state.present.selection
|
||||||
|
: null
|
||||||
|
}
|
||||||
startAgentName={state.present.workflow.startAgent}
|
startAgentName={state.present.workflow.startAgent}
|
||||||
onSelectAgent={handleSelectAgent}
|
onSelectAgent={handleSelectAgent}
|
||||||
onSelectTool={handleSelectTool}
|
onSelectTool={handleSelectTool}
|
||||||
|
|
@ -1006,6 +1040,7 @@ export function WorkflowEditor({
|
||||||
onDeleteAgent={handleDeleteAgent}
|
onDeleteAgent={handleDeleteAgent}
|
||||||
onDeleteTool={handleDeleteTool}
|
onDeleteTool={handleDeleteTool}
|
||||||
onDeletePrompt={handleDeletePrompt}
|
onDeletePrompt={handleDeletePrompt}
|
||||||
|
onShowVisualise={handleShowVisualise}
|
||||||
projectId={state.present.workflow.projectId}
|
projectId={state.present.workflow.projectId}
|
||||||
onReorderAgents={handleReorderAgents}
|
onReorderAgents={handleReorderAgents}
|
||||||
/>
|
/>
|
||||||
|
|
@ -1074,6 +1109,30 @@ export function WorkflowEditor({
|
||||||
handleUpdate={handleUpdatePrompt.bind(null, state.present.selection.name)}
|
handleUpdate={handleUpdatePrompt.bind(null, state.present.selection.name)}
|
||||||
handleClose={handleUnselectPrompt}
|
handleClose={handleUnselectPrompt}
|
||||||
/>}
|
/>}
|
||||||
|
{state.present.selection?.type === "visualise" && (
|
||||||
|
<Panel
|
||||||
|
title={
|
||||||
|
<div className="flex items-center justify-between w-full">
|
||||||
|
<div className="text-base font-semibold text-gray-900 dark:text-gray-100">
|
||||||
|
Agent Graph Visualizer
|
||||||
|
</div>
|
||||||
|
<CustomButton
|
||||||
|
variant="secondary"
|
||||||
|
size="sm"
|
||||||
|
onClick={handleHideVisualise}
|
||||||
|
showHoverContent={true}
|
||||||
|
hoverContent="Close"
|
||||||
|
>
|
||||||
|
<XIcon className="w-4 h-4" />
|
||||||
|
</CustomButton>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="h-full overflow-hidden">
|
||||||
|
<AgentGraphVisualizer workflow={state.present.workflow} />
|
||||||
|
</div>
|
||||||
|
</Panel>
|
||||||
|
)}
|
||||||
</ResizablePanel>
|
</ResizablePanel>
|
||||||
{showCopilot && (
|
{showCopilot && (
|
||||||
<>
|
<>
|
||||||
|
|
@ -1089,13 +1148,17 @@ export function WorkflowEditor({
|
||||||
workflow={state.present.workflow}
|
workflow={state.present.workflow}
|
||||||
dispatch={dispatch}
|
dispatch={dispatch}
|
||||||
chatContext={
|
chatContext={
|
||||||
state.present.selection ? {
|
state.present.selection &&
|
||||||
type: state.present.selection.type,
|
(state.present.selection.type === "agent" ||
|
||||||
name: state.present.selection.name
|
state.present.selection.type === "tool" ||
|
||||||
} : chatMessages.length > 0 ? {
|
state.present.selection.type === "prompt")
|
||||||
type: 'chat',
|
? {
|
||||||
messages: chatMessages
|
type: state.present.selection.type,
|
||||||
} : undefined
|
name: state.present.selection.name
|
||||||
|
}
|
||||||
|
: chatMessages.length > 0
|
||||||
|
? { type: 'chat', messages: chatMessages }
|
||||||
|
: undefined
|
||||||
}
|
}
|
||||||
isInitialState={isInitialState}
|
isInitialState={isInitialState}
|
||||||
dataSources={dataSources}
|
dataSources={dataSources}
|
||||||
|
|
|
||||||
1291
apps/rowboat/package-lock.json
generated
1291
apps/rowboat/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -50,6 +50,7 @@
|
||||||
"ioredis": "^5.6.1",
|
"ioredis": "^5.6.1",
|
||||||
"jose": "^5.9.6",
|
"jose": "^5.9.6",
|
||||||
"lucide-react": "^0.465.0",
|
"lucide-react": "^0.465.0",
|
||||||
|
"mermaid": "^11.8.1",
|
||||||
"mongodb": "^6.8.0",
|
"mongodb": "^6.8.0",
|
||||||
"next": "15.3.4",
|
"next": "15.3.4",
|
||||||
"openai": "^4.67.2",
|
"openai": "^4.67.2",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue