tinyagent-ts
Version:
Modern TypeScript framework for building AI agents with pluggable tools and ReAct reasoning
185 lines • 6.46 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Scratchpad = void 0;
exports.parseThoughtAction = parseThoughtAction;
class Scratchpad {
constructor() {
this.task = '';
this.steps = [];
}
setTask(text) {
this.task = text;
}
clear() {
this.steps = [];
this.task = '';
}
addThought(text) {
this.steps.push({ type: 'thought', text });
}
addAction(action) {
this.steps.push(action);
}
addObservation(text) {
this.steps.push({ type: 'observation', text });
}
addToolResult(tool, output) {
this.steps.push({ type: 'tool_result', tool, output });
}
addReflexion(text) {
this.steps.push({ type: 'reflexion', text });
}
getLastObservation() {
for (let i = this.steps.length - 1; i >= 0; i--) {
const s = this.steps[i];
if (s.type === 'observation')
return s.text;
}
return undefined;
}
/**
* Get all steps in the scratchpad
* @returns Array of all steps
*/
getSteps() {
return [...this.steps];
}
/**
* Get the most recent output/result for a given tool name.
*/
getLastToolResult(toolName) {
for (let i = this.steps.length - 1; i >= 0; i--) {
const s = this.steps[i];
if (s.type === 'tool_result' && s.tool === toolName) {
return s.output;
}
// Some flows use 'observation' steps with JSON stringified output
if (s.type === 'observation') {
try {
const parsed = JSON.parse(s.text);
if (parsed && parsed.tool === toolName && typeof parsed.output === 'string') {
return parsed.output;
}
}
catch { }
}
}
return undefined;
}
/**
* Get the last argument value used for a specific key in any tool call.
*/
getLastArgValue(argKey) {
for (let i = this.steps.length - 1; i >= 0; i--) {
const s = this.steps[i];
if (s.type === 'action' && s.mode === 'json' && s.args && argKey in s.args) {
return s.args[argKey];
}
}
return undefined;
}
toMessages(systemPrompt) {
const msgs = [];
if (systemPrompt)
msgs.push({ role: 'system', content: systemPrompt });
if (this.task)
msgs.push({ role: 'user', content: this.task });
for (const step of this.steps) {
switch (step.type) {
case 'thought':
msgs.push({ role: 'assistant', content: `Thought: ${step.text}` });
break;
case 'reflexion':
msgs.push({ role: 'assistant', content: `Reflexion: ${step.text}` });
break;
case 'action':
if (step.mode === 'code') {
msgs.push({
role: 'assistant',
content: `Action:\n\`\`\`ts\n${step.code}\n\`\`\``,
});
}
else {
const json = JSON.stringify({ tool: step.tool, args: step.args });
msgs.push({ role: 'assistant', content: `Action: ${json}` });
}
break;
case 'observation':
msgs.push({
role: 'assistant',
content: `Observation: ${step.text}`,
});
break;
case 'tool_result':
msgs.push({
role: 'assistant',
content: `Observation: ${step.output}`,
});
break;
}
}
return msgs;
}
}
exports.Scratchpad = Scratchpad;
function parseThoughtAction(text) {
const reflexMatch = text.match(/Reflect(?:ion|xion)?:([\s\S]*?)(?=\n(?:Thought|Action):|$)/i);
const reflexion = reflexMatch ? reflexMatch[1].trim() : undefined;
const thoughtMatch = text.match(/Thought:(.*?)(?:\nAction:|$)/s);
const thought = thoughtMatch ? thoughtMatch[1].trim() : '';
const actionPart = text.split(/\nAction:/s)[1] ?? '';
const trimmed = actionPart.trim();
// First, try to parse as JSON directly
try {
// Check if the trimmed text is a JSON object
if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
const parsed = JSON.parse(trimmed);
if (typeof parsed.tool === 'string' && typeof parsed.args === 'object') {
const action = {
type: 'action',
mode: 'json',
tool: parsed.tool,
args: parsed.args,
};
return { thought, action, reflexion };
}
}
}
catch {
// If JSON parsing fails, continue to other formats
}
// Try to extract JSON from the text (may be embedded in other text)
const jsonMatch = trimmed.match(/{\s*"tool"\s*:\s*"([^"]+)".*}/);
if (jsonMatch) {
try {
const jsonStr = jsonMatch[0];
const parsed = JSON.parse(jsonStr);
if (typeof parsed.tool === 'string' && typeof parsed.args === 'object') {
const action = {
type: 'action',
mode: 'json',
tool: parsed.tool,
args: parsed.args,
};
return { thought, action, reflexion };
}
}
catch {
// If JSON parsing fails, continue to other formats
}
}
// Check for code blocks
if (trimmed.startsWith('```')) {
const codeMatch = trimmed.match(/```(?:\w+)?\n([\s\S]*?)```/);
const code = codeMatch ? codeMatch[1].trim() : trimmed;
const action = { type: 'action', mode: 'code', code };
return { thought, action, reflexion };
}
// Final fallback: treat as code
if (trimmed) {
const action = { type: 'action', mode: 'code', code: trimmed };
return { thought, action, reflexion };
}
return { thought, reflexion };
}
//# sourceMappingURL=scratchpad.js.map