adk-typescript
Version:
TypeScript port of Google's Agent Development Kit (ADK)
303 lines (302 loc) • 14 kB
JavaScript
;
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);