buildGraph function

Future<AgentGraph> buildGraph(
  1. BaseAgent rootAgent
)

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),
  );
}