UNPKG

@xynehq/jaf

Version:

Juspay Agent Framework - A purely functional agent framework with immutable state and composable tools

449 lines (439 loc) 15.4 kB
/** * JAF Visualization - Graphviz Integration * * Functional visualization system for agents and tools using Graphviz */ import { writeFileSync } from 'fs'; import { execSync } from 'child_process'; // ========== Color Schemes ========== const COLOR_SCHEMES = { default: { agent: { shape: 'box', fillcolor: '#E3F2FD', fontcolor: '#1976D2', style: 'filled,rounded' }, tool: { shape: 'ellipse', fillcolor: '#F3E5F5', fontcolor: '#7B1FA2', style: 'filled' }, subAgent: { shape: 'box', fillcolor: '#E8F5E8', fontcolor: '#388E3C', style: 'filled,dashed' }, edge: { color: '#424242', style: 'solid', penwidth: '1.5' }, toolEdge: { color: '#9C27B0', style: 'dashed', penwidth: '1.0' } }, modern: { agent: { shape: 'box', fillcolor: '#667eea', fontcolor: 'white', style: 'filled,rounded', fontname: 'Arial Bold' }, tool: { shape: 'ellipse', fillcolor: '#f093fb', fontcolor: 'white', style: 'filled', fontname: 'Arial' }, subAgent: { shape: 'box', fillcolor: '#4facfe', fontcolor: 'white', style: 'filled,dashed', fontname: 'Arial' }, edge: { color: '#667eea', style: 'solid', penwidth: '2.0', arrowhead: 'vee' }, toolEdge: { color: '#f093fb', style: 'dashed', penwidth: '1.5', arrowhead: 'open' } }, minimal: { agent: { shape: 'box', fillcolor: 'white', fontcolor: 'black', style: 'filled', penwidth: '2' }, tool: { shape: 'ellipse', fillcolor: '#f5f5f5', fontcolor: 'black', style: 'filled' }, subAgent: { shape: 'box', fillcolor: 'white', fontcolor: 'gray', style: 'filled,dashed' }, edge: { color: 'black', style: 'solid', penwidth: '1.0' }, toolEdge: { color: 'gray', style: 'dashed', penwidth: '1.0' } } }; // ========== Graph Generation Functions ========== export const generateAgentGraph = async (agents, options = {}) => { try { const { title = 'JAF Agent Graph', layout = 'dot', rankdir = 'TB', outputFormat = 'png', outputPath = './agent-graph', showToolDetails = true, showSubAgents = true, colorScheme = 'default' } = options; // Generate DOT content const dotContent = generateAgentsDOT(agents, { title, layout, rankdir, showToolDetails, showSubAgents, colorScheme }); // Write DOT file const dotPath = `${outputPath}.dot`; writeFileSync(dotPath, dotContent); // Try to use system graphviz const finalOutputPath = `${outputPath}.${outputFormat}`; try { execSync(`dot -T${outputFormat} "${dotPath}" -o "${finalOutputPath}"`, { stdio: 'pipe' }); return { success: true, outputPath: finalOutputPath, graphDot: dotContent }; } catch (execError) { return { success: false, error: `Graphviz not installed or failed to execute. Install with: brew install graphviz (macOS) or sudo apt-get install graphviz (Linux)`, graphDot: dotContent }; } } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Unknown error occurred' }; } }; export const generateToolGraph = async (tools, options = {}) => { try { const { title = 'JAF Tool Graph', layout = 'circo', outputFormat = 'png', outputPath = './tool-graph', colorScheme = 'default' } = options; // Generate DOT content const dotContent = generateToolsDOT(tools, { title, layout, colorScheme }); // Write DOT file const dotPath = `${outputPath}.dot`; writeFileSync(dotPath, dotContent); // Try to use system graphviz with circo layout const finalOutputPath = `${outputPath}.${outputFormat}`; try { const layoutEngine = layout === 'circo' ? 'circo' : 'dot'; execSync(`${layoutEngine} -T${outputFormat} "${dotPath}" -o "${finalOutputPath}"`, { stdio: 'pipe' }); return { success: true, outputPath: finalOutputPath, graphDot: dotContent }; } catch (execError) { return { success: false, error: `Graphviz not installed or failed to execute. Install with: brew install graphviz (macOS) or sudo apt-get install graphviz (Linux)`, graphDot: dotContent }; } } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Unknown error occurred' }; } }; export const generateRunnerGraph = async (config, options = {}) => { try { const { title = 'JAF Runner Architecture', layout = 'dot', rankdir = 'TB', outputFormat = 'png', outputPath = './runner-graph', showToolDetails = true, showSubAgents = true, colorScheme = 'modern' } = options; // Generate DOT content const dotContent = generateRunnerDOT(config, { title, rankdir, showToolDetails, showSubAgents, colorScheme }); // Write DOT file const dotPath = `${outputPath}.dot`; writeFileSync(dotPath, dotContent); // Try to use system graphviz const finalOutputPath = `${outputPath}.${outputFormat}`; try { const layoutEngine = layout === 'circo' ? 'circo' : layout === 'neato' ? 'neato' : 'dot'; execSync(`${layoutEngine} -T${outputFormat} "${dotPath}" -o "${finalOutputPath}"`, { stdio: 'pipe' }); return { success: true, outputPath: finalOutputPath, graphDot: dotContent }; } catch (execError) { return { success: false, error: `Graphviz not installed or failed to execute. Install with: brew install graphviz (macOS) or sudo apt-get install graphviz (Linux)`, graphDot: dotContent }; } } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Unknown error occurred' }; } }; // Generate DOT content for multiple agents const generateAgentsDOT = (agents, options) => { const { title, layout, rankdir, showToolDetails, showSubAgents, colorScheme } = options; const styles = COLOR_SCHEMES[colorScheme]; let dot = `digraph "AgentGraph" { rankdir=${rankdir}; layout=${layout}; label="${title}"; labelloc=t; fontsize=16; fontname="Arial Bold"; bgcolor=white; pad=0.5; compound=true; `; // Add nodes for each agent for (const agent of agents) { dot += generateAgentNodeDOT(agent, styles, showToolDetails); // Add sub-agents if requested if (showSubAgents && agent.config.subAgents) { for (const subAgent of agent.config.subAgents) { const subAgentId = `${agent.id}_sub_${subAgent.name}`; dot += ` // Sub-agent: ${subAgent.name} "${subAgentId}" [ label="${subAgent.name}", shape=${styles.subAgent.shape}, fillcolor="${styles.subAgent.fillcolor}", fontcolor="${styles.subAgent.fontcolor}", style="${styles.subAgent.style}" ]; "${agent.id}" -> "${subAgentId}" [ color="${styles.edge.color}", style=dashed, label="delegates" ]; `; } } } dot += '\n}'; return dot; }; // Generate DOT content for tools const generateToolsDOT = (tools, options) => { const { title, layout, colorScheme } = options; const styles = COLOR_SCHEMES[colorScheme]; let dot = `digraph "ToolGraph" { layout=${layout}; label="${title}"; labelloc=t; fontsize=16; fontname="Arial Bold"; bgcolor=white; pad=0.5; `; // Add tool nodes for (const tool of tools) { const toolLabel = `${tool.name}\\n${tool.description.substring(0, 30)}${tool.description.length > 30 ? '...' : ''}`; dot += ` // Tool: ${tool.name} "${tool.name}" [ label="${toolLabel}", shape=${styles.tool.shape}, fillcolor="${styles.tool.fillcolor}", fontcolor="${styles.tool.fontcolor}", style="${styles.tool.style}" ]; `; } dot += '\n}'; return dot; }; // Generate DOT for a single agent node const generateAgentNodeDOT = (agent, styles, showToolDetails) => { let label = `${agent.config.name}\\n(${agent.config.model})`; if (showToolDetails && agent.config.tools.length > 0) { label += `\\n${agent.config.tools.length} tools`; } if (agent.config.subAgents && agent.config.subAgents.length > 0) { label += `\\n${agent.config.subAgents.length} sub-agents`; } let dot = ` // Agent: ${agent.config.name} "${agent.id}" [ label="${label}", shape=${styles.agent.shape}, fillcolor="${styles.agent.fillcolor}", fontcolor="${styles.agent.fontcolor}", style="${styles.agent.style}" ]; `; // Add tool nodes and edges if requested if (showToolDetails) { for (const tool of agent.config.tools) { const toolLabel = `${tool.name}\\n${tool.description.substring(0, 30)}${tool.description.length > 30 ? '...' : ''}`; dot += ` // Tool: ${tool.name} "${tool.name}" [ label="${toolLabel}", shape=${styles.tool.shape}, fillcolor="${styles.tool.fillcolor}", fontcolor="${styles.tool.fontcolor}", style="${styles.tool.style}" ]; "${agent.id}" -> "${tool.name}" [ color="${styles.toolEdge.color}", style="${styles.toolEdge.style}", penwidth="${styles.toolEdge.penwidth || '1.0'}" ]; `; } } return dot; }; // Manual DOT generation for runner const generateRunnerDOT = (config, options) => { const { title, rankdir, showToolDetails, showSubAgents, colorScheme } = options; const styles = COLOR_SCHEMES[colorScheme]; let dot = `digraph "RunnerGraph" { rankdir=${rankdir}; label="${title}"; labelloc=t; fontsize=16; fontname="Arial Bold"; bgcolor=white; pad=0.5; compound=true; // Runner node "runner" [ label="Runner", shape=diamond, fillcolor="#28a745", fontcolor=white, style=filled, fontsize=14, fontname="Arial Bold" ]; // Session provider node "session_provider" [ label="Session\\nProvider", shape=box, fillcolor="#ffc107", fontcolor=black, style="filled,rounded" ]; // Agent node "${config.agent.id}" [ label="${config.agent.config.name}\\n(${config.agent.config.model})\\n${config.agent.config.tools.length} tools", shape=box, fillcolor="${styles.agent.fillcolor}", fontcolor="${styles.agent.fontcolor}", style="${styles.agent.style}" ]; // Runner connections "runner" -> "${config.agent.id}" [ color="${styles.edge.color}", style="${styles.edge.style}", label="executes" ]; "runner" -> "session_provider" [ color="#ffc107", style=dashed, label="manages" ]; `; // Add tool nodes and connections if requested if (showToolDetails) { for (const tool of config.agent.config.tools) { dot += ` // Tool: ${tool.name} "${tool.name}" [ label="${tool.name}\\n${tool.description.substring(0, 30)}${tool.description.length > 30 ? '...' : ''}", shape=ellipse, fillcolor="${styles.tool.fillcolor}", fontcolor="${styles.tool.fontcolor}", style="${styles.tool.style}" ]; "${config.agent.id}" -> "${tool.name}" [ color="${styles.toolEdge.color}", style="${styles.toolEdge.style}", penwidth="${styles.toolEdge.penwidth}" ]; `; } } // Add sub-agents if requested if (showSubAgents && config.agent.config.subAgents) { for (const subAgent of config.agent.config.subAgents) { const subAgentId = `${config.agent.id}_sub_${subAgent.name}`; dot += ` // Sub-agent: ${subAgent.name} "${subAgentId}" [ label="${subAgent.name}", shape=box, fillcolor="${styles.subAgent.fillcolor}", fontcolor="${styles.subAgent.fontcolor}", style="${styles.subAgent.style}" ]; "${config.agent.id}" -> "${subAgentId}" [ color="${styles.edge.color}", style=dashed, label="delegates" ]; `; } } dot += ` // Clusters for organization subgraph cluster_agents { label="Agents"; style=filled; fillcolor="#f8f9fa"; "${config.agent.id}"; } subgraph cluster_session { label="Session Layer"; style=filled; fillcolor="#fff3cd"; "session_provider"; } }`; return dot; }; // ========== Helper Functions ========== // These functions are currently unused but kept for potential future use // when we re-implement the graphviz npm package integration // const createAgentLabel = (agent: Agent, showToolDetails: boolean): string => { // let label = `${agent.config.name}\\n(${agent.config.model})`; // // if (showToolDetails && agent.config.tools.length > 0) { // label += `\\n${agent.config.tools.length} tools`; // } // // if (agent.config.subAgents && agent.config.subAgents.length > 0) { // label += `\\n${agent.config.subAgents.length} sub-agents`; // } // // return label; // }; // const createToolLabel = (tool: Tool): string => { // return `${tool.name}\\n${tool.description.substring(0, 30)}${tool.description.length > 30 ? '...' : ''}`; // }; // ========== Validation Functions ========== export const validateGraphOptions = (options) => { const errors = []; if (options.layout && !['dot', 'neato', 'fdp', 'circo', 'twopi'].includes(options.layout)) { errors.push('Invalid layout option'); } if (options.rankdir && !['TB', 'LR', 'BT', 'RL'].includes(options.rankdir)) { errors.push('Invalid rankdir option'); } if (options.outputFormat && !['png', 'svg', 'pdf'].includes(options.outputFormat)) { errors.push('Invalid output format'); } if (options.colorScheme && !['default', 'modern', 'minimal'].includes(options.colorScheme)) { errors.push('Invalid color scheme'); } return errors; }; // ========== Utility Functions ========== export const getGraphDot = (agents, options = {}) => { const { title = 'JAF Agent Graph', rankdir = 'TB', layout = 'dot', colorScheme = 'default' } = options; return generateAgentsDOT(agents, { title, layout, rankdir, showToolDetails: true, showSubAgents: true, colorScheme }); }; // Check if graphviz is installed export const isGraphvizInstalled = () => { try { execSync('dot -V', { stdio: 'pipe' }); return true; } catch { return false; } }; //# sourceMappingURL=graphviz.js.map