runtimes/runtimes/agent.ts (72 lines of code) (raw):

import Ajv from 'ajv' import { Agent, BedrockTools, CancellationToken, GetToolsOptions, InferSchema, ObjectSchema, Tools, ToolSpec, } from '../server-interface' type Tool<T, R> = { name: string description: string inputSchema: ObjectSchema validate: (input: T, token?: CancellationToken) => boolean invoke: (input: T, token?: CancellationToken, updates?: WritableStream) => Promise<R> } export const newAgent = (): Agent => { const tools: Record<string, Tool<any, any>> = {} const ajv = new Ajv({ strictSchema: false }) return { addTool: <T extends InferSchema<S['inputSchema']>, S extends ToolSpec>( spec: S, handler: (input: T, token?: CancellationToken) => Promise<any> ) => { const validator = ajv.compile(spec.inputSchema) const tool = { validate: (input: InferSchema<S['inputSchema']>) => { return validator(input) }, invoke: handler, name: spec.name, description: spec.description, inputSchema: spec.inputSchema, } tools[spec.name] = tool }, runTool: async (toolName: string, input: any, token?: CancellationToken, updates?: WritableStream) => { const tool = tools[toolName] if (!tool) { throw new Error(`Tool ${toolName} not found`) } if (!tool.validate(input, token)) { throw new Error(`Input for tool ${toolName} is invalid`) } return tool.invoke(input, token, updates) }, getTools: <T extends GetToolsOptions>(options?: T) => { // we have to manually assert the type since // Typescript won't be able to infer the return type // from the if statement. if (options?.format === 'bedrock') { return Object.values(tools).map((tool: Tool<any, any>) => { return { toolSpecification: { name: tool.name, description: tool.description, inputSchema: { json: tool.inputSchema, }, }, } }) as T extends { format: 'bedrock' } ? BedrockTools : never } return Object.values(tools).map((tool: Tool<any, any>) => { return { name: tool.name, description: tool.description, inputSchema: tool.inputSchema, } }) as T extends { format: 'bedrock' } ? never : Tools }, } }