UNPKG

tinyagent-ts

Version:

Modern TypeScript framework for building AI agents with pluggable tools and ReAct reasoning

176 lines 8.23 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MultiStepAgent = void 0; const agent_1 = require("./agent"); const final_answer_tool_1 = require("./final-answer.tool"); const scratchpad_1 = require("./utils/scratchpad"); class MultiStepAgent extends agent_1.Agent { constructor(options = {}) { const { maxSteps = 5, trace = false, onStep, systemPrompt, systemPromptFile, } = options; super({ systemPrompt, systemPromptFile }); this.scratchpad = new scratchpad_1.Scratchpad(); this.maxSteps = maxSteps; this.trace = trace; this.onStep = onStep; } log(t, a, o) { if (!this.trace) return; const parts = [ t && `T: ${t}`, a && `A: ${a.mode === 'code' ? 'code' : a.tool}`, o && `O: ${o}`, ]; console.log(parts.filter(Boolean).join(' | ')); } async run(task, opts = {}) { if (opts.trace !== undefined) this.trace = opts.trace; if (opts.onStep) this.onStep = opts.onStep; const modelName = this.getModelName(); const tools = this.buildToolRegistry(); const toolCatalog = Object.values(tools) .filter((t) => t.meta.name !== 'final_answer') .map((t) => `- ${t.meta.name}: ${t.meta.description}`) .join('\n'); const defaultPrompt = this.promptEngine.render('react', { tools: toolCatalog, }); const systemPrompt = this.customSystemPrompt ?? defaultPrompt; this.scratchpad.clear(); this.scratchpad.setTask(String(task)); const finalTool = new final_answer_tool_1.FinalAnswerTool(); let usedTool = false; for (let step = 0; step < this.maxSteps; step++) { const messages = this.scratchpad.toMessages(systemPrompt); const response = await this.makeOpenRouterRequest(messages, modelName); const reply = response.choices[0]?.message?.content?.trim() ?? ''; const { thought, action } = (0, scratchpad_1.parseThoughtAction)(reply); if (thought) this.scratchpad.addThought(thought); if (action) this.scratchpad.addAction(action); this.log(thought, action); let observation = ''; try { if (action && action.mode === 'json') { if (action.tool === finalTool.name) { if (!usedTool) { console.warn('final_answer called before any other tool'); } const validated = finalTool.schema.parse(action.args ?? {}); const finalAnswer = await finalTool.forward(validated); observation = typeof finalAnswer === 'object' ? JSON.stringify(finalAnswer) : String(finalAnswer); this.scratchpad.addObservation(observation); this.log(undefined, undefined, observation); if (this.onStep) this.onStep(this.scratchpad); return validated; } const tool = tools[action.tool]; if (!tool) { observation = `Unknown tool: ${action.tool}`; } else { // Auto-fill missing arguments from scratchpad if possible let toolArgs = { ...action.args }; // Get argument keys from schema if possible (ZodObject), else from args let argKeys = []; const schema = tool.meta.schema; if (schema && typeof schema.shape === 'object') { argKeys = Object.keys(schema.shape); } else if (toolArgs && typeof toolArgs === 'object') { argKeys = Object.keys(toolArgs); } for (const key of argKeys) { if (toolArgs[key] === undefined) { const lastValue = this.scratchpad.getLastArgValue(key); if (lastValue !== undefined) { toolArgs[key] = lastValue; } } } const result = await tool.call(toolArgs); usedTool = true; observation = JSON.stringify(result); } } else if (action) { observation = 'Code actions not supported'; } else { observation = 'No action provided'; } } catch (err) { observation = err instanceof Error ? err.message : String(err); } this.scratchpad.addObservation(observation); this.log(undefined, undefined, observation); if (this.onStep) this.onStep(this.scratchpad); // Reflexion step const reflectMsgs = this.scratchpad.toMessages(systemPrompt); reflectMsgs.push({ role: 'user', content: 'Reflect:' }); const reflectRes = await this.makeOpenRouterRequest(reflectMsgs, modelName); const reflectReply = reflectRes.choices[0]?.message?.content?.trim() ?? ''; const { thought: fixThought, action: fixAction, reflexion } = (0, scratchpad_1.parseThoughtAction)(reflectReply); if (reflexion) { this.scratchpad.addReflexion(reflexion); this.log(reflexion); } if (fixAction) { if (fixAction.mode === 'json') { if (fixAction.tool === finalTool.name) { if (!usedTool) { console.warn('final_answer called before any other tool'); } const validated = finalTool.schema.parse(fixAction.args ?? {}); const finalAnswer = await finalTool.forward(validated); const obs = typeof finalAnswer === 'object' ? JSON.stringify(finalAnswer) : String(finalAnswer); this.scratchpad.addObservation(obs); this.log(undefined, undefined, obs); if (this.onStep) this.onStep(this.scratchpad); return validated; } const tool = tools[fixAction.tool]; let obs = ''; try { if (!tool) { obs = `Unknown tool: ${fixAction.tool}`; } else { const result = await tool.call(fixAction.args); usedTool = true; obs = JSON.stringify(result); } } catch (err) { obs = err instanceof Error ? err.message : String(err); } if (fixThought) this.scratchpad.addThought(fixThought); this.scratchpad.addAction(fixAction); this.scratchpad.addObservation(obs); this.log(fixThought, fixAction, obs); if (this.onStep) this.onStep(this.scratchpad); } } else if (fixThought) { this.scratchpad.addThought(fixThought); this.log(fixThought); if (this.onStep) this.onStep(this.scratchpad); } } return { answer: this.scratchpad.getLastObservation() ?? '' }; } } exports.MultiStepAgent = MultiStepAgent; //# sourceMappingURL=multiStepAgent.js.map