UNPKG

adk-typescript

Version:

TypeScript port of Google's Agent Development Kit (ADK)

303 lines (302 loc) 14 kB
#!/usr/bin/env node "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 __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 __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); // NOTE: If you get a 'Cannot find module \"commander\"' error, run: npm install commander @types/commander const commander_1 = require("commander"); const path_1 = __importDefault(require("path")); const cli_1 = require("./cli"); const cliEval_1 = require("./cliEval"); const cliDeploy_1 = require("./cliDeploy"); const cliCreate_1 = require("./cliCreate"); const agentGraph_1 = require("./agentGraph"); const apiServer_1 = require("./apiServer"); const webServer_1 = require("./webServer"); const fs = __importStar(require("fs")); const index_1 = require("../index"); const program = new commander_1.Command(); program .name('adk-ts') .description('Agent Development Kit CLI tools (TypeScript port)') .version(index_1.VERSION); // create program .command('create <appName>') .description('Creates a new app in the current folder with prepopulated agent template.') .option('--model <model>', 'Optional. The model used for the root agent.') .option('--api_key <apiKey>', 'Optional. The API Key needed to access the model.') .option('--project <project>', 'Optional. The Google Cloud Project for using VertexAI as backend.') .option('--region <region>', 'Optional. The Google Cloud Region for using VertexAI as backend.') .action(async (appName, options) => { await (0, cliCreate_1.runCmd)({ agentName: appName, model: options.model, googleApiKey: options.api_key, googleCloudProject: options.project, googleCloudRegion: options.region, }); }); // run program .command('run <agent>') .description('Runs an interactive CLI for a certain agent.') .option('--save_session', 'Whether to save the session to a json file on exit.', false) .option('--replay <replayFile>', 'Path to a JSON file with initial state and user queries. Creates a new session with this state and runs the queries without interactive mode.') .option('--resume <resumeFile>', 'Path to a previously saved session file. Replays the session and continues in interactive mode.') .action((agent, options) => { try { // Validate that replay and resume are not both specified if (options.replay && options.resume) { console.error('Error: The --replay and --resume options cannot be used together.'); process.exit(1); } // Register ts-node to handle TypeScript files try { require('ts-node/register'); } catch (error) { console.warn('Failed to register ts-node. If you have TypeScript files, this might cause issues.'); } // Resolve the agent path more carefully const cwd = process.cwd(); const agentPath = path_1.default.resolve(cwd, agent); // If agent is "." (current directory), use current directory name as agent name if (agent === '.') { const agentParentDir = path_1.default.dirname(cwd); const agentFolderName = path_1.default.basename(cwd); console.log(`Running agent with parent dir: ${agentParentDir}, folder name: ${agentFolderName}`); (0, cli_1.runCli)({ agentParentDir, agentFolderName, replayFile: options.replay, resumeFile: options.resume, saveSession: options.save_session, }); } else { const agentParentDir = path_1.default.dirname(agentPath); const agentFolderName = path_1.default.basename(agentPath); console.log(`Running agent with parent dir: ${agentParentDir}, folder name: ${agentFolderName}`); (0, cli_1.runCli)({ agentParentDir, agentFolderName, replayFile: options.replay, resumeFile: options.resume, saveSession: options.save_session, }); } } catch (error) { console.error('Error running agent:', error); process.exit(1); } }); // graph program .command('graph <agent>') .description('Generates a graph visualization of the agent and its tools.') .option('--output <outputFile>', 'Path to save the graph image. Default is "<agent_name>_graph.png"') .option('--highlight <pairs...>', 'Pairs of node names to highlight in the graph, e.g., "agent1,agent2"', []) .action((agent, options) => { try { // Import agent module and get root agent const agentParentDir = path_1.default.dirname(agent); const agentFolderName = path_1.default.basename(agent); const agentModulePath = path_1.default.resolve(process.cwd(), agentParentDir, agentFolderName, 'index.ts'); console.log(`Loading agent from: ${agentModulePath}`); const agentModule = require(agentModulePath); // Get the rootAgent from the module const rootAgent = agentModule.rootAgent || (agentModule.default && agentModule.default.rootAgent); if (!rootAgent) { throw new Error(`Could not find rootAgent in module ${agentModulePath}. Make sure it exports a 'rootAgent' property.`); } // Parse highlight pairs if provided const highlightPairs = []; for (const pair of options.highlight) { const [from, to] = pair.split(','); if (from && to) { highlightPairs.push([from, to]); } } // Generate the graph const outputFile = options.output || `${agentFolderName}_graph.png`; const graph = (0, agentGraph_1.getAgentGraph)(rootAgent, highlightPairs, true); // Save the graph image fs.writeFileSync(outputFile, graph); console.log(`Graph saved to ${outputFile}`); } catch (error) { console.error('Error generating graph:', error); process.exit(1); } }); // eval program .command('eval <agentModuleFilePath> [evalSetFilePaths...]') .description('Evaluates an agent given the eval sets.') .option('--config_file_path <configFilePath>', 'Optional. The path to config file.') .option('--print_detailed_results', 'Whether to print detailed results on console.', false) .action(async (agentModuleFilePath, evalSetFilePaths, options) => { // Load evaluation criteria const evaluationCriteria = (0, cliEval_1.getEvaluationCriteriaOrDefault)(options.config_file_path); const evalMetrics = []; for (const metricName in evaluationCriteria) { evalMetrics.push({ metricName, threshold: evaluationCriteria[metricName] }); } console.log(`Using evaluation criteria:`, evaluationCriteria); // Load agent and reset function const rootAgent = (0, cliEval_1.getRootAgent)(agentModuleFilePath); const resetFunc = (0, cliEval_1.tryGetResetFunc)(agentModuleFilePath); // Parse eval sets const evalSetToEvals = (0, cliEval_1.parseAndGetEvalsToRun)(evalSetFilePaths); // Run evals for await (const result of (0, cliEval_1.runEvals)({ evalSetToEvals, rootAgent, resetFunc, evalMetrics, printDetailedResults: options.print_detailed_results, })) { // Print or process results as needed // (You can add summary logic here if desired) } }); // deploy cloud_run program .command('deploy cloud_run <agent>') .description('Deploys an agent to Cloud Run.') .option('--project <project>', 'Google Cloud project to deploy the agent.') .option('--region <region>', 'Google Cloud region to deploy the agent.') .option('--service_name <serviceName>', 'The service name to use in Cloud Run.', 'adk-default-service-name') .option('--app_name <appName>', 'App name of the ADK API server.') .option('--port <port>', 'The port of the ADK API server.', '8000') .option('--trace_to_cloud', 'Whether to enable Cloud Trace for cloud run.', false) .option('--with_ui', 'Deploy ADK Web UI if set.', false) .option('--temp_folder <tempFolder>', 'Temp folder for the generated Cloud Run source files.') .option('--verbosity <verbosity>', 'Override the default verbosity level.', 'WARNING') .option('--session_db_url <sessionDbUrl>', 'Optional. The database URL to store the session.\n' + ' - Use \'agentengine://<agent_engine_resource_id>\' to connect to Agent Engine sessions.\n' + ' - Use \'sqlite://<path_to_sqlite_file>\' to connect to a SQLite DB.\n' + ' - See https://docs.sqlalchemy.org/en/20/core/engines.html#backend-specific-urls for more details on supported DB URLs.') .action((agent, options) => { (0, cliDeploy_1.toCloudRun)({ agentFolder: agent, project: options.project, region: options.region, serviceName: options.service_name, appName: options.app_name, tempFolder: options.temp_folder, port: parseInt(options.port, 10), traceToCloud: options.trace_to_cloud, withUi: options.with_ui, verbosity: options.verbosity, sessionDbUrl: options.session_db_url, }); }); // web command program .command('web [agent]') .description('Starts a web server for agents with Socket.IO for live interaction.') .option('--port <port>', 'Port to run the server on.', '3000') .option('--allow_origin <origins...>', 'Allowed origins for CORS.', ['*']) .option('--session_db_url <sessionDbUrl>', 'Optional. The database URL to store the session.\n' + ' - Use \'agentengine://<agent_engine_resource_id>\' to connect to Agent Engine sessions.\n' + ' - Use \'sqlite://<path_to_sqlite_file>\' to connect to a SQLite DB.\n' + ' - See https://docs.sqlalchemy.org/en/20/core/engines.html#backend-specific-urls for more details on supported DB URLs.') .action((agent, options) => { try { let agentDir = '.'; if (agent) { // If specific agent is provided, use it // Resolve to an absolute path to avoid path resolution issues agentDir = path_1.default.resolve(process.cwd(), agent); console.log(`Using agent directory: ${agentDir}`); } // Start web server with the specified directory (0, webServer_1.startWebServer)({ agentDir, port: parseInt(options.port, 10), allowOrigins: options.allow_origin, sessionDbUrl: options.session_db_url }); } catch (error) { console.error('Error starting web server:', error); process.exit(1); } }); // api_server - implement this with our new apiServer module program .command('api_server') .description('Starts an API server for agents.') .option('--agent_dir <agentDir>', 'Directory containing agent modules.', '.') .option('--session_db_url <sessionDbUrl>', 'Optional. The database URL to store the session.\n' + ' - Use \'agentengine://<agent_engine_resource_id>\' to connect to Agent Engine sessions.\n' + ' - Use \'sqlite://<path_to_sqlite_file>\' to connect to a SQLite DB.\n' + ' - See https://docs.sqlalchemy.org/en/20/core/engines.html#backend-specific-urls for more details on supported DB URLs.') .option('--port <port>', 'Port to run the server on.', '8000') .option('--allow_origin <origins...>', 'Allowed origins for CORS.', ['*']) .option('--with_ui', 'Serve web UI if set.', false) .option('--trace_to_cloud', 'Enable Cloud Trace.', false) .action((options) => { try { const { app, server } = (0, apiServer_1.createApiServer)({ agentDir: options.agent_dir, sessionDbUrl: options.session_db_url, allowOrigins: options.allow_origin, web: options.with_ui, traceToCloud: options.trace_to_cloud, port: parseInt(options.port, 10) }); console.log(`API server started on port ${options.port}`); console.log(`Agent directory: ${path_1.default.resolve(options.agent_dir)}`); // Handle graceful shutdown process.on('SIGINT', () => { console.log('Shutting down API server...'); server.close(() => { console.log('API server stopped.'); process.exit(0); }); }); } catch (error) { console.error('Error starting API server:', error); process.exit(1); } }); program.parse(process.argv);