feat(agent): add provider options passthrough, tool call records, and completion validation retries
This commit is contained in:
@@ -1,5 +1,14 @@
|
||||
# Changelog
|
||||
|
||||
## 2026-05-07 - 3.1.0 - feat(agent)
|
||||
add provider options passthrough, tool call records, and completion validation retries
|
||||
|
||||
- forward provider-specific options to the underlying streamText call
|
||||
- return structured tool call records with inputs, outputs, and errors in agent results
|
||||
- support validateCompletion with reprompting and configurable validation retry limits
|
||||
- export ProviderOptions and tool call record types for consumers
|
||||
- update tests and documentation for the new agent run options and result fields
|
||||
|
||||
## 2026-04-30 - 3.0.3 - fix(build)
|
||||
tighten TypeScript configuration and update dependencies for zod v4 compatibility
|
||||
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@
|
||||
"@types/node": "^25.6.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@push.rocks/smartai": "^2.0.0",
|
||||
"@push.rocks/smartai": "^2.2.0",
|
||||
"@push.rocks/smartfs": "^1.5.1",
|
||||
"@push.rocks/smartrequest": "^5.0.1",
|
||||
"@push.rocks/smartshell": "^3.3.8",
|
||||
|
||||
Generated
+537
-587
File diff suppressed because it is too large
Load Diff
@@ -91,11 +91,15 @@ The single entry point. Options:
|
||||
| `prompt` | `string` | *required* | The user's task/question |
|
||||
| `system` | `string` | `undefined` | System prompt |
|
||||
| `tools` | `ToolSet` | `{}` | Tools the agent can call |
|
||||
| `providerOptions` | `ProviderOptions` | `undefined` | Provider-specific AI SDK request options passed through to `streamText()` |
|
||||
| `maxSteps` | `number` | `20` | Max agentic steps before stopping |
|
||||
| `messages` | `ModelMessage[]` | `[]` | Conversation history (for multi-turn) |
|
||||
| `maxRetries` | `number` | `5` | Max retries on rate-limit/server errors |
|
||||
| `onToken` | `(delta: string) => void` | — | Streaming token callback |
|
||||
| `onToolCall` | `(name: string) => void` | — | Called when a tool is invoked |
|
||||
| `onToolResult` | `(name: string, result: unknown) => void` | — | Called when a tool finishes |
|
||||
| `validateCompletion` | `(result) => string \| void` | — | Return a string to reject and reprompt an incomplete run |
|
||||
| `maxValidationRetries` | `number` | `0` | Number of validation-triggered reprompts allowed |
|
||||
| `onContextOverflow` | `(messages) => messages` | — | Handle context overflow (e.g., compact messages) |
|
||||
|
||||
### `IAgentRunResult`
|
||||
@@ -107,13 +111,75 @@ interface IAgentRunResult {
|
||||
steps: number; // Number of agentic steps taken
|
||||
messages: ModelMessage[]; // Full conversation for multi-turn
|
||||
usage: {
|
||||
promptTokens: number;
|
||||
completionTokens: number;
|
||||
inputTokens: number;
|
||||
outputTokens: number;
|
||||
totalTokens: number;
|
||||
};
|
||||
toolCalls: Array<{
|
||||
toolName: string;
|
||||
input: unknown;
|
||||
output?: unknown;
|
||||
error?: string;
|
||||
}>;
|
||||
}
|
||||
```
|
||||
|
||||
### OpenAI Provider Options
|
||||
|
||||
Use `providerOptions` for provider-specific request settings such as GPT reasoning effort. SmartAgent forwards the object unchanged to AI SDK `streamText()`.
|
||||
|
||||
```typescript
|
||||
import { getModelSetup } from '@push.rocks/smartai';
|
||||
import { runAgent } from '@push.rocks/smartagent';
|
||||
|
||||
const setup = getModelSetup({
|
||||
provider: 'openai',
|
||||
model: 'gpt-5.5',
|
||||
apiKey: process.env.OPENAI_API_KEY,
|
||||
providerOptions: {
|
||||
openai: {
|
||||
reasoningEffort: 'xhigh',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const result = await runAgent({
|
||||
model: setup.model,
|
||||
system: 'You handle financial documents carefully.',
|
||||
prompt: 'Process this inbox document.',
|
||||
tools,
|
||||
maxSteps: 20,
|
||||
providerOptions: setup.providerOptions,
|
||||
});
|
||||
|
||||
const saved = result.toolCalls.some((call) =>
|
||||
call.toolName === 'saveVoucher' || call.toolName === 'saveBankStatement',
|
||||
);
|
||||
```
|
||||
|
||||
### Completion Validation
|
||||
|
||||
Use `validateCompletion` when a workflow must not finish unless a required side-effect happened. Return `void` to accept the run, or return a string to append that string as a new user message and continue. If retries are exhausted, `runAgent()` throws.
|
||||
|
||||
```typescript
|
||||
const result = await runAgent({
|
||||
model,
|
||||
prompt: 'Process this inbox document.',
|
||||
tools,
|
||||
maxSteps: 20,
|
||||
maxValidationRetries: 1,
|
||||
validateCompletion: (result) => {
|
||||
const saved = result.toolCalls.some((call) =>
|
||||
call.toolName === 'saveVoucher' || call.toolName === 'saveBankStatement',
|
||||
);
|
||||
|
||||
if (!saved) {
|
||||
return 'You must call saveVoucher or saveBankStatement before finalizing.';
|
||||
}
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Defining Tools 🛠️
|
||||
|
||||
Tools use Vercel AI SDK's `tool()` helper with Zod schemas:
|
||||
|
||||
+153
@@ -1,8 +1,56 @@
|
||||
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||||
import { MockLanguageModelV3, convertArrayToReadableStream } from 'ai/test';
|
||||
import * as smartagent from '../ts/index.js';
|
||||
import { filesystemTool, shellTool, httpTool, jsonTool, truncateOutput } from '../ts_tools/index.js';
|
||||
import { compactMessages } from '../ts_compaction/index.js';
|
||||
|
||||
const createUsage = (inputTokens: number, outputTokens: number) => ({
|
||||
inputTokens: {
|
||||
total: inputTokens,
|
||||
noCache: inputTokens,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
outputTokens: {
|
||||
total: outputTokens,
|
||||
text: outputTokens,
|
||||
reasoning: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const createTextStreamResult = (text: string) => ({
|
||||
stream: convertArrayToReadableStream([
|
||||
{ type: 'stream-start', warnings: [] },
|
||||
{ type: 'response-metadata', id: 'response-1', timestamp: new Date(0), modelId: 'mock-model' },
|
||||
{ type: 'text-start', id: 'text-1' },
|
||||
{ type: 'text-delta', id: 'text-1', delta: text },
|
||||
{ type: 'text-end', id: 'text-1' },
|
||||
{
|
||||
type: 'finish',
|
||||
finishReason: { unified: 'stop', raw: 'stop' },
|
||||
usage: createUsage(1, 1),
|
||||
},
|
||||
] as any[]),
|
||||
});
|
||||
|
||||
const createToolCallStreamResult = (toolName: string, input: unknown) => ({
|
||||
stream: convertArrayToReadableStream([
|
||||
{ type: 'stream-start', warnings: [] },
|
||||
{ type: 'response-metadata', id: 'response-1', timestamp: new Date(0), modelId: 'mock-model' },
|
||||
{
|
||||
type: 'tool-call',
|
||||
toolCallId: 'tool-call-1',
|
||||
toolName,
|
||||
input: JSON.stringify(input),
|
||||
},
|
||||
{
|
||||
type: 'finish',
|
||||
finishReason: { unified: 'tool-calls', raw: 'tool-calls' },
|
||||
usage: createUsage(2, 1),
|
||||
},
|
||||
] as any[]),
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// Core exports
|
||||
// ============================================================
|
||||
@@ -35,6 +83,111 @@ tap.test('should re-export stepCountIs', async () => {
|
||||
expect(smartagent.stepCountIs).toBeTypeOf('function');
|
||||
});
|
||||
|
||||
tap.test('runAgent should forward providerOptions to streamText', async () => {
|
||||
const model = new MockLanguageModelV3({
|
||||
doStream: async () => createTextStreamResult('ok') as any,
|
||||
});
|
||||
|
||||
const providerOptions = {
|
||||
openai: {
|
||||
reasoningEffort: 'xhigh',
|
||||
},
|
||||
} as const;
|
||||
|
||||
const result = await smartagent.runAgent({
|
||||
model,
|
||||
prompt: 'hello',
|
||||
providerOptions,
|
||||
});
|
||||
|
||||
expect(result.text).toEqual('ok');
|
||||
expect((model.doStreamCalls[0].providerOptions as any).openai.reasoningEffort).toEqual('xhigh');
|
||||
});
|
||||
|
||||
tap.test('runAgent should return final tool call records', async () => {
|
||||
let streamCallCount = 0;
|
||||
const callbackToolCalls: Array<{ name: string; input: unknown }> = [];
|
||||
const callbackToolResults: Array<{ name: string; result: unknown }> = [];
|
||||
const model = new MockLanguageModelV3({
|
||||
doStream: async () => {
|
||||
streamCallCount++;
|
||||
return streamCallCount === 1
|
||||
? createToolCallStreamResult('echo', { text: 'hello' }) as any
|
||||
: createTextStreamResult('saved') as any;
|
||||
},
|
||||
});
|
||||
|
||||
const result = await smartagent.runAgent({
|
||||
model,
|
||||
prompt: 'echo hello',
|
||||
tools: {
|
||||
echo: smartagent.tool({
|
||||
description: 'Echo text',
|
||||
inputSchema: smartagent.z.object({ text: smartagent.z.string() }),
|
||||
execute: async ({ text }: { text: string }) => `saved:${text}`,
|
||||
}),
|
||||
},
|
||||
maxSteps: 5,
|
||||
onToolCall: (name, input) => callbackToolCalls.push({ name, input }),
|
||||
onToolResult: (name, result) => callbackToolResults.push({ name, result }),
|
||||
});
|
||||
|
||||
const echoCall = result.toolCalls.find((toolCall) => toolCall.toolName === 'echo');
|
||||
|
||||
expect(result.text).toEqual('saved');
|
||||
expect(echoCall).toBeTruthy();
|
||||
expect(echoCall!.input).toEqual({ text: 'hello' });
|
||||
expect(echoCall!.output).toEqual('saved:hello');
|
||||
expect(callbackToolCalls[0]).toEqual({ name: 'echo', input: { text: 'hello' } });
|
||||
expect(callbackToolResults[0]).toEqual({ name: 'echo', result: 'saved:hello' });
|
||||
});
|
||||
|
||||
tap.test('runAgent should reprompt when validateCompletion returns a string', async () => {
|
||||
let streamCallCount = 0;
|
||||
let validationCallCount = 0;
|
||||
const model = new MockLanguageModelV3({
|
||||
doStream: async () => {
|
||||
streamCallCount++;
|
||||
return createTextStreamResult(streamCallCount === 1 ? 'incomplete' : 'complete') as any;
|
||||
},
|
||||
});
|
||||
|
||||
const result = await smartagent.runAgent({
|
||||
model,
|
||||
prompt: 'process document',
|
||||
maxValidationRetries: 1,
|
||||
validateCompletion: (runResult) => {
|
||||
validationCallCount++;
|
||||
return runResult.text === 'complete' ? undefined : 'Call a save tool before finalizing.';
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.text).toEqual('complete');
|
||||
expect(validationCallCount).toEqual(2);
|
||||
expect(model.doStreamCalls.length).toEqual(2);
|
||||
expect(JSON.stringify(model.doStreamCalls[1].prompt)).toInclude('Call a save tool before finalizing.');
|
||||
});
|
||||
|
||||
tap.test('runAgent should reject when validation retries are exhausted', async () => {
|
||||
let threw = false;
|
||||
const model = new MockLanguageModelV3({
|
||||
doStream: async () => createTextStreamResult('incomplete') as any,
|
||||
});
|
||||
|
||||
try {
|
||||
await smartagent.runAgent({
|
||||
model,
|
||||
prompt: 'process document',
|
||||
validateCompletion: () => 'Missing required save tool call.',
|
||||
});
|
||||
} catch (error) {
|
||||
threw = true;
|
||||
expect((error as Error).message).toInclude('Missing required save tool call.');
|
||||
}
|
||||
|
||||
expect(threw).toBeTrue();
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// ToolRegistry
|
||||
// ============================================================
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@push.rocks/smartagent',
|
||||
version: '3.0.3',
|
||||
version: '3.1.0',
|
||||
description: 'Agentic loop for ai-sdk (Vercel AI SDK). Wraps streamText with stopWhen for parallel multi-step tool execution. Built on @push.rocks/smartai.'
|
||||
}
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ export { ToolRegistry } from './smartagent.classes.toolregistry.js';
|
||||
export { truncateOutput } from './smartagent.utils.truncation.js';
|
||||
export type { ITruncateResult } from './smartagent.utils.truncation.js';
|
||||
export { ContextOverflowError } from './smartagent.interfaces.js';
|
||||
export type { IAgentRunOptions, IAgentRunResult } from './smartagent.interfaces.js';
|
||||
export type { IAgentRunOptions, IAgentRunResult, IAgentToolCallRecord, ProviderOptions } from './smartagent.interfaces.js';
|
||||
|
||||
// Re-export tool() and z so consumers can define tools without extra imports
|
||||
export { tool, jsonSchema } from '@push.rocks/smartai';
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ import { tool, jsonSchema } from '@push.rocks/smartai';
|
||||
|
||||
export { tool, jsonSchema };
|
||||
|
||||
export type { LanguageModelV3 } from '@push.rocks/smartai';
|
||||
export type { LanguageModelV3, TSmartAiProviderOptions as ProviderOptions } from '@push.rocks/smartai';
|
||||
|
||||
// zod
|
||||
import { z } from 'zod';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Retry backoff and context overflow logic derived from opencode (MIT) — https://github.com/sst/opencode
|
||||
|
||||
import * as plugins from './plugins.js';
|
||||
import type { IAgentRunOptions, IAgentRunResult } from './smartagent.interfaces.js';
|
||||
import type { IAgentRunOptions, IAgentRunResult, IAgentToolCallRecord } from './smartagent.interfaces.js';
|
||||
import { ContextOverflowError } from './smartagent.interfaces.js';
|
||||
|
||||
// Retry constants
|
||||
@@ -76,11 +76,62 @@ function isContextOverflow(err: unknown): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
function parseToolInput(input: unknown): unknown {
|
||||
if (typeof input !== 'string') return input;
|
||||
try {
|
||||
return JSON.parse(input);
|
||||
} catch {
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
function errorToString(error: unknown): string {
|
||||
if (error instanceof Error) return error.message;
|
||||
return String(error);
|
||||
}
|
||||
|
||||
function recordToolCall(
|
||||
toolCalls: IAgentToolCallRecord[],
|
||||
toolCallIndexes: Map<string, number>,
|
||||
toolCall: unknown,
|
||||
update: { output?: unknown; error?: unknown } = {},
|
||||
): void {
|
||||
const call = toolCall as any;
|
||||
const toolCallId = call?.toolCallId;
|
||||
const nextRecord: IAgentToolCallRecord = {
|
||||
toolName: String(call?.toolName ?? ''),
|
||||
input: parseToolInput(call?.input ?? call?.args),
|
||||
};
|
||||
const hasOutput = Object.prototype.hasOwnProperty.call(update, 'output');
|
||||
const hasError = Object.prototype.hasOwnProperty.call(update, 'error');
|
||||
|
||||
if (hasOutput) nextRecord.output = update.output;
|
||||
if (hasError && update.error !== undefined) nextRecord.error = errorToString(update.error);
|
||||
|
||||
const existingIndex = typeof toolCallId === 'string' ? toolCallIndexes.get(toolCallId) : undefined;
|
||||
if (existingIndex !== undefined) {
|
||||
const existingRecord = toolCalls[existingIndex];
|
||||
existingRecord.toolName = nextRecord.toolName || existingRecord.toolName;
|
||||
if (nextRecord.input !== undefined) existingRecord.input = nextRecord.input;
|
||||
if (hasOutput) existingRecord.output = nextRecord.output;
|
||||
if (nextRecord.error !== undefined) existingRecord.error = nextRecord.error;
|
||||
return;
|
||||
}
|
||||
|
||||
toolCalls.push(nextRecord);
|
||||
if (typeof toolCallId === 'string') {
|
||||
toolCallIndexes.set(toolCallId, toolCalls.length - 1);
|
||||
}
|
||||
}
|
||||
|
||||
export async function runAgent(options: IAgentRunOptions): Promise<IAgentRunResult> {
|
||||
let stepCount = 0;
|
||||
let attempt = 0;
|
||||
let totalInput = 0;
|
||||
let totalOutput = 0;
|
||||
let validationRetries = 0;
|
||||
const toolCalls: IAgentToolCallRecord[] = [];
|
||||
const toolCallIndexes = new Map<string, number>();
|
||||
|
||||
const tools = options.tools ?? {};
|
||||
|
||||
@@ -110,6 +161,7 @@ export async function runAgent(options: IAgentRunOptions): Promise<IAgentRunResu
|
||||
system: options.system,
|
||||
messages,
|
||||
tools: allTools,
|
||||
providerOptions: options.providerOptions,
|
||||
stopWhen: plugins.stepCountIs(options.maxSteps ?? 20),
|
||||
maxRetries: 0, // handled manually below
|
||||
abortSignal: options.abort,
|
||||
@@ -137,20 +189,48 @@ export async function runAgent(options: IAgentRunOptions): Promise<IAgentRunResu
|
||||
|
||||
experimental_onToolCallStart: options.onToolCall
|
||||
? ({ toolCall }) => {
|
||||
options.onToolCall!(toolCall.toolName, (toolCall as any).input ?? (toolCall as any).args);
|
||||
const input = parseToolInput((toolCall as any).input ?? (toolCall as any).args);
|
||||
recordToolCall(toolCalls, toolCallIndexes, toolCall);
|
||||
options.onToolCall!(toolCall.toolName, input);
|
||||
}
|
||||
: undefined,
|
||||
: ({ toolCall }) => {
|
||||
recordToolCall(toolCalls, toolCallIndexes, toolCall);
|
||||
},
|
||||
|
||||
experimental_onToolCallFinish: options.onToolResult
|
||||
? ({ toolCall, output }) => {
|
||||
options.onToolResult!(toolCall.toolName, output);
|
||||
? (event) => {
|
||||
recordToolCall(
|
||||
toolCalls,
|
||||
toolCallIndexes,
|
||||
event.toolCall,
|
||||
event.success ? { output: event.output } : { error: event.error },
|
||||
);
|
||||
options.onToolResult!(event.toolCall.toolName, event.success ? event.output : undefined);
|
||||
}
|
||||
: undefined,
|
||||
: (event) => {
|
||||
recordToolCall(
|
||||
toolCalls,
|
||||
toolCallIndexes,
|
||||
event.toolCall,
|
||||
event.success ? { output: event.output } : { error: event.error },
|
||||
);
|
||||
},
|
||||
|
||||
onStepFinish: ({ usage }) => {
|
||||
onStepFinish: ({ usage, toolCalls: stepToolCalls, toolResults, content }) => {
|
||||
stepCount++;
|
||||
totalInput += usage?.inputTokens ?? 0;
|
||||
totalOutput += usage?.outputTokens ?? 0;
|
||||
for (const toolCall of stepToolCalls) {
|
||||
recordToolCall(toolCalls, toolCallIndexes, toolCall);
|
||||
}
|
||||
for (const toolResult of toolResults) {
|
||||
recordToolCall(toolCalls, toolCallIndexes, toolResult, { output: (toolResult as any).output });
|
||||
}
|
||||
for (const part of content) {
|
||||
if ((part as any).type === 'tool-error') {
|
||||
recordToolCall(toolCalls, toolCallIndexes, part, { error: (part as any).error });
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -158,12 +238,13 @@ export async function runAgent(options: IAgentRunOptions): Promise<IAgentRunResu
|
||||
const text = await result.text;
|
||||
const finishReason = await result.finishReason;
|
||||
const responseData = await result.response;
|
||||
const responseMessages = responseData.messages as plugins.ModelMessage[];
|
||||
|
||||
attempt = 0; // reset on success
|
||||
|
||||
return {
|
||||
const runResult: IAgentRunResult = {
|
||||
text,
|
||||
messages: responseData.messages as plugins.ModelMessage[],
|
||||
messages: responseMessages,
|
||||
steps: stepCount,
|
||||
finishReason,
|
||||
usage: {
|
||||
@@ -171,7 +252,26 @@ export async function runAgent(options: IAgentRunOptions): Promise<IAgentRunResu
|
||||
outputTokens: totalOutput,
|
||||
totalTokens: totalInput + totalOutput,
|
||||
},
|
||||
toolCalls,
|
||||
};
|
||||
|
||||
if (options.validateCompletion) {
|
||||
const validationPrompt = await options.validateCompletion(runResult);
|
||||
if (typeof validationPrompt === 'string') {
|
||||
if (validationRetries >= (options.maxValidationRetries ?? 0)) {
|
||||
throw new Error(`Agent completion validation failed: ${validationPrompt}`);
|
||||
}
|
||||
validationRetries++;
|
||||
messages = [
|
||||
...messages,
|
||||
...responseMessages,
|
||||
{ role: 'user' as const, content: validationPrompt },
|
||||
];
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return runResult;
|
||||
} catch (err: unknown) {
|
||||
// Abort — don't retry
|
||||
if (err instanceof DOMException && err.name === 'AbortError') throw err;
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
import type { ToolSet, ModelMessage, LanguageModelV3 } from './plugins.js';
|
||||
import type { ToolSet, ModelMessage, LanguageModelV3, ProviderOptions } from './plugins.js';
|
||||
|
||||
export type { ProviderOptions };
|
||||
|
||||
export interface IAgentToolCallRecord {
|
||||
toolName: string;
|
||||
input: unknown;
|
||||
output?: unknown;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export interface IAgentRunOptions {
|
||||
/** The LanguageModelV3 to use — from smartai.getModel() */
|
||||
@@ -9,6 +18,8 @@ export interface IAgentRunOptions {
|
||||
system?: string;
|
||||
/** Tools available to the agent */
|
||||
tools?: ToolSet;
|
||||
/** Provider-specific AI SDK request options passed through to streamText() */
|
||||
providerOptions?: ProviderOptions;
|
||||
/**
|
||||
* Maximum number of LLM↔tool round trips.
|
||||
* Each step may execute multiple tools in parallel.
|
||||
@@ -23,6 +34,13 @@ export interface IAgentRunOptions {
|
||||
onToolCall?: (toolName: string, input: unknown) => void;
|
||||
/** Called when a tool call completes */
|
||||
onToolResult?: (toolName: string, result: unknown) => void;
|
||||
/**
|
||||
* Validate the completed run. Return a string to reject the run and reprompt,
|
||||
* or return void to accept the result.
|
||||
*/
|
||||
validateCompletion?: (result: IAgentRunResult) => Promise<string | void> | string | void;
|
||||
/** Number of validation-triggered reprompts allowed. Default: 0 */
|
||||
maxValidationRetries?: number;
|
||||
/**
|
||||
* Called when total token usage approaches the model's context limit.
|
||||
* Receives the full message history and must return a compacted replacement.
|
||||
@@ -44,6 +62,8 @@ export interface IAgentRunResult {
|
||||
finishReason: string;
|
||||
/** Accumulated token usage across all steps */
|
||||
usage: { inputTokens: number; outputTokens: number; totalTokens: number };
|
||||
/** Tool calls observed during the run, including inputs and outputs/errors when available */
|
||||
toolCalls: IAgentToolCallRecord[];
|
||||
}
|
||||
|
||||
export class ContextOverflowError extends Error {
|
||||
|
||||
Reference in New Issue
Block a user