UNPKG

@iqai/adk-cli

Version:

CLI tool for creating, running, and testing ADK-TS agents

287 lines 12.9 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.AgentGraphService = void 0; const node_fs_1 = require("node:fs"); const node_path_1 = require("node:path"); const node_url_1 = require("node:url"); const common_1 = require("@nestjs/common"); const agent_loader_service_1 = require("./agent-loader.service"); const agent_manager_service_1 = require("./agent-manager.service"); let AgentGraphService = class AgentGraphService { agentManager; logger = new common_1.Logger("agent-graph"); constructor(agentManager) { this.agentManager = agentManager; } async getGraph(agentPath) { const registry = this.agentManager.getAgents(); const loaded = this.agentManager.getLoadedAgents().get(agentPath); let agent = loaded?.agent ?? registry.get(agentPath)?.instance; const nodes = []; const edges = []; const seen = new Set(); if (!agent) { // Try to load the agent module just-in-time for richer introspection const root = registry.get(agentPath); if (root) { try { const loader = new agent_loader_service_1.AgentLoader(true); let filePath = (0, node_path_1.join)(root.absolutePath, "agent.ts"); if (!(0, node_fs_1.existsSync)(filePath)) { filePath = (0, node_path_1.join)(root.absolutePath, "agent.js"); } if ((0, node_fs_1.existsSync)(filePath)) { loader.loadEnvironmentVariables(filePath); let mod = {}; try { mod = filePath.endsWith(".ts") ? await loader.importTypeScriptFile(filePath, root.projectRoot) : (await Promise.resolve(`${(0, node_url_1.pathToFileURL)(filePath).href}`).then(s => __importStar(require(s)))); const agentResult = await loader.resolveAgentExport(mod); agent = agentResult.agent; } catch (e) { this.logger.warn(`Failed to load agent for graph at ${filePath}: ${e instanceof Error ? e.message : String(e)}`); } } } catch { } } } if (agent) { // Preferred path: we have an actual agent instance to introspect await this.traverseAgentGraph(agent, nodes, edges, seen); } else { this.logger.debug("Graph fallback: agent instance not available"); // Fallback: build a directory-based graph from the registry without loading the agent const root = registry.get(agentPath); if (!root) { // Unknown agent id; return empty graph instead of 500 return { nodes: [], edges: [] }; } await this.buildGraphFromRegistryFallback(agentPath, nodes, edges, seen); } return { nodes, edges }; } // Cross-package safe detection using constructor name instead of instanceof // Rationale: Users' agents are compiled with esbuild and may load their own // copy of @iqai/adk. instanceof would fail across module boundaries, so we // use duck typing and constructor.name to classify. getNodeMetaForAgent(ag) { const typeName = ag?.constructor?.name ?? "BaseAgent"; if (typeName === "LlmAgent") { return { type: "LlmAgent", shape: "ellipse", group: undefined }; } if (typeName === "SequentialAgent") { return { type: "SequentialAgent", shape: "ellipse", group: "sequential" }; } if (typeName === "LoopAgent") { return { type: "LoopAgent", shape: "ellipse", group: "loop" }; } if (typeName === "ParallelAgent") { return { type: "ParallelAgent", shape: "ellipse", group: "parallel" }; } return { type: typeName, shape: "ellipse", group: undefined }; } // Narrowing helper for LoopAgent without importing concrete class isLoopAgent(agent) { return agent?.constructor?.name === "LoopAgent"; } createToolNode(tool) { return { id: `tool:${tool.name}`, label: `🔧 ${tool.name}`, kind: "tool", type: tool.constructor?.name ?? "Tool", shape: "box", }; } addAgentNode(ag, nodes, seen) { const meta = this.getNodeMetaForAgent(ag); const node = { id: `agent:${ag.name}`, label: `🤖 ${ag.name}`, kind: "agent", type: meta.type, shape: meta.shape, group: meta.group, }; if (!seen.has(node.id)) { nodes.push(node); seen.add(node.id); } return node; } async traverseAgentGraph(ag, nodes, edges, seen, parent) { const current = this.addAgentNode(ag, nodes, seen); if (parent) { edges.push({ from: `agent:${parent.name}`, to: current.id }); } // Recurse sub-agents for (const sub of ag.subAgents || []) { await this.traverseAgentGraph(sub, nodes, edges, seen, ag); } // Tools: prefer duck typing to avoid cross-realm instanceof issues if (typeof ag?.canonicalTools === "function") { try { const tools = await ag.canonicalTools(); for (const t of tools) { const n = this.createToolNode(t); if (!seen.has(n.id)) { nodes.push(n); seen.add(n.id); } edges.push({ from: `agent:${ag.name}`, to: n.id }); } } catch (e) { this.logger.warn(`Failed to resolve tools for agent ${ag.name}: ${e instanceof Error ? e.message : String(e)}`); } } } async buildGraphFromRegistryFallback(agentPath, nodes, edges, seen) { const registry = this.agentManager.getAgents(); const root = registry.get(agentPath); if (!root) return; // handled by caller earlier const rootNode = { id: `agent:${root.name}`, label: `🤖 ${root.name}`, kind: "agent", type: "Agent", shape: "ellipse", }; nodes.push(rootNode); seen.add(rootNode.id); const prefix = root.relativePath.endsWith("/") ? root.relativePath : `${root.relativePath}/`; for (const [rel, entry] of registry.entries()) { if (!rel.startsWith(prefix)) continue; if (rel === root.relativePath) continue; const childNode = { id: `agent:${entry.name}`, label: `🤖 ${entry.name}`, kind: "agent", type: "Agent", shape: "ellipse", }; if (!seen.has(childNode.id)) { nodes.push(childNode); seen.add(childNode.id); } edges.push({ from: rootNode.id, to: childNode.id }); // Best-effort: try to load sub-agent module directly to discover tools try { const loader = new agent_loader_service_1.AgentLoader(true); // Resolve agent file path let filePath = (0, node_path_1.join)(entry.absolutePath, "agent.ts"); if (!(0, node_fs_1.existsSync)(filePath)) { filePath = (0, node_path_1.join)(entry.absolutePath, "agent.js"); } if ((0, node_fs_1.existsSync)(filePath)) { // Load env from the project before import loader.loadEnvironmentVariables(filePath); let mod = {}; try { if (filePath.endsWith(".ts")) { mod = await loader.importTypeScriptFile(filePath, entry.projectRoot); } else { mod = (await Promise.resolve(`${(0, node_url_1.pathToFileURL)(filePath).href}`).then(s => __importStar(require(s)))); } } catch (e) { this.logger.warn(`Failed to import sub-agent module at ${filePath}: ${e instanceof Error ? e.message : String(e)}`); } // Try to find a function export that returns an agent with canonicalTools (no build) let subAgentInstance; for (const [k, v] of Object.entries(mod)) { if (typeof v !== "function") continue; // Heuristic: names containing 'agent' if (!/agent/i.test(k)) continue; try { const result = await Promise.resolve(v()); if (result && typeof result?.canonicalTools === "function") { subAgentInstance = result; break; } } catch { } } if (subAgentInstance) { try { const tools = await subAgentInstance.canonicalTools(); for (const t of tools) { const n = this.createToolNode(t); if (!seen.has(n.id)) { nodes.push(n); seen.add(n.id); } edges.push({ from: childNode.id, to: n.id }); } } catch (e) { this.logger.warn(`Failed to resolve tools for sub-agent ${entry.name}: ${e instanceof Error ? e.message : String(e)}`); } } } } catch { } } } }; exports.AgentGraphService = AgentGraphService; exports.AgentGraphService = AgentGraphService = __decorate([ (0, common_1.Injectable)(), __metadata("design:paramtypes", [agent_manager_service_1.AgentManager]) ], AgentGraphService); //# sourceMappingURL=agent-graph.service.js.map