buildGraph function
Builds an AgentGraph from rootAgent and its reachable children/tools.
Implementation
Future<AgentGraph> buildGraph(BaseAgent rootAgent) async {
final Map<String, AgentGraphNode> nodes = <String, AgentGraphNode>{};
final Set<(String, String)> edges = <(String, String)>{};
final Map<(String, String), String> edgeLabels = <(String, String), String>{};
void addEdge(String from, String to, {Object? label}) {
final (String, String) edge = (from, to);
edges.add(edge);
if (label != null) {
edgeLabels[edge] = '$label';
}
}
late Future<void> Function(BaseAgent agent) visitAgent;
late Future<void> Function(Workflow workflow) visitWorkflow;
visitAgent = (BaseAgent agent) async {
if (agent is Workflow) {
await visitWorkflow(agent);
return;
}
nodes.putIfAbsent(
agent.name,
() => AgentGraphNode(
id: agent.name,
caption: '🤖 ${agent.name}',
kind: 'agent',
),
);
for (final BaseAgent subAgent in agent.subAgents) {
await visitAgent(subAgent);
addEdge(agent.name, subAgent.name);
}
if (agent is LlmAgent) {
final List<BaseTool> tools = await agent.canonicalTools();
for (final BaseTool tool in tools) {
final String toolNodeId = 'tool:${tool.name}';
final String caption = tool is FunctionTool
? '🔧 ${tool.name}'
: '🧰 ${tool.name}';
nodes.putIfAbsent(
toolNodeId,
() => AgentGraphNode(id: toolNodeId, caption: caption, kind: 'tool'),
);
addEdge(agent.name, toolNodeId);
}
}
};
visitWorkflow = (Workflow workflow) async {
nodes.putIfAbsent(
workflow.name,
() => AgentGraphNode(
id: workflow.name,
caption: '🤖 ${workflow.name}',
kind: 'workflow',
),
);
final Set<String> workflowNodeNames = <String>{
for (final BaseNode node in workflow.nodes) node.name,
};
final Set<String> targetedNodes = <String>{};
for (final BaseNode node in workflow.nodes) {
final String nodeId = _workflowNodeId(workflow, node.name);
nodes.putIfAbsent(
nodeId,
() => AgentGraphNode(
id: nodeId,
caption: _workflowNodeCaption(node),
kind: _workflowNodeKind(node),
),
);
if (node is AgentNode) {
await visitAgent(node.agent);
addEdge(nodeId, node.agent.name);
} else if (node is ToolNode) {
final String toolNodeId = 'tool:${node.tool.name}';
final String caption = node.tool is FunctionTool
? '🔧 ${node.tool.name}'
: '🧰 ${node.tool.name}';
nodes.putIfAbsent(
toolNodeId,
() => AgentGraphNode(id: toolNodeId, caption: caption, kind: 'tool'),
);
addEdge(nodeId, toolNodeId);
}
}
for (final Edge edge in workflow.edges) {
if (!workflowNodeNames.contains(edge.toNode)) {
continue;
}
targetedNodes.add(edge.toNode);
final String to = _workflowNodeId(workflow, edge.toNode);
if (edge.fromNode == START) {
addEdge(workflow.name, to, label: edge.route);
continue;
}
if (!workflowNodeNames.contains(edge.fromNode)) {
continue;
}
final String from = _workflowNodeId(workflow, edge.fromNode);
addEdge(from, to, label: edge.route);
}
for (final BaseNode node in workflow.nodes) {
final String to = _workflowNodeId(workflow, node.name);
for (final String dependency in node.dependsOn) {
if (workflowNodeNames.contains(dependency)) {
targetedNodes.add(node.name);
addEdge(_workflowNodeId(workflow, dependency), to);
}
}
}
for (final BaseNode node in workflow.nodes) {
if (!targetedNodes.contains(node.name)) {
addEdge(workflow.name, _workflowNodeId(workflow, node.name));
}
}
};
await visitAgent(rootAgent);
return AgentGraph(
nodes: nodes.values.toList(growable: false),
edges: edges.toList(growable: false),
edgeLabels: Map<(String, String), String>.unmodifiable(edgeLabels),
);
}