feat(core-agent): bump xsai / remove patches (#2164)

## Summary

- Bump catalog `@xsai/*`, `@xsai-ext/providers`, and `xsschema` to
**0.5.0-beta.8**, and delete the three `@xsai/*` pnpm patches — the
beta.2 ones from #1602 and the beta.8 regenerations that landed on main
in `38a008500`.
- Always capture tool failures on the core-agent chat path: xsAI beta.8
marks failed tool executions with `isError: true` on `tool-result.done`,
and `llm-service.ts` maps that to AIRI's `tool-error` event via
`toAiriStreamEvent`, so the agent loop continues instead of aborting.
- Remove the `captureToolErrors` request flag (and stop forwarding it
into `streamText`).
- Rebased onto latest `main` (`b230e16b2`). Includes one follow-up fix:
steps are marked settled before the finish listener runs, and finish
listener failures still reject the stream.

### Related

- Supersedes / follows up on
[#1602](https://github.com/moeru-ai/airi/pull/1602) (`captureToolErrors`
+ xsai patches).

### Scope of capture

| Case | Covered |
| --- | --- |
| A — unknown tool | yes |
| B — invalid / unparseable arguments JSON | yes |
| C — `validate` failure | yes |
| D — `execute` throw | yes |
| `missing_name` / `missing_arguments` | no (xsai still aborts) |
| `repairToolCall` | no |

Error copy on beta.8: `Tool "<toolName>" execution failed: …` (produced
by xsAI).

## Test plan

### Automated (Vitest)

- [x] core-agent `llm-service.test.ts` — 16/16
- [x] core-agent full suite — 82/82
- [x] stage-ui `llm.test.ts` + `chat.contract.test.ts` — 44/44 (the
previous `stepsSettled` timing failure is fixed in this branch)
- [x] stage-ui full suite — 594 passed / 0 failed (one
browser-test-runner teardown error, not a test failure)
- [x] typecheck — core-agent, stage-ui, component-calling, satori-bot
pass; telegram-bot fails only at `src/utils/velin.ts`, which is
pre-existing on main and untouched by this PR

### Real-environment E2E (rebase branch, DeepSeek V4 Flash via DeepSeek
API)

Harness: `/Users/lulu/GitHub/airi-e2e/pr2164/tool-error-e2e-rebase.mjs`
— drives the built `core-agent` `streamFrom` with a deliberately failing
tool.

| Case | Result | Evidence |
| --- | --- | --- |
| D — execute throw | **pass** | `tool-error` carried `Tool
"always_fail" execution failed: boom: deterministic tool failure`; the
model answered: "The always_fail tool threw a deterministic error as
expected." |
| A — unknown tool | **pass** | The model called the unavailable
`search_the_moon_database` after being told truthfully that this tests
AIRI's error capture; runtime returned `tool-error` and the conversation
continued. |
| B — bad arguments JSON | not observed on real model | providers rarely
emit invalid `arguments`; covered by unit test |
| C — `validate` failure | pass (earlier manual run with a temporary
validate-gated tool) | — |

Evidence artifacts:
`/Users/lulu/GitHub/airi-e2e/artifacts/pr2164/tool-error-e2e-2026-08-10T16-49-15-384Z.{json,log}`

### Notes / non-goals

- Fallout-only updates for the xsai beta.8 API rename: `textStream`,
`inputTokens` / `outputTokens` / `totalTokens` in component-calling /
telegram / satori.

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Lulu
2026-08-11 17:51:15 +08:00
committed by GitHub
co-authored by Cursor autofix-ci[bot]
parent b230e16b2e
commit fd8b7a0ae3
18 changed files with 219 additions and 1034 deletions
@@ -1,39 +0,0 @@
diff --git a/dist/index.d.ts b/dist/index.d.ts
index c9cc51d8b715b6f220d67574b8c674247d032926..9b4c2e6b096a0a139792d3fd4ce89c2f15bfc51e 100644
--- a/dist/index.d.ts
+++ b/dist/index.d.ts
@@ -1,11 +1,15 @@
import { WithUnknown } from '@xsai/shared';
-import { ChatOptions, CompletionStep, PostToolCall, PrepareStep, PreToolCall, StopCondition, Message, Usage, FinishReason, AssistantMessage, ChatCompletionUsage, CompletionToolCall, CompletionToolResult } from '@xsai/shared-chat';
+import { ChatOptions, CompletionStep, OnToolCallFinishCallback, OnToolCallStartCallback, PostToolCall, PrepareStep, PreToolCall, RepairToolCallFunction, StopCondition, Message, Usage, FinishReason, AssistantMessage, ChatCompletionUsage, CompletionToolCall, CompletionToolResult } from '@xsai/shared-chat';
interface GenerateTextOptions extends ChatOptions {
onStepFinish?: (step: CompletionStep<true>) => Promise<unknown> | unknown;
+ captureToolErrors?: boolean;
+ onToolCallFinish?: OnToolCallFinishCallback;
+ onToolCallStart?: OnToolCallStartCallback;
postToolCall?: PostToolCall;
prepareStep?: PrepareStep;
preToolCall?: PreToolCall;
+ repairToolCall?: RepairToolCallFunction;
/** @internal */
steps?: CompletionStep<true>[];
/** @default `stepCountAtLeast(1)` */
diff --git a/dist/index.js b/dist/index.js
index d4fdab8b13f83fc2eb55eca0d15919133347393e..334428415f242fc6b11c874e988bc98187c3eadc 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -55,9 +55,13 @@ const rawGenerateText = async (options) => {
const results = await Promise.all(
msgToolCalls.map(async (toolCall) => executeTool({
abortSignal: options.abortSignal,
+ captureToolErrors: options.captureToolErrors,
messages,
+ onToolCallFinish: options.onToolCallFinish,
+ onToolCallStart: options.onToolCallStart,
postToolCall: options.postToolCall,
preToolCall: options.preToolCall,
+ repairToolCall: options.repairToolCall,
toolCall,
tools: options.tools
}))
@@ -1,211 +0,0 @@
diff --git a/dist/index.d.ts b/dist/index.d.ts
index b327ec1c9dda3a8c4ddf39beb6a7b4ffc6afb860..8f740ffc45080cf6b80155e62fa87b090aa8efff 100644
--- a/dist/index.d.ts
+++ b/dist/index.d.ts
@@ -94,6 +94,7 @@ interface CompletionToolCall {
}
interface CompletionToolResult {
args: unknown;
+ error?: unknown;
isError?: boolean;
result: ToolExecuteResult;
toolCallId: string;
@@ -101,6 +102,24 @@ interface CompletionToolResult {
}
type PostToolCall = (toolResult: CompletionToolResult, options: ToolExecuteOptions) => CompletionToolResult | Promise<CompletionToolResult | void> | void;
type PreToolCall = (toolCall: CompletionToolCall, options: ToolExecuteOptions) => CompletionToolCall | CompletionToolResult | Promise<CompletionToolCall | CompletionToolResult | void> | void;
+type OnToolCallFinishCallback = (context: {
+ durationMs: number;
+ error?: unknown;
+ output?: unknown;
+ toolCallId: string;
+ toolName: string;
+}) => Promise<void> | void;
+type OnToolCallStartCallback = (context: {
+ input: unknown;
+ toolCallId: string;
+ toolName: string;
+}) => Promise<void> | void;
+type RepairToolCallFunction = (context: {
+ error: Error;
+ messages: Message[];
+ toolCall: ToolCall;
+ tools?: Tool[];
+}) => Promise<ToolCall | null> | ToolCall | null;
interface Tool {
execute: (input: unknown, options: ToolExecuteOptions) => Promise<ToolExecuteResult> | ToolExecuteResult;
function: {
@@ -275,9 +294,13 @@ declare const chat: <T extends WithUnknown<ChatOptions>>(options: T) => Promise<
interface ExecuteToolOptions<T = ToolMessage['content']> {
abortSignal?: AbortSignal;
+ captureToolErrors?: boolean;
messages: Message[];
+ onToolCallFinish?: OnToolCallFinishCallback;
+ onToolCallStart?: OnToolCallStartCallback;
postToolCall?: PostToolCall;
preToolCall?: PreToolCall;
+ repairToolCall?: RepairToolCallFunction;
toolCall: ToolCall;
tools?: Tool[];
wrapResult?: (result: ToolExecuteResult) => T;
@@ -285,10 +308,11 @@ interface ExecuteToolOptions<T = ToolMessage['content']> {
interface ExecuteToolResult<T = ToolMessage['content']> {
completionToolCall: CompletionToolCall;
completionToolResult: CompletionToolResult;
+ message: ToolMessage;
result: T;
}
declare const toCompletionToolCall: (toolCall: ToolCall) => CompletionToolCall;
-declare const executeTool: <T = ToolMessage["content"]>({ abortSignal, messages, postToolCall, preToolCall, toolCall, tools, wrapResult }: ExecuteToolOptions<T>) => Promise<ExecuteToolResult<T>>;
+declare const executeTool: <T = ToolMessage["content"]>(options: ExecuteToolOptions<T>) => Promise<ExecuteToolResult<T>>;
interface ResolvePrepareStepOptions<TInput = Message[], TToolChoice = ToolChoice> {
input: TInput;
@@ -317,4 +341,4 @@ declare const computeTotalUsage: (totalUsage: undefined | Usage, usage: Usage) =
declare const normalizeChatCompletionUsage: (usage: ChatCompletionUsage) => Usage;
export { and, chat, computeTotalUsage, executeTool, hasToolCall, normalizeChatCompletionUsage, not, or, resolvePrepareStep, shouldStop, stepCountAtLeast, toCompletionToolCall };
-export type { AssistantMessage, AudioContentPart, ChatCompletionUsage, ChatOptions, CommonContentPart, CompletionStep, CompletionToolCall, CompletionToolResult, DeveloperMessage, ErrorEvent, Event, EventType, ExecuteToolOptions, ExecuteToolResult, FileContentPart, FinishReason, ImageContentPart, Message, PostToolCall, PreToolCall, PrepareStep, PrepareStepOptions, PrepareStepResult, ReasoningDeltaEvent, ReasoningDoneEvent, ReasoningStartEvent, RefusalContentPart, ResolvePrepareStepOptions, ResolvePrepareStepResult, StepDoneEvent, StepStartEvent, StopCondition, StopContext, SystemMessage, TextContentPart, TextDeltaEvent, TextDoneEvent, TextStartEvent, Tool, ToolCall, ToolCallDeltaEvent, ToolCallDoneEvent, ToolCallStartEvent, ToolChoice, ToolExecuteOptions, ToolExecuteResult, ToolMessage, ToolResultDoneEvent, ToolValidateFailure, ToolValidateResult, ToolValidateSuccess, Usage, UserMessage };
+export type { AssistantMessage, AudioContentPart, ChatCompletionUsage, ChatOptions, CommonContentPart, CompletionStep, CompletionToolCall, CompletionToolResult, DeveloperMessage, ErrorEvent, Event, EventType, ExecuteToolOptions, ExecuteToolResult, FileContentPart, FinishReason, ImageContentPart, Message, OnToolCallFinishCallback, OnToolCallStartCallback, PostToolCall, PreToolCall, PrepareStep, PrepareStepOptions, PrepareStepResult, RepairToolCallFunction, ReasoningDeltaEvent, ReasoningDoneEvent, ReasoningStartEvent, RefusalContentPart, ResolvePrepareStepOptions, ResolvePrepareStepResult, StepDoneEvent, StepStartEvent, StopCondition, StopContext, SystemMessage, TextContentPart, TextDeltaEvent, TextDoneEvent, TextStartEvent, Tool, ToolCall, ToolCallDeltaEvent, ToolCallDoneEvent, ToolCallStartEvent, ToolChoice, ToolExecuteOptions, ToolExecuteResult, ToolMessage, ToolResultDoneEvent, ToolValidateFailure, ToolValidateResult, ToolValidateSuccess, Usage, UserMessage };
diff --git a/dist/index.js b/dist/index.js
index d8fa061c8fe9ba9b76aafea74f40e27a957b0a17..45373a31d56a3127377789177e28e609c99fca64 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -1,4 +1,4 @@
-import { postJSON, InvalidToolCallError, InvalidToolInputError } from '@xsai/shared';
+import { postJSON, InvalidToolCallError, InvalidToolInputError, ToolExecutionError } from '@xsai/shared';
const chat = async (options) => postJSON("chat/completions", {
...options,
@@ -56,15 +56,19 @@ const parseToolInput = async (tool, input) => {
};
const createErrorToolResult = (toolCall, args, cause, abortSignal) => ({
args,
+ error: cause,
isError: true,
result: `Tool "${toolCall.toolName}" execution failed: ${abortSignal?.aborted === true ? "This operation was aborted" : cause instanceof Error ? cause.message : String(cause)}`,
toolCallId: toolCall.toolCallId,
toolName: toolCall.toolName
});
-const catchToolError = async (toolCall, abortSignal, callback) => {
+const isAbortError = (error, abortSignal) => abortSignal?.aborted === true || error instanceof Error && error.name === "AbortError";
+const catchToolError = async (toolCall, abortSignal, captureToolErrors, callback) => {
try {
return await callback(toolCall);
} catch (cause) {
+ if (isAbortError(cause, abortSignal) || !captureToolErrors)
+ throw cause;
return createErrorToolResult(toolCall, InvalidToolInputError.isInstance(cause) ? cause.toolInput : toolCall.args, cause, abortSignal);
}
};
@@ -90,7 +94,7 @@ const findTool = (tools, toolName, toolCall) => {
}
return tool;
};
-const executeTool = async ({ abortSignal, messages, postToolCall, preToolCall, toolCall, tools, wrapResult }) => {
+const executeToolBase = async ({ abortSignal, captureToolErrors, messages, postToolCall, preToolCall, toolCall, tools, wrapResult }) => {
const wrap = wrapResult ?? toToolMessageContent;
const toolName = toolCall.function.name;
const toolArguments = toolCall.function.arguments;
@@ -120,7 +124,7 @@ const executeTool = async ({ abortSignal, messages, postToolCall, preToolCall, t
let completionToolResult;
let parsedArgs;
let shouldPostToolCall = false;
- const preToolCallResult = await catchToolError(completionToolCall, abortSignal, async (toolCall2) => preToolCall?.(toolCall2, toolExecuteOptions));
+ const preToolCallResult = await catchToolError(completionToolCall, abortSignal, captureToolErrors, async (toolCall2) => preToolCall?.(toolCall2, toolExecuteOptions));
if (preToolCallResult) {
assertSameToolCallId(completionToolCall.toolCallId, preToolCallResult, "preToolCallResult");
if ("result" in preToolCallResult)
@@ -128,13 +132,25 @@ const executeTool = async ({ abortSignal, messages, postToolCall, preToolCall, t
else
completionToolCall = preToolCallResult;
}
- completionToolResult ??= await catchToolError(completionToolCall, abortSignal, async () => {
+ completionToolResult ??= await catchToolError(completionToolCall, abortSignal, captureToolErrors, async () => {
const tool = findTool(tools, completionToolCall.toolName, completionToolCall);
parsedArgs = await parseToolInput(tool, completionToolCall.args);
if (abortSignal?.aborted === true)
return createErrorToolResult(completionToolCall, parsedArgs, abortSignal.reason, abortSignal);
shouldPostToolCall = true;
- const result = await tool.execute(parsedArgs, toolExecuteOptions);
+ let result;
+ try {
+ result = await tool.execute(parsedArgs, toolExecuteOptions);
+ } catch (cause) {
+ if (isAbortError(cause, abortSignal))
+ throw cause;
+ throw new ToolExecutionError(`Tool "${completionToolCall.toolName}" execution failed.`, {
+ cause,
+ toolCallId: completionToolCall.toolCallId,
+ toolInput: parsedArgs,
+ toolName: completionToolCall.toolName
+ });
+ }
return {
args: parsedArgs,
result,
@@ -144,7 +160,7 @@ const executeTool = async ({ abortSignal, messages, postToolCall, preToolCall, t
});
if (shouldPostToolCall) {
completionToolResult.args = parsedArgs;
- const postToolCallResult = await catchToolError(completionToolResult, abortSignal, async (toolResult) => postToolCall?.(toolResult, toolExecuteOptions));
+ const postToolCallResult = await catchToolError(completionToolResult, abortSignal, captureToolErrors, async (toolResult) => postToolCall?.(toolResult, toolExecuteOptions));
if (postToolCallResult) {
assertSameToolCallId(completionToolResult.toolCallId, postToolCallResult, "postToolCallResult");
completionToolResult = postToolCallResult;
@@ -156,6 +172,54 @@ const executeTool = async ({ abortSignal, messages, postToolCall, preToolCall, t
result: wrap(completionToolResult.result)
};
};
+const callToolLifecycle = async (callback, context) => {
+ try {
+ await callback?.(context);
+ } catch {
+ }
+};
+const executeTool = async (options) => {
+ const { abortSignal, captureToolErrors, messages, onToolCallFinish, onToolCallStart, repairToolCall, toolCall, tools } = options;
+ const toolCallId = toolCall.id;
+ const toolName = toolCall.function?.name ?? "unknown";
+ const startTime = Date.now();
+ try {
+ const execution = await executeToolBase(options);
+ const failure = execution.completionToolResult.error;
+ if (failure instanceof Error && repairToolCall && (InvalidToolCallError.isInstance(failure) || InvalidToolInputError.isInstance(failure))) {
+ const repaired = await repairToolCall({ error: failure, messages, toolCall, tools });
+ if (repaired != null)
+ return executeTool({ ...options, repairToolCall: void 0, toolCall: repaired });
+ }
+ if (!execution.completionToolResult.isError)
+ await callToolLifecycle(onToolCallStart, { input: execution.completionToolResult.args, toolCallId, toolName });
+ await callToolLifecycle(onToolCallFinish, {
+ durationMs: Date.now() - startTime,
+ error: execution.completionToolResult.error,
+ output: execution.completionToolResult.isError ? void 0 : execution.completionToolResult.result,
+ toolCallId,
+ toolName
+ });
+ return {
+ ...execution,
+ message: {
+ content: execution.result,
+ role: "tool",
+ tool_call_id: execution.completionToolCall.toolCallId
+ }
+ };
+ } catch (error) {
+ if (isAbortError(error, abortSignal))
+ throw error;
+ if (error instanceof Error && repairToolCall && (InvalidToolCallError.isInstance(error) || InvalidToolInputError.isInstance(error))) {
+ const repaired = await repairToolCall({ error, messages, toolCall, tools });
+ if (repaired != null)
+ return executeTool({ ...options, repairToolCall: void 0, toolCall: repaired });
+ }
+ await callToolLifecycle(onToolCallFinish, { durationMs: Date.now() - startTime, error, toolCallId, toolName });
+ throw error;
+ }
+};
const resolvePrepareStep = async ({ input, model, prepareStep, stepNumber, steps, toolChoice }) => {
const prepared = prepareStep == null ? void 0 : await prepareStep({
@@ -1,200 +0,0 @@
diff --git a/dist/index.d.ts b/dist/index.d.ts
index a596849dec1316dd84e4261650aa6b439327a4f5..b238d42480e4654cdb89d0619540275b7796e80d 100644
--- a/dist/index.d.ts
+++ b/dist/index.d.ts
@@ -1,5 +1,35 @@
import { WithUnknown } from '@xsai/shared';
-import { ToolCall, FinishReason, ChatCompletionUsage, ChatOptions, Event, CompletionStep, PostToolCall, PrepareStep, PreToolCall, StopCondition, Message, Usage } from '@xsai/shared-chat';
+import { CompletionToolCall, CompletionToolResult, ToolCall, FinishReason, ChatCompletionUsage, ChatOptions, CompletionStep, OnToolCallFinishCallback, OnToolCallStartCallback, PostToolCall, PrepareStep, PreToolCall, RepairToolCallFunction, StopCondition, Message, Usage } from '@xsai/shared-chat';
+
+type StreamTextEvent = (CompletionToolCall & {
+ type: 'tool-call';
+}) | (CompletionToolResult & {
+ type: 'tool-error';
+}) | (CompletionToolResult & {
+ type: 'tool-result';
+}) | {
+ argsTextDelta: string;
+ toolCallId: string;
+ toolName: string;
+ type: 'tool-call-delta';
+} | {
+ error: unknown;
+ type: 'error';
+} | {
+ finishReason: FinishReason;
+ type: 'finish';
+ usage?: Usage;
+} | {
+ text: string;
+ type: 'reasoning-delta';
+} | {
+ text: string;
+ type: 'text-delta';
+} | {
+ toolCallId: string;
+ toolName: string;
+ type: 'tool-call-streaming-start';
+};
interface StreamTextChunkResult {
choices: {
@@ -27,12 +57,16 @@ interface StreamTextChunkResult {
}
interface StreamTextOptions extends ChatOptions {
- onEvent?: (event: Event) => Promise<unknown> | unknown;
+ onEvent?: (event: StreamTextEvent) => Promise<unknown> | unknown;
onFinish?: (step?: CompletionStep) => Promise<unknown> | unknown;
onStepFinish?: (step: CompletionStep) => Promise<unknown> | unknown;
+ captureToolErrors?: boolean;
+ onToolCallFinish?: OnToolCallFinishCallback;
+ onToolCallStart?: OnToolCallStartCallback;
postToolCall?: PostToolCall;
prepareStep?: PrepareStep;
preToolCall?: PreToolCall;
+ repairToolCall?: RepairToolCallFunction;
/** @default `stepCountAtLeast(1)` */
stopWhen?: StopCondition<Message>;
/**
@@ -48,8 +82,8 @@ interface StreamTextOptions extends ChatOptions {
};
}
interface StreamTextResult {
- eventStream: ReadableStream<Event>;
- fullStream: ReadableStream<StreamTextChunkResult>;
+ eventStream: ReadableStream<StreamTextEvent>;
+ fullStream: ReadableStream<StreamTextEvent>;
messages: Promise<Message[]>;
reasoningTextStream: ReadableStream<string>;
steps: Promise<CompletionStep[]>;
@@ -60,4 +94,4 @@ interface StreamTextResult {
declare const streamText: (options: WithUnknown<StreamTextOptions>) => StreamTextResult;
export { streamText };
-export type { StreamTextChunkResult, StreamTextOptions, StreamTextResult };
+export type { StreamTextChunkResult, StreamTextEvent, StreamTextOptions, StreamTextResult };
diff --git a/dist/index.js b/dist/index.js
index ec6e01b0defd49370f75ed50d975aa6da8c03faf..4a956cc07f62049de4fe2ea10ba8c3fba2875ecb 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -63,7 +63,6 @@ const streamText = (options) => {
let finishReason = "other";
let reasoningStarted = false;
let textStarted = false;
- pushEvent({ type: "step.start" });
await stream.pipeThrough(new TextDecoderStream()).pipeThrough(new EventSourceParserStream()).pipeThrough(new JsonMessageTransformStream()).pipeTo(new WritableStream({
abort: (reason) => {
errorControllers(reason, eventCtrl, fullCtrl, textCtrl, reasoningTextCtrl);
@@ -82,18 +81,16 @@ const streamText = (options) => {
reasoningField = "reasoning";
if (!reasoningStarted) {
reasoningStarted = true;
- pushEvent({ type: "reasoning.start" });
}
- pushEvent({ delta: choice.delta.reasoning, type: "reasoning.delta" });
+ pushEvent({ text: choice.delta.reasoning, type: "reasoning-delta" });
pushReasoningText(choice.delta.reasoning);
} else if (choice.delta.reasoning_content != null) {
if (reasoningField !== "reasoning_content")
reasoningField = "reasoning_content";
if (!reasoningStarted) {
reasoningStarted = true;
- pushEvent({ type: "reasoning.start" });
}
- pushEvent({ delta: choice.delta.reasoning_content, type: "reasoning.delta" });
+ pushEvent({ text: choice.delta.reasoning_content, type: "reasoning-delta" });
pushReasoningText(choice.delta.reasoning_content);
}
if (choice.finish_reason != null)
@@ -102,16 +99,14 @@ const streamText = (options) => {
if (choice.delta.content != null) {
if (!textStarted) {
textStarted = true;
- pushEvent({ type: "text.start" });
}
- pushEvent({ delta: choice.delta.content, type: "text.delta" });
+ pushEvent({ text: choice.delta.content, type: "text-delta" });
pushText(choice.delta.content);
} else if (choice.delta.refusal != null) {
if (!textStarted) {
textStarted = true;
- pushEvent({ type: "text.start" });
}
- pushEvent({ delta: choice.delta.refusal, type: "text.delta" });
+ pushEvent({ text: choice.delta.refusal, type: "text-delta" });
pushText(choice.delta.refusal);
}
} else {
@@ -125,21 +120,17 @@ const streamText = (options) => {
arguments: toolCall.function.arguments ?? ""
}
};
- pushEvent({ toolCallId: toolCall.id, toolName: toolCall.function.name, type: "tool-call.start" });
+ pushEvent({ toolCallId: toolCall.id, toolName: toolCall.function.name, type: "tool-call-streaming-start" });
if (toolCall.function.arguments != null && toolCall.function.arguments.length > 0)
- pushEvent({ delta: toolCall.function.arguments, type: "tool-call.delta" });
+ pushEvent({ argsTextDelta: toolCall.function.arguments, toolCallId: toolCall.id, toolName: toolCall.function.name, type: "tool-call-delta" });
} else {
tool_calls[index].function.arguments += toolCall.function.arguments;
- pushEvent({ delta: toolCall.function.arguments, type: "tool-call.delta" });
+ pushEvent({ argsTextDelta: toolCall.function.arguments, toolCallId: toolCall.id, toolName: toolCall.function.name ?? tool_calls[index].function.name, type: "tool-call-delta" });
}
}
}
}
}));
- if (reasoningStarted)
- pushEvent({ content: reasoningText ?? "", type: "reasoning.done" });
- if (textStarted)
- pushEvent({ content: text, type: "text.done" });
messages.push({
...reasoningField != null ? { [reasoningField]: reasoningText } : {},
content: text,
@@ -152,7 +143,7 @@ const streamText = (options) => {
if (options.abortSignal?.aborted === true)
throw options.abortSignal.reason ?? new Error("This operation was aborted");
for (const toolCall of toolCalls)
- pushEvent({ ...toolCall, type: "tool-call.done" });
+ pushEvent({ ...toolCall, type: "tool-call" });
const step = {
finishReason,
text,
@@ -169,9 +160,13 @@ const streamText = (options) => {
const results = await Promise.all(
validToolCalls.map(async (toolCall) => executeTool({
abortSignal: options.abortSignal,
+ captureToolErrors: options.captureToolErrors,
messages,
+ onToolCallFinish: options.onToolCallFinish,
+ onToolCallStart: options.onToolCallStart,
postToolCall: options.postToolCall,
preToolCall: options.preToolCall,
+ repairToolCall: options.repairToolCall,
toolCall,
tools: options.tools
}))
@@ -185,12 +180,12 @@ const streamText = (options) => {
role: "tool",
tool_call_id: completionToolCall.toolCallId
});
- pushEvent({ ...completionToolResult, type: "tool-result.done" });
+ pushEvent({ ...completionToolResult, type: completionToolResult.isError ? "tool-error" : "tool-result" });
}
}
const willContinue = validToolCalls.length > 0 && !stop && !options.abortSignal?.aborted;
pushStep(step);
- pushEvent({ type: "step.done", usage });
+ pushEvent({ finishReason, type: "finish", usage });
if (willContinue)
return async () => doStream();
};
@@ -222,7 +217,7 @@ const streamText = (options) => {
})();
return {
eventStream,
- fullStream,
+ fullStream: eventStream,
messages: resultMessages.promise,
reasoningTextStream,
steps: resultSteps.promise,