@ai-sdk/provider-utils 5.0.0-beta.25 → 5.0.0-beta.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,9 +9,10 @@ declare function convertReadableStreamToArray<T>(stream: ReadableStream<T>): Pro
9
9
  declare function convertResponseStreamToArray(response: Response): Promise<string[]>;
10
10
 
11
11
  declare function isNodeVersion(version: number): boolean;
12
+ declare function isNodeVersionAtLeast(major: number, minor?: number, patch?: number): boolean;
12
13
 
13
14
  declare function mockId({ prefix, }?: {
14
15
  prefix?: string;
15
16
  }): () => string;
16
17
 
17
- export { convertArrayToAsyncIterable, convertArrayToReadableStream, convertAsyncIterableToArray, convertReadableStreamToArray, convertResponseStreamToArray, isNodeVersion, mockId };
18
+ export { convertArrayToAsyncIterable, convertArrayToReadableStream, convertAsyncIterableToArray, convertReadableStreamToArray, convertResponseStreamToArray, isNodeVersion, isNodeVersionAtLeast, mockId };
@@ -47,16 +47,29 @@ async function convertReadableStreamToArray(stream) {
47
47
 
48
48
  // src/test/convert-response-stream-to-array.ts
49
49
  async function convertResponseStreamToArray(response) {
50
- return convertReadableStreamToArray(
50
+ return await convertReadableStreamToArray(
51
51
  response.body.pipeThrough(new TextDecoderStream())
52
52
  );
53
53
  }
54
54
 
55
55
  // src/test/is-node-version.ts
56
+ function getNodeVersionParts() {
57
+ return process.versions.node.split(".").map((version) => Number.parseInt(version, 10));
58
+ }
56
59
  function isNodeVersion(version) {
57
- const nodeMajorVersion = parseInt(process.version.slice(1).split(".")[0], 10);
60
+ const [nodeMajorVersion] = getNodeVersionParts();
58
61
  return nodeMajorVersion === version;
59
62
  }
63
+ function isNodeVersionAtLeast(major, minor = 0, patch = 0) {
64
+ const [nodeMajorVersion, nodeMinorVersion, nodePatchVersion] = getNodeVersionParts();
65
+ if (nodeMajorVersion !== major) {
66
+ return nodeMajorVersion > major;
67
+ }
68
+ if (nodeMinorVersion !== minor) {
69
+ return nodeMinorVersion > minor;
70
+ }
71
+ return nodePatchVersion >= patch;
72
+ }
60
73
 
61
74
  // src/test/mock-id.ts
62
75
  function mockId({
@@ -72,6 +85,7 @@ export {
72
85
  convertReadableStreamToArray,
73
86
  convertResponseStreamToArray,
74
87
  isNodeVersion,
88
+ isNodeVersionAtLeast,
75
89
  mockId
76
90
  };
77
91
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/test/convert-array-to-async-iterable.ts","../../src/test/convert-array-to-readable-stream.ts","../../src/test/convert-async-iterable-to-array.ts","../../src/test/convert-readable-stream-to-array.ts","../../src/test/convert-response-stream-to-array.ts","../../src/test/is-node-version.ts","../../src/test/mock-id.ts"],"sourcesContent":["export function convertArrayToAsyncIterable<T>(values: T[]): AsyncIterable<T> {\n return {\n async *[Symbol.asyncIterator]() {\n for (const value of values) {\n yield value;\n }\n },\n };\n}\n","export function convertArrayToReadableStream<T>(\n values: T[],\n): ReadableStream<T> {\n return new ReadableStream({\n start(controller) {\n try {\n for (const value of values) {\n controller.enqueue(value);\n }\n } finally {\n controller.close();\n }\n },\n });\n}\n","export async function convertAsyncIterableToArray<T>(\n iterable: AsyncIterable<T>,\n): Promise<T[]> {\n const result: T[] = [];\n for await (const item of iterable) {\n result.push(item);\n }\n return result;\n}\n","export async function convertReadableStreamToArray<T>(\n stream: ReadableStream<T>,\n): Promise<T[]> {\n const reader = stream.getReader();\n const result: T[] = [];\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n result.push(value);\n }\n\n return result;\n}\n","import { convertReadableStreamToArray } from './convert-readable-stream-to-array';\n\nexport async function convertResponseStreamToArray(\n response: Response,\n): Promise<string[]> {\n return convertReadableStreamToArray(\n response.body!.pipeThrough(new TextDecoderStream()),\n );\n}\n","export function isNodeVersion(version: number) {\n const nodeMajorVersion = parseInt(process.version.slice(1).split('.')[0], 10);\n return nodeMajorVersion === version;\n}\n","export function mockId({\n prefix = 'id',\n}: {\n prefix?: string;\n} = {}): () => string {\n let counter = 0;\n return () => `${prefix}-${counter++}`;\n}\n"],"mappings":";AAAO,SAAS,4BAA+B,QAA+B;AAC5E,SAAO;AAAA,IACL,QAAQ,OAAO,aAAa,IAAI;AAC9B,iBAAW,SAAS,QAAQ;AAC1B,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;;;ACRO,SAAS,6BACd,QACmB;AACnB,SAAO,IAAI,eAAe;AAAA,IACxB,MAAM,YAAY;AAChB,UAAI;AACF,mBAAW,SAAS,QAAQ;AAC1B,qBAAW,QAAQ,KAAK;AAAA,QAC1B;AAAA,MACF,UAAE;AACA,mBAAW,MAAM;AAAA,MACnB;AAAA,IACF;AAAA,EACF,CAAC;AACH;;;ACdA,eAAsB,4BACpB,UACc;AACd,QAAM,SAAc,CAAC;AACrB,mBAAiB,QAAQ,UAAU;AACjC,WAAO,KAAK,IAAI;AAAA,EAClB;AACA,SAAO;AACT;;;ACRA,eAAsB,6BACpB,QACc;AACd,QAAM,SAAS,OAAO,UAAU;AAChC,QAAM,SAAc,CAAC;AAErB,SAAO,MAAM;AACX,UAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,QAAI,KAAM;AACV,WAAO,KAAK,KAAK;AAAA,EACnB;AAEA,SAAO;AACT;;;ACXA,eAAsB,6BACpB,UACmB;AACnB,SAAO;AAAA,IACL,SAAS,KAAM,YAAY,IAAI,kBAAkB,CAAC;AAAA,EACpD;AACF;;;ACRO,SAAS,cAAc,SAAiB;AAC7C,QAAM,mBAAmB,SAAS,QAAQ,QAAQ,MAAM,CAAC,EAAE,MAAM,GAAG,EAAE,CAAC,GAAG,EAAE;AAC5E,SAAO,qBAAqB;AAC9B;;;ACHO,SAAS,OAAO;AAAA,EACrB,SAAS;AACX,IAEI,CAAC,GAAiB;AACpB,MAAI,UAAU;AACd,SAAO,MAAM,GAAG,MAAM,IAAI,SAAS;AACrC;","names":[]}
1
+ {"version":3,"sources":["../../src/test/convert-array-to-async-iterable.ts","../../src/test/convert-array-to-readable-stream.ts","../../src/test/convert-async-iterable-to-array.ts","../../src/test/convert-readable-stream-to-array.ts","../../src/test/convert-response-stream-to-array.ts","../../src/test/is-node-version.ts","../../src/test/mock-id.ts"],"sourcesContent":["export function convertArrayToAsyncIterable<T>(values: T[]): AsyncIterable<T> {\n return {\n async *[Symbol.asyncIterator]() {\n for (const value of values) {\n yield value;\n }\n },\n };\n}\n","export function convertArrayToReadableStream<T>(\n values: T[],\n): ReadableStream<T> {\n return new ReadableStream({\n start(controller) {\n try {\n for (const value of values) {\n controller.enqueue(value);\n }\n } finally {\n controller.close();\n }\n },\n });\n}\n","export async function convertAsyncIterableToArray<T>(\n iterable: AsyncIterable<T>,\n): Promise<T[]> {\n const result: T[] = [];\n for await (const item of iterable) {\n result.push(item);\n }\n return result;\n}\n","export async function convertReadableStreamToArray<T>(\n stream: ReadableStream<T>,\n): Promise<T[]> {\n const reader = stream.getReader();\n const result: T[] = [];\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n result.push(value);\n }\n\n return result;\n}\n","import { convertReadableStreamToArray } from './convert-readable-stream-to-array';\n\nexport async function convertResponseStreamToArray(\n response: Response,\n): Promise<string[]> {\n return await convertReadableStreamToArray(\n response.body!.pipeThrough(new TextDecoderStream()),\n );\n}\n","function getNodeVersionParts() {\n return process.versions.node\n .split('.')\n .map(version => Number.parseInt(version, 10));\n}\n\nexport function isNodeVersion(version: number) {\n const [nodeMajorVersion] = getNodeVersionParts();\n return nodeMajorVersion === version;\n}\n\nexport function isNodeVersionAtLeast(major: number, minor = 0, patch = 0) {\n const [nodeMajorVersion, nodeMinorVersion, nodePatchVersion] =\n getNodeVersionParts();\n\n if (nodeMajorVersion !== major) {\n return nodeMajorVersion > major;\n }\n\n if (nodeMinorVersion !== minor) {\n return nodeMinorVersion > minor;\n }\n\n return nodePatchVersion >= patch;\n}\n","export function mockId({\n prefix = 'id',\n}: {\n prefix?: string;\n} = {}): () => string {\n let counter = 0;\n return () => `${prefix}-${counter++}`;\n}\n"],"mappings":";AAAO,SAAS,4BAA+B,QAA+B;AAC5E,SAAO;AAAA,IACL,QAAQ,OAAO,aAAa,IAAI;AAC9B,iBAAW,SAAS,QAAQ;AAC1B,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;;;ACRO,SAAS,6BACd,QACmB;AACnB,SAAO,IAAI,eAAe;AAAA,IACxB,MAAM,YAAY;AAChB,UAAI;AACF,mBAAW,SAAS,QAAQ;AAC1B,qBAAW,QAAQ,KAAK;AAAA,QAC1B;AAAA,MACF,UAAE;AACA,mBAAW,MAAM;AAAA,MACnB;AAAA,IACF;AAAA,EACF,CAAC;AACH;;;ACdA,eAAsB,4BACpB,UACc;AACd,QAAM,SAAc,CAAC;AACrB,mBAAiB,QAAQ,UAAU;AACjC,WAAO,KAAK,IAAI;AAAA,EAClB;AACA,SAAO;AACT;;;ACRA,eAAsB,6BACpB,QACc;AACd,QAAM,SAAS,OAAO,UAAU;AAChC,QAAM,SAAc,CAAC;AAErB,SAAO,MAAM;AACX,UAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,QAAI,KAAM;AACV,WAAO,KAAK,KAAK;AAAA,EACnB;AAEA,SAAO;AACT;;;ACXA,eAAsB,6BACpB,UACmB;AACnB,SAAO,MAAM;AAAA,IACX,SAAS,KAAM,YAAY,IAAI,kBAAkB,CAAC;AAAA,EACpD;AACF;;;ACRA,SAAS,sBAAsB;AAC7B,SAAO,QAAQ,SAAS,KACrB,MAAM,GAAG,EACT,IAAI,aAAW,OAAO,SAAS,SAAS,EAAE,CAAC;AAChD;AAEO,SAAS,cAAc,SAAiB;AAC7C,QAAM,CAAC,gBAAgB,IAAI,oBAAoB;AAC/C,SAAO,qBAAqB;AAC9B;AAEO,SAAS,qBAAqB,OAAe,QAAQ,GAAG,QAAQ,GAAG;AACxE,QAAM,CAAC,kBAAkB,kBAAkB,gBAAgB,IACzD,oBAAoB;AAEtB,MAAI,qBAAqB,OAAO;AAC9B,WAAO,mBAAmB;AAAA,EAC5B;AAEA,MAAI,qBAAqB,OAAO;AAC9B,WAAO,mBAAmB;AAAA,EAC5B;AAEA,SAAO,oBAAoB;AAC7B;;;ACxBO,SAAS,OAAO;AAAA,EACrB,SAAS;AACX,IAEI,CAAC,GAAiB;AACpB,MAAI,UAAU;AACd,SAAO,MAAM,GAAG,MAAM,IAAI,SAAS;AACrC;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/provider-utils",
3
- "version": "5.0.0-beta.25",
3
+ "version": "5.0.0-beta.27",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -34,7 +34,7 @@
34
34
  "dependencies": {
35
35
  "@standard-schema/spec": "^1.1.0",
36
36
  "@workflow/serde": "4.1.0",
37
- "eventsource-parser": "^3.0.6",
37
+ "eventsource-parser": "^3.0.8",
38
38
  "@ai-sdk/provider": "4.0.0-beta.12"
39
39
  },
40
40
  "devDependencies": {
@@ -52,7 +52,8 @@
52
52
  "node": ">=18"
53
53
  },
54
54
  "publishConfig": {
55
- "access": "public"
55
+ "access": "public",
56
+ "provenance": true
56
57
  },
57
58
  "homepage": "https://ai-sdk.dev/docs",
58
59
  "repository": {
package/src/index.ts CHANGED
@@ -41,11 +41,15 @@ export { parseJsonEventStream } from './parse-json-event-stream';
41
41
  export { parseProviderOptions } from './parse-provider-options';
42
42
  export * from './post-to-api';
43
43
  export {
44
- createProviderToolFactory,
45
- createProviderToolFactoryWithOutputSchema,
46
- type ProviderToolFactory,
47
- type ProviderToolFactoryWithOutputSchema,
48
- } from './provider-tool-factory';
44
+ createProviderDefinedToolFactory,
45
+ createProviderDefinedToolFactoryWithOutputSchema,
46
+ type ProviderDefinedToolFactory,
47
+ type ProviderDefinedToolFactoryWithOutputSchema,
48
+ } from './provider-defined-tool-factory';
49
+ export {
50
+ createProviderExecutedToolFactory,
51
+ type ProviderExecutedToolFactory,
52
+ } from './provider-executed-tool-factory';
49
53
  export {
50
54
  DEFAULT_MAX_DOWNLOAD_SIZE,
51
55
  readResponseWithSizeLimit,
@@ -1,3 +1,6 @@
1
+ /**
2
+ * A value that can be provided either synchronously or as a promise-like.
3
+ */
1
4
  export type MaybePromiseLike<T> =
2
5
  | T // Raw value
3
6
  | PromiseLike<T>; // Promise of value
package/src/parse-json.ts CHANGED
@@ -43,7 +43,7 @@ export async function parseJSON<T>({
43
43
  return value;
44
44
  }
45
45
 
46
- return validateTypes<T>({ value, schema });
46
+ return await validateTypes<T>({ value, schema });
47
47
  } catch (error) {
48
48
  if (
49
49
  JSONParseError.isInstance(error) ||
@@ -28,7 +28,7 @@ export const postJsonToApi = async <T>({
28
28
  abortSignal?: AbortSignal;
29
29
  fetch?: FetchFunction;
30
30
  }) =>
31
- postToApi({
31
+ await postToApi({
32
32
  url,
33
33
  headers: {
34
34
  'Content-Type': 'application/json',
@@ -61,7 +61,7 @@ export const postFormDataToApi = async <T>({
61
61
  abortSignal?: AbortSignal;
62
62
  fetch?: FetchFunction;
63
63
  }) =>
64
- postToApi({
64
+ await postToApi({
65
65
  url,
66
66
  headers,
67
67
  body: {
@@ -1,8 +1,11 @@
1
1
  import { tool, Tool, ToolExecuteFunction } from './types/tool';
2
2
  import { FlexibleSchema } from './schema';
3
3
  import { Context } from './types/context';
4
-
5
- export type ProviderToolFactory<
4
+ /**
5
+ * A provider-defined tool is a tool for which the provider defines the input
6
+ * and output schemas, but does not execute the tool.
7
+ */
8
+ export type ProviderDefinedToolFactory<
6
9
  INPUT,
7
10
  ARGS extends object,
8
11
  CONTEXT extends Context = {},
@@ -17,7 +20,7 @@ export type ProviderToolFactory<
17
20
  },
18
21
  ) => Tool<INPUT, OUTPUT, CONTEXT>;
19
22
 
20
- export function createProviderToolFactory<
23
+ export function createProviderDefinedToolFactory<
21
24
  INPUT,
22
25
  ARGS extends object,
23
26
  CONTEXT extends Context = {},
@@ -27,7 +30,7 @@ export function createProviderToolFactory<
27
30
  }: {
28
31
  id: `${string}.${string}`;
29
32
  inputSchema: FlexibleSchema<INPUT>;
30
- }): ProviderToolFactory<INPUT, ARGS, CONTEXT> {
33
+ }): ProviderDefinedToolFactory<INPUT, ARGS, CONTEXT> {
31
34
  return <OUTPUT>({
32
35
  execute,
33
36
  outputSchema,
@@ -48,6 +51,7 @@ export function createProviderToolFactory<
48
51
  }): Tool<INPUT, OUTPUT, CONTEXT> =>
49
52
  tool({
50
53
  type: 'provider',
54
+ isProviderExecuted: false,
51
55
  id,
52
56
  args,
53
57
  inputSchema,
@@ -61,7 +65,7 @@ export function createProviderToolFactory<
61
65
  });
62
66
  }
63
67
 
64
- export type ProviderToolFactoryWithOutputSchema<
68
+ export type ProviderDefinedToolFactoryWithOutputSchema<
65
69
  INPUT,
66
70
  OUTPUT,
67
71
  ARGS extends object,
@@ -77,7 +81,7 @@ export type ProviderToolFactoryWithOutputSchema<
77
81
  },
78
82
  ) => Tool<INPUT, OUTPUT, CONTEXT>;
79
83
 
80
- export function createProviderToolFactoryWithOutputSchema<
84
+ export function createProviderDefinedToolFactoryWithOutputSchema<
81
85
  INPUT,
82
86
  OUTPUT,
83
87
  ARGS extends object,
@@ -86,23 +90,11 @@ export function createProviderToolFactoryWithOutputSchema<
86
90
  id,
87
91
  inputSchema,
88
92
  outputSchema,
89
- supportsDeferredResults,
90
93
  }: {
91
94
  id: `${string}.${string}`;
92
95
  inputSchema: FlexibleSchema<INPUT>;
93
96
  outputSchema: FlexibleSchema<OUTPUT>;
94
- /**
95
- * Whether this provider-executed tool supports deferred results.
96
- *
97
- * When true, the tool result may not be returned in the same turn as the
98
- * tool call (e.g., when using programmatic tool calling where a server tool
99
- * triggers a client-executed tool, and the server tool's result is deferred
100
- * until the client tool is resolved).
101
- *
102
- * @default false
103
- */
104
- supportsDeferredResults?: boolean;
105
- }): ProviderToolFactoryWithOutputSchema<INPUT, OUTPUT, ARGS, CONTEXT> {
97
+ }): ProviderDefinedToolFactoryWithOutputSchema<INPUT, OUTPUT, ARGS, CONTEXT> {
106
98
  return ({
107
99
  execute,
108
100
  needsApproval,
@@ -121,6 +113,7 @@ export function createProviderToolFactoryWithOutputSchema<
121
113
  }): Tool<INPUT, OUTPUT, CONTEXT> =>
122
114
  tool({
123
115
  type: 'provider',
116
+ isProviderExecuted: false,
124
117
  id,
125
118
  args,
126
119
  inputSchema,
@@ -131,6 +124,5 @@ export function createProviderToolFactoryWithOutputSchema<
131
124
  onInputStart,
132
125
  onInputDelta,
133
126
  onInputAvailable,
134
- supportsDeferredResults,
135
127
  });
136
128
  }
@@ -0,0 +1,70 @@
1
+ import { FlexibleSchema } from './schema';
2
+ import { Context } from './types/context';
3
+ import { tool, Tool } from './types/tool';
4
+
5
+ /**
6
+ * A provider-executed tool is a tool for which the provider executes the tool.
7
+ */
8
+ export type ProviderExecutedToolFactory<
9
+ INPUT,
10
+ OUTPUT,
11
+ ARGS extends object,
12
+ CONTEXT extends Context = {},
13
+ > = (
14
+ options: ARGS & {
15
+ onInputStart?: Tool<INPUT, OUTPUT, CONTEXT>['onInputStart'];
16
+ onInputDelta?: Tool<INPUT, OUTPUT, CONTEXT>['onInputDelta'];
17
+ onInputAvailable?: Tool<INPUT, OUTPUT, CONTEXT>['onInputAvailable'];
18
+ },
19
+ ) => Tool<INPUT, OUTPUT, CONTEXT>;
20
+
21
+ export function createProviderExecutedToolFactory<
22
+ INPUT,
23
+ OUTPUT,
24
+ ARGS extends object,
25
+ CONTEXT extends Context = {},
26
+ >({
27
+ id,
28
+ inputSchema,
29
+ outputSchema,
30
+ supportsDeferredResults,
31
+ }: {
32
+ id: `${string}.${string}`;
33
+ inputSchema: FlexibleSchema<INPUT>;
34
+ outputSchema: FlexibleSchema<OUTPUT>;
35
+
36
+ /**
37
+ * Whether this provider-executed tool supports deferred results.
38
+ *
39
+ * When true, the tool result may not be returned in the same turn as the
40
+ * tool call (e.g., when using programmatic tool calling where a server tool
41
+ * triggers a client-executed tool, and the server tool's result is deferred
42
+ * until the client tool is resolved).
43
+ *
44
+ * @default false
45
+ */
46
+ supportsDeferredResults?: boolean;
47
+ }): ProviderExecutedToolFactory<INPUT, OUTPUT, ARGS, CONTEXT> {
48
+ return ({
49
+ onInputStart,
50
+ onInputDelta,
51
+ onInputAvailable,
52
+ ...args
53
+ }: ARGS & {
54
+ onInputStart?: Tool<INPUT, OUTPUT, CONTEXT>['onInputStart'];
55
+ onInputDelta?: Tool<INPUT, OUTPUT, CONTEXT>['onInputDelta'];
56
+ onInputAvailable?: Tool<INPUT, OUTPUT, CONTEXT>['onInputAvailable'];
57
+ }): Tool<INPUT, OUTPUT, CONTEXT> =>
58
+ tool({
59
+ type: 'provider',
60
+ isProviderExecuted: true,
61
+ id,
62
+ args,
63
+ inputSchema,
64
+ outputSchema,
65
+ onInputStart,
66
+ onInputDelta,
67
+ onInputAvailable,
68
+ supportsDeferredResults,
69
+ });
70
+ }
@@ -19,7 +19,9 @@ export interface StreamingToolCallDelta {
19
19
  } | null;
20
20
  }
21
21
 
22
- export interface StreamingToolCallTrackerOptions {
22
+ export interface StreamingToolCallTrackerOptions<
23
+ DELTA extends StreamingToolCallDelta = StreamingToolCallDelta,
24
+ > {
23
25
  /**
24
26
  * ID generator function for tool call IDs.
25
27
  * Defaults to the standard generateId.
@@ -40,9 +42,7 @@ export interface StreamingToolCallTrackerOptions {
40
42
  * The returned metadata is stored on the tool call and passed to
41
43
  * `buildToolCallProviderMetadata` when the tool call is finalized.
42
44
  */
43
- extractMetadata?: (
44
- delta: StreamingToolCallDelta,
45
- ) => SharedV4ProviderMetadata | undefined;
45
+ extractMetadata?: (delta: DELTA) => SharedV4ProviderMetadata | undefined;
46
46
 
47
47
  /**
48
48
  * Build the `providerMetadata` object for a `tool-call` event.
@@ -62,6 +62,11 @@ interface TrackedToolCall {
62
62
  metadata?: SharedV4ProviderMetadata;
63
63
  }
64
64
 
65
+ type StreamingToolCallTrackerController = Pick<
66
+ TransformStreamDefaultController<LanguageModelV4StreamPart>,
67
+ 'enqueue'
68
+ >;
69
+
65
70
  /**
66
71
  * Tracks streaming tool call state across multiple deltas from an
67
72
  * OpenAI-compatible chat completion stream. Handles argument accumulation,
@@ -70,18 +75,25 @@ interface TrackedToolCall {
70
75
  *
71
76
  * Used by openai, openai-compatible, groq, deepseek, and alibaba providers.
72
77
  */
73
- export class StreamingToolCallTracker {
78
+ export class StreamingToolCallTracker<
79
+ DELTA extends StreamingToolCallDelta = StreamingToolCallDelta,
80
+ > {
74
81
  private toolCalls: TrackedToolCall[] = [];
82
+ private readonly controller: StreamingToolCallTrackerController;
75
83
  private readonly _generateId: () => string;
76
84
  private readonly typeValidation: 'none' | 'if-present' | 'required';
77
85
  private readonly extractMetadata?: (
78
- delta: StreamingToolCallDelta,
86
+ delta: DELTA,
79
87
  ) => SharedV4ProviderMetadata | undefined;
80
88
  private readonly buildToolCallProviderMetadata?: (
81
89
  metadata: SharedV4ProviderMetadata | undefined,
82
90
  ) => SharedV4ProviderMetadata | undefined;
83
91
 
84
- constructor(options: StreamingToolCallTrackerOptions = {}) {
92
+ constructor(
93
+ controller: StreamingToolCallTrackerController,
94
+ options: StreamingToolCallTrackerOptions<DELTA> = {},
95
+ ) {
96
+ this.controller = controller;
85
97
  this._generateId = options.generateId ?? defaultGenerateId;
86
98
  this.typeValidation = options.typeValidation ?? 'none';
87
99
  this.extractMetadata = options.extractMetadata;
@@ -93,16 +105,13 @@ export class StreamingToolCallTracker {
93
105
  * Emits tool-input-start, tool-input-delta, tool-input-end, and tool-call
94
106
  * events as appropriate.
95
107
  */
96
- processDelta(
97
- toolCallDelta: StreamingToolCallDelta,
98
- enqueue: (part: LanguageModelV4StreamPart) => void,
99
- ): void {
108
+ processDelta(toolCallDelta: DELTA): void {
100
109
  const index = toolCallDelta.index ?? this.toolCalls.length;
101
110
 
102
111
  if (this.toolCalls[index] == null) {
103
- this.processNewToolCall(index, toolCallDelta, enqueue);
112
+ this.processNewToolCall(index, toolCallDelta);
104
113
  } else {
105
- this.processExistingToolCall(index, toolCallDelta, enqueue);
114
+ this.processExistingToolCall(index, toolCallDelta);
106
115
  }
107
116
  }
108
117
 
@@ -110,19 +119,15 @@ export class StreamingToolCallTracker {
110
119
  * Finalize any unfinished tool calls. Should be called during the stream's
111
120
  * flush handler to ensure all tool calls are properly completed.
112
121
  */
113
- flush(enqueue: (part: LanguageModelV4StreamPart) => void): void {
122
+ flush(): void {
114
123
  for (const toolCall of this.toolCalls) {
115
124
  if (!toolCall.hasFinished) {
116
- this.finishToolCall(toolCall, enqueue);
125
+ this.finishToolCall(toolCall);
117
126
  }
118
127
  }
119
128
  }
120
129
 
121
- private processNewToolCall(
122
- index: number,
123
- toolCallDelta: StreamingToolCallDelta,
124
- enqueue: (part: LanguageModelV4StreamPart) => void,
125
- ): void {
130
+ private processNewToolCall(index: number, toolCallDelta: DELTA): void {
126
131
  if (this.typeValidation === 'required') {
127
132
  if (toolCallDelta.type !== 'function') {
128
133
  throw new InvalidResponseDataError({
@@ -153,7 +158,7 @@ export class StreamingToolCallTracker {
153
158
  });
154
159
  }
155
160
 
156
- enqueue({
161
+ this.controller.enqueue({
157
162
  type: 'tool-input-start',
158
163
  id: toolCallDelta.id,
159
164
  toolName: toolCallDelta.function.name,
@@ -176,7 +181,7 @@ export class StreamingToolCallTracker {
176
181
 
177
182
  // Emit initial delta if arguments already present
178
183
  if (toolCall.function.arguments.length > 0) {
179
- enqueue({
184
+ this.controller.enqueue({
180
185
  type: 'tool-input-delta',
181
186
  id: toolCall.id,
182
187
  delta: toolCall.function.arguments,
@@ -186,15 +191,11 @@ export class StreamingToolCallTracker {
186
191
  // Check if tool call is complete
187
192
  // (some providers send the full tool call in one chunk)
188
193
  if (isParsableJson(toolCall.function.arguments)) {
189
- this.finishToolCall(toolCall, enqueue);
194
+ this.finishToolCall(toolCall);
190
195
  }
191
196
  }
192
197
 
193
- private processExistingToolCall(
194
- index: number,
195
- toolCallDelta: StreamingToolCallDelta,
196
- enqueue: (part: LanguageModelV4StreamPart) => void,
197
- ): void {
198
+ private processExistingToolCall(index: number, toolCallDelta: DELTA): void {
198
199
  const toolCall = this.toolCalls[index];
199
200
 
200
201
  if (toolCall.hasFinished) {
@@ -204,7 +205,7 @@ export class StreamingToolCallTracker {
204
205
  if (toolCallDelta.function?.arguments != null) {
205
206
  toolCall.function.arguments += toolCallDelta.function.arguments;
206
207
 
207
- enqueue({
208
+ this.controller.enqueue({
208
209
  type: 'tool-input-delta',
209
210
  id: toolCall.id,
210
211
  delta: toolCallDelta.function.arguments,
@@ -213,15 +214,12 @@ export class StreamingToolCallTracker {
213
214
 
214
215
  // Check if tool call is complete
215
216
  if (isParsableJson(toolCall.function.arguments)) {
216
- this.finishToolCall(toolCall, enqueue);
217
+ this.finishToolCall(toolCall);
217
218
  }
218
219
  }
219
220
 
220
- private finishToolCall(
221
- toolCall: TrackedToolCall,
222
- enqueue: (part: LanguageModelV4StreamPart) => void,
223
- ): void {
224
- enqueue({
221
+ private finishToolCall(toolCall: TrackedToolCall): void {
222
+ this.controller.enqueue({
225
223
  type: 'tool-input-end',
226
224
  id: toolCall.id,
227
225
  });
@@ -230,7 +228,7 @@ export class StreamingToolCallTracker {
230
228
  toolCall.metadata,
231
229
  );
232
230
 
233
- enqueue({
231
+ this.controller.enqueue({
234
232
  type: 'tool-call',
235
233
  toolCallId: toolCall.id ?? this._generateId(),
236
234
  toolName: toolCall.function.name,
@@ -3,7 +3,7 @@ import { convertReadableStreamToArray } from './convert-readable-stream-to-array
3
3
  export async function convertResponseStreamToArray(
4
4
  response: Response,
5
5
  ): Promise<string[]> {
6
- return convertReadableStreamToArray(
6
+ return await convertReadableStreamToArray(
7
7
  response.body!.pipeThrough(new TextDecoderStream()),
8
8
  );
9
9
  }
@@ -1,4 +1,25 @@
1
+ function getNodeVersionParts() {
2
+ return process.versions.node
3
+ .split('.')
4
+ .map(version => Number.parseInt(version, 10));
5
+ }
6
+
1
7
  export function isNodeVersion(version: number) {
2
- const nodeMajorVersion = parseInt(process.version.slice(1).split('.')[0], 10);
8
+ const [nodeMajorVersion] = getNodeVersionParts();
3
9
  return nodeMajorVersion === version;
4
10
  }
11
+
12
+ export function isNodeVersionAtLeast(major: number, minor = 0, patch = 0) {
13
+ const [nodeMajorVersion, nodeMinorVersion, nodePatchVersion] =
14
+ getNodeVersionParts();
15
+
16
+ if (nodeMajorVersion !== major) {
17
+ return nodeMajorVersion > major;
18
+ }
19
+
20
+ if (nodeMinorVersion !== minor) {
21
+ return nodeMinorVersion > minor;
22
+ }
23
+
24
+ return nodePatchVersion >= patch;
25
+ }
@@ -24,6 +24,7 @@ export type { InferToolSetContext } from './infer-tool-set-context';
24
24
  export type { ModelMessage } from './model-message';
25
25
  export type { ProviderOptions } from './provider-options';
26
26
  export type { ProviderReference } from './provider-reference';
27
+ export type { SensitiveContext } from './sensitive-context';
27
28
  export type { SystemModelMessage } from './system-model-message';
28
29
  export {
29
30
  dynamicTool,
@@ -0,0 +1,9 @@
1
+ import type { Context } from './context';
2
+
3
+ /**
4
+ * Top-level context properties that contain sensitive data and should be
5
+ * excluded from telemetry.
6
+ */
7
+ export type SensitiveContext<CONTEXT extends Context | unknown | never> =
8
+ | { [KEY in keyof CONTEXT]?: boolean }
9
+ | undefined;
@@ -13,4 +13,11 @@ export type ToolApprovalRequest = {
13
13
  * ID of the tool call that the approval request is for.
14
14
  */
15
15
  toolCallId: string;
16
+
17
+ /**
18
+ * Flag indicating whether the tool was automatically approved or denied.
19
+ *
20
+ * @default false
21
+ */
22
+ isAutomatic?: boolean;
16
23
  };