Skip to content

Commit 59fcf30

Browse files
kaizen403dancer
andauthored
fix(ai): make experimental_context required in ToolLoopAgentOnFinishCallback (#12219)
## Summary - Fixes type inconsistency where `ToolLoopAgentOnFinishCallback` had `experimental_context` as optional (`?`) while `StreamTextOnFinishCallback` and `GenerateTextOnFinishCallback` had it as required - Adds type tests to verify callback type compatibility between agent and streamText/generateText callbacks - Updates JSDoc comment to use "tool execution" wording for consistency with other callbacks ## Problem As reported in #12195, `ToolLoopAgentOnFinishCallback` defines `experimental_context?: unknown` (optional), but `StreamTextOnFinishCallback` and `GenerateTextOnFinishCallback` define `experimental_context: unknown` (required). Since `ToolLoopAgent` delegates to `streamText`/`generateText`, and both always pass `experimental_context` when invoking the callback ([stream-text.ts#L1011](https://github.com/vercel/ai/blob/main/packages/ai/src/generate-text/stream-text.ts#L1011), [generate-text.ts#L940](https://github.com/vercel/ai/blob/main/packages/ai/src/generate-text/generate-text.ts#L940)), the types should match. This inconsistency causes type errors when trying to use a `StreamTextOnFinishCallback` where `ToolLoopAgentOnFinishCallback` is expected (or vice versa). ## Solution Changed `experimental_context?: unknown` to `experimental_context: unknown` in `ToolLoopAgentOnFinishCallback` to match the other callback types. ## Testing - All 1812 tests pass including new type tests - Added 2 new type tests verifying bidirectional type compatibility between `StreamTextOnFinishCallback` and `ToolLoopAgentOnFinishCallback` - Full type check passes with no errors Fixes #12195 --------- Co-authored-by: kaizen403 <kaizen403@users.noreply.github.com> Co-authored-by: ‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌ <144584931+dancer@users.noreply.github.com> Co-authored-by: josh <josh@afterima.ge>
1 parent 5bba028 commit 59fcf30

File tree

3 files changed

+36
-3
lines changed

3 files changed

+36
-3
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'ai': patch
3+
---
4+
5+
fix(ai): make experimental_context required in ToolLoopAgentOnFinishCallback
6+
7+
This fixes a type inconsistency where `ToolLoopAgentOnFinishCallback` had `experimental_context` as optional while `StreamTextOnFinishCallback` and `GenerateTextOnFinishCallback` had it as required. Since `ToolLoopAgent` delegates to `streamText`/`generateText`, and both always pass `experimental_context` when invoking the callback, the types should match.

packages/ai/src/agent/tool-loop-agent-on-finish-callback.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ export type ToolLoopAgentOnFinishCallback<TOOLS extends ToolSet = {}> = (
2020
readonly totalUsage: LanguageModelUsage;
2121

2222
/**
23-
* Context that is passed into tool calls.
23+
* Context that is passed into tool execution.
2424
*
2525
* Experimental (can break in patch releases).
2626
*
2727
* @default undefined
2828
*/
29-
experimental_context?: unknown;
29+
experimental_context: unknown;
3030
},
3131
) => PromiseLike<void> | void;

packages/ai/src/agent/tool-loop-agent.test-d.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,39 @@
11
import { describe, expectTypeOf, it } from 'vitest';
22
import { z } from 'zod';
3-
import { Output } from '../generate-text';
3+
import { Output, StreamTextOnFinishCallback } from '../generate-text';
44
import { MockLanguageModelV3 } from '../test/mock-language-model-v3';
55
import { AsyncIterableStream } from '../util/async-iterable-stream';
66
import { DeepPartial } from '../util/deep-partial';
77
import { AgentCallParameters, AgentStreamParameters } from './agent';
88
import { ToolLoopAgent } from './tool-loop-agent';
9+
import { ToolLoopAgentOnFinishCallback } from './tool-loop-agent-on-finish-callback';
910

1011
describe('ToolLoopAgent', () => {
12+
describe('onFinish callback type compatibility', () => {
13+
it('should allow StreamTextOnFinishCallback where ToolLoopAgentOnFinishCallback is expected', () => {
14+
const streamTextCallback: StreamTextOnFinishCallback<{}> =
15+
async event => {
16+
const context: unknown = event.experimental_context;
17+
context;
18+
};
19+
20+
expectTypeOf(streamTextCallback).toMatchTypeOf<
21+
ToolLoopAgentOnFinishCallback<{}>
22+
>();
23+
});
24+
25+
it('should allow ToolLoopAgentOnFinishCallback where StreamTextOnFinishCallback is expected', () => {
26+
const agentCallback: ToolLoopAgentOnFinishCallback<{}> = async event => {
27+
const context: unknown = event.experimental_context;
28+
context;
29+
};
30+
31+
expectTypeOf(agentCallback).toMatchTypeOf<
32+
StreamTextOnFinishCallback<{}>
33+
>();
34+
});
35+
});
36+
1137
describe('generate', () => {
1238
it('should not allow system prompt', async () => {
1339
const agent = new ToolLoopAgent({

0 commit comments

Comments
 (0)