adk-typescript
Version:
TypeScript port of Google's Agent Development Kit (ADK)
297 lines (296 loc) • 12.4 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;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.runInputFile = runInputFile;
exports.runInteractively = runInteractively;
exports.runCli = runCli;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const readline = __importStar(require("readline"));
const util_1 = require("util");
const InMemoryArtifactService_1 = require("../artifacts/InMemoryArtifactService");
const runners_1 = require("../runners");
const InMemorySessionService_1 = require("../sessions/InMemorySessionService");
const envs = __importStar(require("./utils/envs"));
/**
* Run an agent using input from a file
*
* @param appName Name of the application
* @param userId User ID to create the session with
* @param rootAgent The root agent to run
* @param artifactService Service for managing artifacts
* @param sessionService Service for managing sessions
* @param inputPath Path to the input file
* @returns The created session
*/
async function runInputFile(appName, userId, rootAgent, artifactService, sessionService, inputPath) {
const runner = new runners_1.Runner({
appName,
agent: rootAgent,
artifactService,
sessionService,
});
const inputFileRaw = await (0, util_1.promisify)(fs.readFile)(inputPath, 'utf-8');
const inputFile = JSON.parse(inputFileRaw);
// Add time to state
const state = { ...inputFile.state, _time: new Date() };
// Create a new session with the state
const session = await sessionService.createSession({
appName,
userId,
state,
});
for (const query of inputFile.queries) {
console.log(`[user]: ${query}`);
const content = {
role: 'user',
parts: [{ text: query }]
};
for await (const event of runner.runAsync({
userId: session.userId,
sessionId: session.id,
newMessage: content,
})) {
if (event.content && event.content.parts) {
const text = event.content.parts
.map((part) => part.text || '')
.join('');
if (text) {
console.log(`[${event.author}]: ${text}`);
}
}
}
}
return session;
}
/**
* Run an agent interactively via CLI
*
* @param rootAgent The root agent to run
* @param artifactService Service for managing artifacts
* @param session The session to use
* @param sessionService Service for managing sessions
*/
async function runInteractively(rootAgent, artifactService, session, sessionService) {
const runner = new runners_1.Runner({
appName: session.appName,
agent: rootAgent,
artifactService,
sessionService,
});
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const ask = (q) => new Promise(resolve => rl.question(q, resolve));
let isRunning = true;
while (isRunning) {
const query = (await ask('[user]: ')).trim();
if (!query)
continue;
if (query === 'exit') {
isRunning = false;
break;
}
const content = {
role: 'user',
parts: [{ text: query }]
};
for await (const event of runner.runAsync({
userId: session.userId,
sessionId: session.id,
newMessage: content,
})) {
if (event.content && event.content.parts) {
const text = event.content.parts
.map((part) => part.text || '')
.join('');
if (text) {
console.log(`[${event.author}]: ${text}`);
}
}
}
}
rl.close();
}
/**
* Extract conversation contents from session events
*
* @param session The session to extract contents from
* @returns Array of conversation contents
*/
function getSessionContents(session) {
if (!session.events)
return [];
return session.events
.filter(event => event.content && event.content.parts && event.content.parts.length > 0)
.map(event => event.content);
}
/**
* Run the CLI for a specific agent
*
* @param options Configuration options
* @param options.agentParentDir The parent directory of the agent
* @param options.agentFolderName The folder name of the agent
* @param options.replayFile Optional path to a replay JSON file with initial state and queries
* @param options.resumeFile Optional path to a previously saved session file
* @param options.saveSession Whether to save the session after running
*/
async function runCli({ agentParentDir, agentFolderName, replayFile, resumeFile, saveSession = false, }) {
// Add agent parent directory to the module search path
if (!process.env.PYTHONPATH?.includes(agentParentDir)) {
process.env.PYTHONPATH = (process.env.PYTHONPATH || '') + path.delimiter + agentParentDir;
}
// Initialize services
const artifactService = new InMemoryArtifactService_1.InMemoryArtifactService();
const sessionService = new InMemorySessionService_1.InMemorySessionService();
const userId = 'test_user';
// Create a default session
let session = await sessionService.createSession({
appName: agentFolderName,
userId,
});
// Resolve the agent path more carefully
let agentModulePath;
// Check if we're in the agent directory or parent directory
const currentDir = process.cwd();
const tentativeSrcPath = path.resolve(currentDir, agentFolderName, 'src');
const directSrcPath = path.resolve(currentDir, 'src');
// In TypeScript we use agent.ts directly instead of index.ts
// This better matches the Python ADK where agent.py is the main file
if (fs.existsSync(path.resolve(tentativeSrcPath, 'agent.ts'))) {
// If we're in the parent directory and agent folder has src/agent.ts
agentModulePath = path.resolve(tentativeSrcPath, 'agent.ts');
}
else if (fs.existsSync(path.resolve(directSrcPath, 'agent.ts'))) {
// If we're already in the agent directory and src/agent.ts exists
agentModulePath = path.resolve(directSrcPath, 'agent.ts');
}
else {
// Fall back to directly looking for agent.ts in the specified path
agentModulePath = path.resolve(process.cwd(), agentParentDir, agentFolderName, 'agent.ts');
}
console.log(`Loading agent from: ${agentModulePath}`);
try {
// Load environment variables for the agent
envs.loadDotenvForAgent(agentFolderName, agentParentDir);
// Use ts-node to load the TypeScript module
try {
// First try using ts-node/register to load the module
require('ts-node/register');
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.`);
}
if (replayFile) {
// Run with replay file (creates a new session and runs queries)
session = await runInputFile(agentFolderName, userId, rootAgent, artifactService, sessionService, replayFile);
}
else if (resumeFile) {
// Load session from file and replay events
const sessionRaw = await (0, util_1.promisify)(fs.readFile)(resumeFile, 'utf-8');
const loadedSession = JSON.parse(sessionRaw);
// Merge session data into our session object
session.id = loadedSession.id || session.id;
session.appName = loadedSession.appName || session.appName;
session.userId = loadedSession.userId || session.userId;
session.state = loadedSession.state || session.state;
// Replay all events from the loaded session
if (loadedSession.events && Array.isArray(loadedSession.events)) {
for (const event of loadedSession.events) {
await sessionService.appendEvent({ session, event });
// Display the content for each event
if (event.content && event.content.parts && event.content.parts.length > 0) {
const text = event.content.parts[0].text;
if (text) {
if (event.author === 'user') {
console.log(`[user]: ${text}`);
}
else {
console.log(`[${event.author}]: ${text}`);
}
}
}
}
}
// Continue with interactive mode
await runInteractively(rootAgent, artifactService, session, sessionService);
}
else {
// Run interactively without input file
console.log(`Running agent ${rootAgent.name}, type exit to exit.`);
await runInteractively(rootAgent, artifactService, session, sessionService);
}
// Save session if requested
if (saveSession) {
let sessionPath;
if (replayFile) {
sessionPath = replayFile.replace('.input.json', '.session.json');
}
else {
// Ask for session ID
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const sessionId = await new Promise(resolve => {
rl.question('Session ID to save: ', resolve);
});
rl.close();
sessionPath = path.join(path.dirname(agentModulePath), `${sessionId}.session.json`);
}
// Fetch updated session
const updatedSession = await sessionService.getSession({
appName: session.appName,
userId: session.userId,
sessionId: session.id,
}) || session;
// Save session to file
await (0, util_1.promisify)(fs.writeFile)(sessionPath, JSON.stringify(updatedSession, null, 2));
console.log('Session saved to', sessionPath);
}
}
catch (error) {
console.error('Error loading agent module:', error);
process.exit(1);
}
}
catch (error) {
console.error('Error running CLI:', error);
process.exit(1);
}
}