assistant-stream 0.3.19 → 0.3.20
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.
- package/dist/core/tool/ToolCallReader.d.ts +2 -0
- package/dist/core/tool/ToolCallReader.d.ts.map +1 -1
- package/dist/core/tool/ToolCallReader.js +60 -13
- package/dist/core/tool/ToolCallReader.js.map +1 -1
- package/dist/core/tool/ToolExecutionStream.js +3 -2
- package/dist/core/tool/ToolExecutionStream.js.map +1 -1
- package/dist/core/tool/schema-utils.d.ts +2 -0
- package/dist/core/tool/schema-utils.d.ts.map +1 -1
- package/dist/core/tool/schema-utils.js +5 -2
- package/dist/core/tool/schema-utils.js.map +1 -1
- package/dist/core/tool/tool-types.d.ts +44 -5
- package/dist/core/tool/tool-types.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/package.json +2 -2
- package/src/core/tool/ToolCallReader.test.ts +86 -0
- package/src/core/tool/ToolCallReader.ts +83 -29
- package/src/core/tool/ToolExecutionStream.ts +8 -2
- package/src/core/tool/schema-utils.test.ts +63 -0
- package/src/core/tool/schema-utils.ts +25 -3
- package/src/core/tool/tool-types.ts +73 -1
- package/src/index.ts +1 -0
|
@@ -8,6 +8,7 @@ declare class ToolCallArgsReaderImpl<T extends ReadonlyJSONObject> implements To
|
|
|
8
8
|
private argTextDeltas;
|
|
9
9
|
private handles;
|
|
10
10
|
private args;
|
|
11
|
+
private finished;
|
|
11
12
|
constructor(argTextDeltas: ReadableStream<string>);
|
|
12
13
|
private processStream;
|
|
13
14
|
get<PathT extends TypePath<T>>(...fieldPath: PathT): Promise<TypeAtPath<T, PathT>>;
|
|
@@ -28,6 +29,7 @@ declare class ToolCallReaderImpl<TArgs extends ReadonlyJSONObject, TResult exten
|
|
|
28
29
|
argsText: string;
|
|
29
30
|
constructor();
|
|
30
31
|
appendArgsTextDelta(text: string): Promise<void>;
|
|
32
|
+
finishArgsText(): Promise<void>;
|
|
31
33
|
setResponse(value: ToolResponse<TResult>): void;
|
|
32
34
|
result: {
|
|
33
35
|
get: () => Promise<TResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ToolCallReader.d.ts","names":[],"sources":["../../../src/core/tool/ToolCallReader.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"ToolCallReader.d.ts","names":[],"sources":["../../../src/core/tool/ToolCallReader.ts"],"mappings":";;;;;;cA0Qa,sBAAA,WACD,kBAAA,aACC,kBAAA,CAAmB,CAAA;EAAA,QACtB,aAAA;EAAA,QACA,OAAA;EAAA,QACA,IAAA;EAAA,QACA,QAAA;cAEI,aAAA,EAAe,cAAA;EAAA,QAKb,aAAA;EA+Bd,GAAA,eAAkB,QAAA,CAAS,CAAA,MACtB,SAAA,EAAW,KAAA,GACb,OAAA,CAAQ,UAAA,CAAW,CAAA,EAAG,KAAA;EAiCzB,YAAA,eAA2B,QAAA,CAAS,CAAA,MAC/B,SAAA,EAAW,KAAA,GACb,mBAAA,CAAoB,WAAA,CAAY,UAAA,CAAW,CAAA,EAAG,KAAA;EA4BjD,UAAA,eAAyB,QAAA,CAAS,CAAA,MAC7B,SAAA,EAAW,KAAA,GACb,UAAA,CAAW,CAAA,EAAG,KAAA,+BACb,mBAAA,CAAoB,CAAA;EA6BxB,OAAA,eAAsB,QAAA,CAAS,CAAA,MAC1B,SAAA,EAAW,KAAA,GACb,UAAA,CAAW,CAAA,EAAG,KAAA,UAAe,KAAA,YAC5B,mBAAA,CAAoB,CAAA;AAAA;AAAA,cA8Bb,0BAAA,iBACK,iBAAA,aACL,sBAAA,CAAuB,OAAA;EAAA,iBACL,OAAA;cAAA,OAAA,EAAS,OAAA,CAAQ,YAAA,CAAa,OAAA;EAEpD,GAAA,IAAG,OAAA,CAAA,YAAA,CAAA,OAAA;AAAA;AAAA,cAKC,kBAAA,eACG,kBAAA,kBACE,iBAAA,aACL,cAAA,CAAe,KAAA,EAAO,OAAA;EAAA,SACjB,IAAA,EAAM,sBAAA,CAAuB,KAAA;EAAA,SAC7B,QAAA,EAAU,0BAAA,CAA2B,OAAA;EAAA,iBACpC,QAAA;EAAA,iBACA,OAAA;EAEV,QAAA;;EAYD,mBAAA,CAAoB,IAAA,WAAe,OAAA;EAanC,cAAA,IAAkB,OAAA;EAWxB,WAAA,CAAY,KAAA,EAAO,YAAA,CAAa,OAAA;EAIhC,MAAA"}
|
|
@@ -35,6 +35,17 @@ var GetHandle = class {
|
|
|
35
35
|
this.dispose();
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
|
+
end(args) {
|
|
39
|
+
if (this.disposed) return;
|
|
40
|
+
try {
|
|
41
|
+
const value = getField(args, this.fieldPath);
|
|
42
|
+
this.resolve(value);
|
|
43
|
+
} catch (e) {
|
|
44
|
+
this.reject(e);
|
|
45
|
+
} finally {
|
|
46
|
+
this.dispose();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
38
49
|
dispose() {
|
|
39
50
|
this.disposed = true;
|
|
40
51
|
}
|
|
@@ -61,6 +72,11 @@ var StreamValuesHandle = class {
|
|
|
61
72
|
this.dispose();
|
|
62
73
|
}
|
|
63
74
|
}
|
|
75
|
+
end() {
|
|
76
|
+
if (this.disposed) return;
|
|
77
|
+
this.controller.close();
|
|
78
|
+
this.dispose();
|
|
79
|
+
}
|
|
64
80
|
dispose() {
|
|
65
81
|
this.disposed = true;
|
|
66
82
|
}
|
|
@@ -92,6 +108,11 @@ var StreamTextHandle = class {
|
|
|
92
108
|
this.dispose();
|
|
93
109
|
}
|
|
94
110
|
}
|
|
111
|
+
end() {
|
|
112
|
+
if (this.disposed) return;
|
|
113
|
+
this.controller.close();
|
|
114
|
+
this.dispose();
|
|
115
|
+
}
|
|
95
116
|
dispose() {
|
|
96
117
|
this.disposed = true;
|
|
97
118
|
}
|
|
@@ -125,6 +146,11 @@ var ForEachHandle = class {
|
|
|
125
146
|
this.dispose();
|
|
126
147
|
}
|
|
127
148
|
}
|
|
149
|
+
end() {
|
|
150
|
+
if (this.disposed) return;
|
|
151
|
+
this.controller.close();
|
|
152
|
+
this.dispose();
|
|
153
|
+
}
|
|
128
154
|
dispose() {
|
|
129
155
|
this.disposed = true;
|
|
130
156
|
}
|
|
@@ -133,6 +159,7 @@ var ToolCallArgsReaderImpl = class {
|
|
|
133
159
|
argTextDeltas;
|
|
134
160
|
handles = /* @__PURE__ */ new Set();
|
|
135
161
|
args = parsePartialJsonObject("");
|
|
162
|
+
finished = false;
|
|
136
163
|
constructor(argTextDeltas) {
|
|
137
164
|
this.argTextDeltas = argTextDeltas;
|
|
138
165
|
this.processStream();
|
|
@@ -153,7 +180,10 @@ var ToolCallArgsReaderImpl = class {
|
|
|
153
180
|
}
|
|
154
181
|
} catch (error) {
|
|
155
182
|
console.error("Error processing argument stream:", error);
|
|
156
|
-
|
|
183
|
+
} finally {
|
|
184
|
+
this.finished = true;
|
|
185
|
+
for (const handle of this.handles) handle.end(this.args);
|
|
186
|
+
this.handles.clear();
|
|
157
187
|
}
|
|
158
188
|
}
|
|
159
189
|
get(...fieldPath) {
|
|
@@ -166,57 +196,64 @@ var ToolCallArgsReaderImpl = class {
|
|
|
166
196
|
return;
|
|
167
197
|
}
|
|
168
198
|
}
|
|
199
|
+
if (this.finished) {
|
|
200
|
+
handle.end(this.args);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
169
203
|
this.handles.add(handle);
|
|
170
204
|
handle.update(this.args);
|
|
171
205
|
});
|
|
172
206
|
}
|
|
173
207
|
streamValues(...fieldPath) {
|
|
174
208
|
const simplePath = fieldPath;
|
|
209
|
+
let handle;
|
|
175
210
|
return asAsyncIterableStream(new ReadableStream({
|
|
176
211
|
start: (controller) => {
|
|
177
|
-
|
|
178
|
-
this.handles.add(handle);
|
|
212
|
+
handle = new StreamValuesHandle(controller, simplePath);
|
|
213
|
+
if (!this.finished) this.handles.add(handle);
|
|
179
214
|
handle.update(this.args);
|
|
215
|
+
if (this.finished) handle.end();
|
|
180
216
|
},
|
|
181
217
|
cancel: () => {
|
|
182
|
-
|
|
218
|
+
if (handle) {
|
|
183
219
|
handle.dispose();
|
|
184
220
|
this.handles.delete(handle);
|
|
185
|
-
break;
|
|
186
221
|
}
|
|
187
222
|
}
|
|
188
223
|
}));
|
|
189
224
|
}
|
|
190
225
|
streamText(...fieldPath) {
|
|
191
226
|
const simplePath = fieldPath;
|
|
227
|
+
let handle;
|
|
192
228
|
return asAsyncIterableStream(new ReadableStream({
|
|
193
229
|
start: (controller) => {
|
|
194
|
-
|
|
195
|
-
this.handles.add(handle);
|
|
230
|
+
handle = new StreamTextHandle(controller, simplePath);
|
|
231
|
+
if (!this.finished) this.handles.add(handle);
|
|
196
232
|
handle.update(this.args);
|
|
233
|
+
if (this.finished) handle.end();
|
|
197
234
|
},
|
|
198
235
|
cancel: () => {
|
|
199
|
-
|
|
236
|
+
if (handle) {
|
|
200
237
|
handle.dispose();
|
|
201
238
|
this.handles.delete(handle);
|
|
202
|
-
break;
|
|
203
239
|
}
|
|
204
240
|
}
|
|
205
241
|
}));
|
|
206
242
|
}
|
|
207
243
|
forEach(...fieldPath) {
|
|
208
244
|
const simplePath = fieldPath;
|
|
245
|
+
let handle;
|
|
209
246
|
return asAsyncIterableStream(new ReadableStream({
|
|
210
247
|
start: (controller) => {
|
|
211
|
-
|
|
212
|
-
this.handles.add(handle);
|
|
248
|
+
handle = new ForEachHandle(controller, simplePath);
|
|
249
|
+
if (!this.finished) this.handles.add(handle);
|
|
213
250
|
handle.update(this.args);
|
|
251
|
+
if (this.finished) handle.end();
|
|
214
252
|
},
|
|
215
253
|
cancel: () => {
|
|
216
|
-
|
|
254
|
+
if (handle) {
|
|
217
255
|
handle.dispose();
|
|
218
256
|
this.handles.delete(handle);
|
|
219
|
-
break;
|
|
220
257
|
}
|
|
221
258
|
}
|
|
222
259
|
}));
|
|
@@ -256,6 +293,16 @@ var ToolCallReaderImpl = class {
|
|
|
256
293
|
}
|
|
257
294
|
this.argsText += text;
|
|
258
295
|
}
|
|
296
|
+
async finishArgsText() {
|
|
297
|
+
const writer = this.writable.getWriter();
|
|
298
|
+
try {
|
|
299
|
+
await writer.close();
|
|
300
|
+
} catch (err) {
|
|
301
|
+
console.warn(err);
|
|
302
|
+
} finally {
|
|
303
|
+
writer.releaseLock();
|
|
304
|
+
}
|
|
305
|
+
}
|
|
259
306
|
setResponse(value) {
|
|
260
307
|
this.resolve(value);
|
|
261
308
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ToolCallReader.js","names":[],"sources":["../../../src/core/tool/ToolCallReader.ts"],"sourcesContent":["import { promiseWithResolvers } from \"../../utils/promiseWithResolvers\";\nimport {\n parsePartialJsonObject,\n getPartialJsonObjectFieldState,\n} from \"../../utils/json/parse-partial-json-object\";\nimport type {\n ToolCallArgsReader,\n ToolCallReader,\n ToolCallResponseReader,\n} from \"./tool-types\";\nimport type { DeepPartial, TypeAtPath, TypePath } from \"./type-path-utils\";\nimport type { ToolResponse } from \"./ToolResponse\";\nimport { asAsyncIterableStream } from \"../../utils/AsyncIterableStream\";\nimport type {\n AsyncIterableStream,\n ReadonlyJSONObject,\n ReadonlyJSONValue,\n} from \"../../utils\";\n\n// TODO: remove dispose\n\nfunction getField<T>(obj: T, fieldPath: (string | number)[]): unknown {\n let current: unknown = obj;\n for (const key of fieldPath) {\n if (current === undefined || current === null) {\n return undefined;\n }\n current = current[key as keyof typeof current];\n }\n return current;\n}\n\ninterface Handle {\n update(args: unknown): void;\n dispose(): void;\n}\n\nclass GetHandle<T, TValue> implements Handle {\n private resolve: (value: TValue) => void;\n private reject: (reason: unknown) => void;\n private disposed = false;\n private fieldPath: (string | number)[];\n\n constructor(\n resolve: (value: TValue) => void,\n reject: (reason: unknown) => void,\n fieldPath: (string | number)[],\n ) {\n this.resolve = resolve;\n this.reject = reject;\n this.fieldPath = fieldPath;\n }\n\n update(args: unknown): void {\n if (this.disposed) return;\n\n try {\n // Check if the field is complete\n if (\n getPartialJsonObjectFieldState(\n args as Record<string, unknown>,\n this.fieldPath,\n ) === \"complete\"\n ) {\n const value = getField(args as T, this.fieldPath);\n if (value !== undefined) {\n this.resolve(value as TValue);\n this.dispose();\n }\n }\n } catch (e) {\n this.reject(e);\n this.dispose();\n }\n }\n\n dispose(): void {\n this.disposed = true;\n }\n}\n\nclass StreamValuesHandle<T> implements Handle {\n private controller: ReadableStreamDefaultController<unknown>;\n private disposed = false;\n private fieldPath: (string | number)[];\n\n constructor(\n controller: ReadableStreamDefaultController<unknown>,\n fieldPath: (string | number)[],\n ) {\n this.controller = controller;\n this.fieldPath = fieldPath;\n }\n\n update(args: unknown): void {\n if (this.disposed) return;\n\n try {\n const value = getField(args as T, this.fieldPath);\n\n if (value !== undefined) {\n this.controller.enqueue(value);\n }\n\n // Check if the field is complete, if so close the stream\n if (\n getPartialJsonObjectFieldState(\n args as Record<string, unknown>,\n this.fieldPath,\n ) === \"complete\"\n ) {\n this.controller.close();\n this.dispose();\n }\n } catch (e) {\n this.controller.error(e);\n this.dispose();\n }\n }\n\n dispose(): void {\n this.disposed = true;\n }\n}\n\nclass StreamTextHandle<T> implements Handle {\n private controller: ReadableStreamDefaultController<unknown>;\n private disposed = false;\n private fieldPath: (string | number)[];\n private lastValue: string | undefined = undefined;\n\n constructor(\n controller: ReadableStreamDefaultController<unknown>,\n fieldPath: (string | number)[],\n ) {\n this.controller = controller;\n this.fieldPath = fieldPath;\n }\n\n update(args: unknown): void {\n if (this.disposed) return;\n\n try {\n const value = getField(args as T, this.fieldPath);\n\n if (value !== undefined && typeof value === \"string\") {\n const delta = value.substring(this.lastValue?.length || 0);\n this.lastValue = value;\n this.controller.enqueue(delta);\n }\n\n // Check if the field is complete, if so close the stream\n if (\n getPartialJsonObjectFieldState(\n args as Record<string, unknown>,\n this.fieldPath,\n ) === \"complete\"\n ) {\n this.controller.close();\n this.dispose();\n }\n } catch (e) {\n this.controller.error(e);\n this.dispose();\n }\n }\n\n dispose(): void {\n this.disposed = true;\n }\n}\n\nclass ForEachHandle<T> implements Handle {\n private controller: ReadableStreamDefaultController<unknown>;\n private disposed = false;\n private fieldPath: (string | number)[];\n private processedIndexes = new Set<number>();\n\n constructor(\n controller: ReadableStreamDefaultController<unknown>,\n fieldPath: (string | number)[],\n ) {\n this.controller = controller;\n this.fieldPath = fieldPath;\n }\n\n update(args: unknown): void {\n if (this.disposed) return;\n\n try {\n const array = getField(args as T, this.fieldPath);\n\n if (!Array.isArray(array)) {\n return;\n }\n\n // Check each array element and emit completed ones that haven't been processed\n for (let i = 0; i < array.length; i++) {\n if (!this.processedIndexes.has(i)) {\n const elementPath = [...this.fieldPath, i];\n if (\n getPartialJsonObjectFieldState(\n args as Record<string, unknown>,\n elementPath,\n ) === \"complete\"\n ) {\n this.controller.enqueue(array[i]);\n this.processedIndexes.add(i);\n }\n }\n }\n\n // Check if the entire array is complete\n if (\n getPartialJsonObjectFieldState(\n args as Record<string, unknown>,\n this.fieldPath,\n ) === \"complete\"\n ) {\n this.controller.close();\n this.dispose();\n }\n } catch (e) {\n this.controller.error(e);\n this.dispose();\n }\n }\n\n dispose(): void {\n this.disposed = true;\n }\n}\n\n// Implementation of ToolCallReader that uses stream of partial JSON\nexport class ToolCallArgsReaderImpl<\n T extends ReadonlyJSONObject,\n> implements ToolCallArgsReader<T> {\n private argTextDeltas: ReadableStream<string>;\n private handles: Set<Handle> = new Set();\n private args: unknown = parsePartialJsonObject(\"\");\n\n constructor(argTextDeltas: ReadableStream<string>) {\n this.argTextDeltas = argTextDeltas;\n this.processStream();\n }\n\n private async processStream(): Promise<void> {\n try {\n let accumulatedText = \"\";\n const reader = this.argTextDeltas.getReader();\n\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n\n accumulatedText += value;\n const parsedArgs = parsePartialJsonObject(accumulatedText);\n\n if (parsedArgs !== undefined) {\n this.args = parsedArgs;\n // Notify all handles of the updated args\n for (const handle of this.handles) {\n handle.update(parsedArgs);\n }\n }\n }\n } catch (error) {\n console.error(\"Error processing argument stream:\", error);\n // Notify handles of the error\n for (const handle of this.handles) {\n handle.dispose();\n }\n }\n }\n\n get<PathT extends TypePath<T>>(\n ...fieldPath: PathT\n ): Promise<TypeAtPath<T, PathT>> {\n return new Promise<TypeAtPath<T, PathT>>((resolve, reject) => {\n const handle = new GetHandle<T, TypeAtPath<T, PathT>>(\n resolve,\n reject,\n fieldPath,\n );\n\n // Check if the field is already complete in current args\n if (\n this.args &&\n getPartialJsonObjectFieldState(\n this.args as Record<string, unknown>,\n fieldPath,\n ) === \"complete\"\n ) {\n const value = getField(this.args as T, fieldPath);\n if (value !== undefined) {\n resolve(value as TypeAtPath<T, PathT>);\n return;\n }\n }\n\n this.handles.add(handle);\n handle.update(this.args);\n });\n }\n\n streamValues<PathT extends TypePath<T>>(\n ...fieldPath: PathT\n ): AsyncIterableStream<DeepPartial<TypeAtPath<T, PathT>>> {\n // Use a type assertion to convert the complex TypePath to a simple array\n const simplePath = fieldPath as unknown as (string | number)[];\n\n const stream = new ReadableStream<DeepPartial<TypeAtPath<T, PathT>>>({\n start: (controller) => {\n const handle = new StreamValuesHandle<T>(controller, simplePath);\n this.handles.add(handle);\n\n // Check current args immediately\n handle.update(this.args);\n },\n cancel: () => {\n // Find and dispose the corresponding handle\n for (const handle of this.handles) {\n if (handle instanceof StreamValuesHandle) {\n handle.dispose();\n this.handles.delete(handle);\n break;\n }\n }\n },\n });\n\n return asAsyncIterableStream(stream) as any;\n }\n\n streamText<PathT extends TypePath<T>>(\n ...fieldPath: PathT\n ): TypeAtPath<T, PathT> extends string & (infer U)\n ? AsyncIterableStream<U>\n : never {\n // Use a type assertion to convert the complex TypePath to a simple array\n const simplePath = fieldPath as unknown as (string | number)[];\n\n const stream = new ReadableStream<unknown>({\n start: (controller) => {\n const handle = new StreamTextHandle<T>(controller, simplePath);\n this.handles.add(handle);\n\n // Check current args immediately\n handle.update(this.args);\n },\n cancel: () => {\n // Find and dispose the corresponding handle\n for (const handle of this.handles) {\n if (handle instanceof StreamTextHandle) {\n handle.dispose();\n this.handles.delete(handle);\n break;\n }\n }\n },\n });\n\n return asAsyncIterableStream(stream) as any;\n }\n\n forEach<PathT extends TypePath<T>>(\n ...fieldPath: PathT\n ): TypeAtPath<T, PathT> extends Array<infer U>\n ? AsyncIterableStream<U>\n : never {\n // Use a type assertion to convert the complex TypePath to a simple array\n const simplePath = fieldPath as unknown as (string | number)[];\n\n const stream = new ReadableStream<unknown>({\n start: (controller) => {\n const handle = new ForEachHandle<T>(controller, simplePath);\n this.handles.add(handle);\n\n // Check current args immediately\n handle.update(this.args);\n },\n cancel: () => {\n // Find and dispose the corresponding handle\n for (const handle of this.handles) {\n if (handle instanceof ForEachHandle) {\n handle.dispose();\n this.handles.delete(handle);\n break;\n }\n }\n },\n });\n\n return asAsyncIterableStream(stream) as any;\n }\n}\n\nexport class ToolCallResponseReaderImpl<\n TResult extends ReadonlyJSONValue,\n> implements ToolCallResponseReader<TResult> {\n constructor(private readonly promise: Promise<ToolResponse<TResult>>) {}\n\n public get() {\n return this.promise;\n }\n}\n\nexport class ToolCallReaderImpl<\n TArgs extends ReadonlyJSONObject,\n TResult extends ReadonlyJSONValue,\n> implements ToolCallReader<TArgs, TResult> {\n public readonly args: ToolCallArgsReaderImpl<TArgs>;\n public readonly response: ToolCallResponseReaderImpl<TResult>;\n private readonly writable: WritableStream<string>;\n private readonly resolve: (value: ToolResponse<TResult>) => void;\n\n public argsText: string = \"\";\n\n constructor() {\n const stream = new TransformStream<string, string>();\n this.writable = stream.writable;\n this.args = new ToolCallArgsReaderImpl<TArgs>(stream.readable);\n\n const { promise, resolve } = promiseWithResolvers<ToolResponse<TResult>>();\n this.resolve = resolve;\n this.response = new ToolCallResponseReaderImpl<TResult>(promise);\n }\n\n async appendArgsTextDelta(text: string): Promise<void> {\n const writer = this.writable.getWriter();\n try {\n await writer.write(text);\n } catch (err) {\n console.warn(err);\n } finally {\n writer.releaseLock();\n }\n\n this.argsText += text;\n }\n\n setResponse(value: ToolResponse<TResult>): void {\n this.resolve(value);\n }\n\n result = {\n get: async () => {\n const response = await this.response.get();\n return response.result;\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ToolCallReader.js","names":[],"sources":["../../../src/core/tool/ToolCallReader.ts"],"sourcesContent":["import { promiseWithResolvers } from \"../../utils/promiseWithResolvers\";\nimport {\n parsePartialJsonObject,\n getPartialJsonObjectFieldState,\n} from \"../../utils/json/parse-partial-json-object\";\nimport type {\n ToolCallArgsReader,\n ToolCallReader,\n ToolCallResponseReader,\n} from \"./tool-types\";\nimport type { DeepPartial, TypeAtPath, TypePath } from \"./type-path-utils\";\nimport type { ToolResponse } from \"./ToolResponse\";\nimport { asAsyncIterableStream } from \"../../utils/AsyncIterableStream\";\nimport type {\n AsyncIterableStream,\n ReadonlyJSONObject,\n ReadonlyJSONValue,\n} from \"../../utils\";\n\n// TODO: remove dispose\n\nfunction getField<T>(obj: T, fieldPath: (string | number)[]): unknown {\n let current: unknown = obj;\n for (const key of fieldPath) {\n if (current === undefined || current === null) {\n return undefined;\n }\n current = current[key as keyof typeof current];\n }\n return current;\n}\n\ninterface Handle {\n update(args: unknown): void;\n end(args: unknown): void;\n dispose(): void;\n}\n\nclass GetHandle<T, TValue> implements Handle {\n private resolve: (value: TValue) => void;\n private reject: (reason: unknown) => void;\n private disposed = false;\n private fieldPath: (string | number)[];\n\n constructor(\n resolve: (value: TValue) => void,\n reject: (reason: unknown) => void,\n fieldPath: (string | number)[],\n ) {\n this.resolve = resolve;\n this.reject = reject;\n this.fieldPath = fieldPath;\n }\n\n update(args: unknown): void {\n if (this.disposed) return;\n\n try {\n // Check if the field is complete\n if (\n getPartialJsonObjectFieldState(\n args as Record<string, unknown>,\n this.fieldPath,\n ) === \"complete\"\n ) {\n const value = getField(args as T, this.fieldPath);\n if (value !== undefined) {\n this.resolve(value as TValue);\n this.dispose();\n }\n }\n } catch (e) {\n this.reject(e);\n this.dispose();\n }\n }\n\n end(args: unknown): void {\n if (this.disposed) return;\n\n try {\n const value = getField(args as T, this.fieldPath);\n this.resolve(value as TValue);\n } catch (e) {\n this.reject(e);\n } finally {\n this.dispose();\n }\n }\n\n dispose(): void {\n this.disposed = true;\n }\n}\n\nclass StreamValuesHandle<T> implements Handle {\n private controller: ReadableStreamDefaultController<unknown>;\n private disposed = false;\n private fieldPath: (string | number)[];\n\n constructor(\n controller: ReadableStreamDefaultController<unknown>,\n fieldPath: (string | number)[],\n ) {\n this.controller = controller;\n this.fieldPath = fieldPath;\n }\n\n update(args: unknown): void {\n if (this.disposed) return;\n\n try {\n const value = getField(args as T, this.fieldPath);\n\n if (value !== undefined) {\n this.controller.enqueue(value);\n }\n\n // Check if the field is complete, if so close the stream\n if (\n getPartialJsonObjectFieldState(\n args as Record<string, unknown>,\n this.fieldPath,\n ) === \"complete\"\n ) {\n this.controller.close();\n this.dispose();\n }\n } catch (e) {\n this.controller.error(e);\n this.dispose();\n }\n }\n\n end(): void {\n if (this.disposed) return;\n this.controller.close();\n this.dispose();\n }\n\n dispose(): void {\n this.disposed = true;\n }\n}\n\nclass StreamTextHandle<T> implements Handle {\n private controller: ReadableStreamDefaultController<unknown>;\n private disposed = false;\n private fieldPath: (string | number)[];\n private lastValue: string | undefined = undefined;\n\n constructor(\n controller: ReadableStreamDefaultController<unknown>,\n fieldPath: (string | number)[],\n ) {\n this.controller = controller;\n this.fieldPath = fieldPath;\n }\n\n update(args: unknown): void {\n if (this.disposed) return;\n\n try {\n const value = getField(args as T, this.fieldPath);\n\n if (value !== undefined && typeof value === \"string\") {\n const delta = value.substring(this.lastValue?.length || 0);\n this.lastValue = value;\n this.controller.enqueue(delta);\n }\n\n // Check if the field is complete, if so close the stream\n if (\n getPartialJsonObjectFieldState(\n args as Record<string, unknown>,\n this.fieldPath,\n ) === \"complete\"\n ) {\n this.controller.close();\n this.dispose();\n }\n } catch (e) {\n this.controller.error(e);\n this.dispose();\n }\n }\n\n end(): void {\n if (this.disposed) return;\n this.controller.close();\n this.dispose();\n }\n\n dispose(): void {\n this.disposed = true;\n }\n}\n\nclass ForEachHandle<T> implements Handle {\n private controller: ReadableStreamDefaultController<unknown>;\n private disposed = false;\n private fieldPath: (string | number)[];\n private processedIndexes = new Set<number>();\n\n constructor(\n controller: ReadableStreamDefaultController<unknown>,\n fieldPath: (string | number)[],\n ) {\n this.controller = controller;\n this.fieldPath = fieldPath;\n }\n\n update(args: unknown): void {\n if (this.disposed) return;\n\n try {\n const array = getField(args as T, this.fieldPath);\n\n if (!Array.isArray(array)) {\n return;\n }\n\n // Check each array element and emit completed ones that haven't been processed\n for (let i = 0; i < array.length; i++) {\n if (!this.processedIndexes.has(i)) {\n const elementPath = [...this.fieldPath, i];\n if (\n getPartialJsonObjectFieldState(\n args as Record<string, unknown>,\n elementPath,\n ) === \"complete\"\n ) {\n this.controller.enqueue(array[i]);\n this.processedIndexes.add(i);\n }\n }\n }\n\n // Check if the entire array is complete\n if (\n getPartialJsonObjectFieldState(\n args as Record<string, unknown>,\n this.fieldPath,\n ) === \"complete\"\n ) {\n this.controller.close();\n this.dispose();\n }\n } catch (e) {\n this.controller.error(e);\n this.dispose();\n }\n }\n\n end(): void {\n if (this.disposed) return;\n this.controller.close();\n this.dispose();\n }\n\n dispose(): void {\n this.disposed = true;\n }\n}\n\n// Implementation of ToolCallReader that uses stream of partial JSON\nexport class ToolCallArgsReaderImpl<\n T extends ReadonlyJSONObject,\n> implements ToolCallArgsReader<T> {\n private argTextDeltas: ReadableStream<string>;\n private handles: Set<Handle> = new Set();\n private args: unknown = parsePartialJsonObject(\"\");\n private finished = false;\n\n constructor(argTextDeltas: ReadableStream<string>) {\n this.argTextDeltas = argTextDeltas;\n this.processStream();\n }\n\n private async processStream(): Promise<void> {\n try {\n let accumulatedText = \"\";\n const reader = this.argTextDeltas.getReader();\n\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n\n accumulatedText += value;\n const parsedArgs = parsePartialJsonObject(accumulatedText);\n\n if (parsedArgs !== undefined) {\n this.args = parsedArgs;\n // Notify all handles of the updated args\n for (const handle of this.handles) {\n handle.update(parsedArgs);\n }\n }\n }\n } catch (error) {\n console.error(\"Error processing argument stream:\", error);\n } finally {\n this.finished = true;\n for (const handle of this.handles) {\n handle.end(this.args);\n }\n this.handles.clear();\n }\n }\n\n get<PathT extends TypePath<T>>(\n ...fieldPath: PathT\n ): Promise<TypeAtPath<T, PathT>> {\n return new Promise<TypeAtPath<T, PathT>>((resolve, reject) => {\n const handle = new GetHandle<T, TypeAtPath<T, PathT>>(\n resolve,\n reject,\n fieldPath,\n );\n\n // Check if the field is already complete in current args\n if (\n this.args &&\n getPartialJsonObjectFieldState(\n this.args as Record<string, unknown>,\n fieldPath,\n ) === \"complete\"\n ) {\n const value = getField(this.args as T, fieldPath);\n if (value !== undefined) {\n resolve(value as TypeAtPath<T, PathT>);\n return;\n }\n }\n\n if (this.finished) {\n handle.end(this.args);\n return;\n }\n\n this.handles.add(handle);\n handle.update(this.args);\n });\n }\n\n streamValues<PathT extends TypePath<T>>(\n ...fieldPath: PathT\n ): AsyncIterableStream<DeepPartial<TypeAtPath<T, PathT>>> {\n // Use a type assertion to convert the complex TypePath to a simple array\n const simplePath = fieldPath as unknown as (string | number)[];\n\n let handle: StreamValuesHandle<T> | undefined;\n const stream = new ReadableStream<DeepPartial<TypeAtPath<T, PathT>>>({\n start: (controller) => {\n handle = new StreamValuesHandle<T>(controller, simplePath);\n if (!this.finished) this.handles.add(handle);\n\n // Check current args immediately\n handle.update(this.args);\n\n if (this.finished) handle.end();\n },\n cancel: () => {\n // Dispose this stream's own handle (captured above) — scanning for the\n // first match would dispose a concurrent streamValues()'s handle.\n if (handle) {\n handle.dispose();\n this.handles.delete(handle);\n }\n },\n });\n\n return asAsyncIterableStream(stream) as any;\n }\n\n streamText<PathT extends TypePath<T>>(\n ...fieldPath: PathT\n ): TypeAtPath<T, PathT> extends string & (infer U)\n ? AsyncIterableStream<U>\n : never {\n // Use a type assertion to convert the complex TypePath to a simple array\n const simplePath = fieldPath as unknown as (string | number)[];\n\n let handle: StreamTextHandle<T> | undefined;\n const stream = new ReadableStream<unknown>({\n start: (controller) => {\n handle = new StreamTextHandle<T>(controller, simplePath);\n if (!this.finished) this.handles.add(handle);\n\n // Check current args immediately\n handle.update(this.args);\n\n if (this.finished) handle.end();\n },\n cancel: () => {\n // Dispose this stream's own handle (captured above) — scanning for the\n // first match would dispose a concurrent streamText()'s handle.\n if (handle) {\n handle.dispose();\n this.handles.delete(handle);\n }\n },\n });\n\n return asAsyncIterableStream(stream) as any;\n }\n\n forEach<PathT extends TypePath<T>>(\n ...fieldPath: PathT\n ): TypeAtPath<T, PathT> extends Array<infer U>\n ? AsyncIterableStream<U>\n : never {\n // Use a type assertion to convert the complex TypePath to a simple array\n const simplePath = fieldPath as unknown as (string | number)[];\n\n let handle: ForEachHandle<T> | undefined;\n const stream = new ReadableStream<unknown>({\n start: (controller) => {\n handle = new ForEachHandle<T>(controller, simplePath);\n if (!this.finished) this.handles.add(handle);\n\n // Check current args immediately\n handle.update(this.args);\n\n if (this.finished) handle.end();\n },\n cancel: () => {\n // Dispose this stream's own handle (captured above) — scanning for the\n // first match would dispose a concurrent forEach()'s handle.\n if (handle) {\n handle.dispose();\n this.handles.delete(handle);\n }\n },\n });\n\n return asAsyncIterableStream(stream) as any;\n }\n}\n\nexport class ToolCallResponseReaderImpl<\n TResult extends ReadonlyJSONValue,\n> implements ToolCallResponseReader<TResult> {\n constructor(private readonly promise: Promise<ToolResponse<TResult>>) {}\n\n public get() {\n return this.promise;\n }\n}\n\nexport class ToolCallReaderImpl<\n TArgs extends ReadonlyJSONObject,\n TResult extends ReadonlyJSONValue,\n> implements ToolCallReader<TArgs, TResult> {\n public readonly args: ToolCallArgsReaderImpl<TArgs>;\n public readonly response: ToolCallResponseReaderImpl<TResult>;\n private readonly writable: WritableStream<string>;\n private readonly resolve: (value: ToolResponse<TResult>) => void;\n\n public argsText: string = \"\";\n\n constructor() {\n const stream = new TransformStream<string, string>();\n this.writable = stream.writable;\n this.args = new ToolCallArgsReaderImpl<TArgs>(stream.readable);\n\n const { promise, resolve } = promiseWithResolvers<ToolResponse<TResult>>();\n this.resolve = resolve;\n this.response = new ToolCallResponseReaderImpl<TResult>(promise);\n }\n\n async appendArgsTextDelta(text: string): Promise<void> {\n const writer = this.writable.getWriter();\n try {\n await writer.write(text);\n } catch (err) {\n console.warn(err);\n } finally {\n writer.releaseLock();\n }\n\n this.argsText += text;\n }\n\n async finishArgsText(): Promise<void> {\n const writer = this.writable.getWriter();\n try {\n await writer.close();\n } catch (err) {\n console.warn(err);\n } finally {\n writer.releaseLock();\n }\n }\n\n setResponse(value: ToolResponse<TResult>): void {\n this.resolve(value);\n }\n\n result = {\n get: async () => {\n const response = await this.response.get();\n return response.result;\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -11,7 +11,7 @@ var ToolExecutionStream = class extends PipeableTransformStream {
|
|
|
11
11
|
const toolCallControllers = /* @__PURE__ */ new Map();
|
|
12
12
|
super((readable) => {
|
|
13
13
|
const transform = new TransformStream({
|
|
14
|
-
transform(chunk, controller) {
|
|
14
|
+
async transform(chunk, controller) {
|
|
15
15
|
if (chunk.type !== "part-finish" || chunk.meta.type !== "tool-call") controller.enqueue(chunk);
|
|
16
16
|
switch (chunk.type) {
|
|
17
17
|
case "part-start":
|
|
@@ -30,7 +30,7 @@ var ToolExecutionStream = class extends PipeableTransformStream {
|
|
|
30
30
|
const toolCallId = chunk.meta.toolCallId;
|
|
31
31
|
const controller = toolCallControllers.get(toolCallId);
|
|
32
32
|
if (!controller) throw new Error("No controller found for tool call");
|
|
33
|
-
controller.appendArgsTextDelta(chunk.textDelta);
|
|
33
|
+
await controller.appendArgsTextDelta(chunk.textDelta);
|
|
34
34
|
}
|
|
35
35
|
break;
|
|
36
36
|
case "result": {
|
|
@@ -51,6 +51,7 @@ var ToolExecutionStream = class extends PipeableTransformStream {
|
|
|
51
51
|
const { toolCallId, toolName } = chunk.meta;
|
|
52
52
|
const streamController = toolCallControllers.get(toolCallId);
|
|
53
53
|
if (!streamController) throw new Error("No controller found for tool call");
|
|
54
|
+
await streamController.finishArgsText();
|
|
54
55
|
let isExecuting = false;
|
|
55
56
|
const promise = withPromiseOrValue(() => {
|
|
56
57
|
let args;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ToolExecutionStream.js","names":[],"sources":["../../../src/core/tool/ToolExecutionStream.ts"],"sourcesContent":["import sjson from \"secure-json-parse\";\nimport type { AssistantStreamChunk } from \"../AssistantStreamChunk\";\nimport {\n type AssistantMetaStreamChunk,\n AssistantMetaTransformStream,\n} from \"../utils/stream/AssistantMetaTransformStream\";\nimport { PipeableTransformStream } from \"../utils/stream/PipeableTransformStream\";\nimport type {\n ReadonlyJSONObject,\n ReadonlyJSONValue,\n} from \"../../utils/json/json-value\";\nimport { ToolResponse } from \"./ToolResponse\";\nimport { withPromiseOrValue } from \"../utils/withPromiseOrValue\";\nimport { ToolCallReaderImpl } from \"./ToolCallReader\";\nimport type { ToolCallReader } from \"./tool-types\";\n\ntype ToolCallback = (toolCall: {\n toolCallId: string;\n toolName: string;\n args: ReadonlyJSONObject;\n}) =>\n | Promise<ToolResponse<ReadonlyJSONValue>>\n | ToolResponse<ReadonlyJSONValue>\n | undefined;\n\ntype ToolStreamCallback = <\n TArgs extends ReadonlyJSONObject = ReadonlyJSONObject,\n TResult extends ReadonlyJSONValue = ReadonlyJSONValue,\n>(toolCall: {\n reader: ToolCallReader<TArgs, TResult>;\n toolCallId: string;\n toolName: string;\n}) => void;\n\ntype ToolExecutionOptions = {\n execute: ToolCallback;\n streamCall: ToolStreamCallback;\n onExecutionStart?:\n | ((toolCallId: string, toolName: string) => void)\n | undefined;\n onExecutionEnd?: ((toolCallId: string, toolName: string) => void) | undefined;\n};\n\nexport class ToolExecutionStream extends PipeableTransformStream<\n AssistantStreamChunk,\n AssistantStreamChunk\n> {\n constructor(options: ToolExecutionOptions) {\n const toolCallPromises = new Map<string, PromiseLike<void>>();\n const toolCallControllers = new Map<\n string,\n ToolCallReaderImpl<ReadonlyJSONObject, ReadonlyJSONValue>\n >();\n\n super((readable) => {\n const transform = new TransformStream<\n AssistantMetaStreamChunk,\n AssistantStreamChunk\n >({\n transform(chunk, controller) {\n // forward everything\n if (chunk.type !== \"part-finish\" || chunk.meta.type !== \"tool-call\") {\n controller.enqueue(chunk);\n }\n\n const type = chunk.type;\n\n switch (type) {\n case \"part-start\":\n if (chunk.part.type === \"tool-call\") {\n const reader = new ToolCallReaderImpl<\n ReadonlyJSONObject,\n ReadonlyJSONValue\n >();\n toolCallControllers.set(chunk.part.toolCallId, reader);\n\n options.streamCall({\n reader,\n toolCallId: chunk.part.toolCallId,\n toolName: chunk.part.toolName,\n });\n }\n break;\n case \"text-delta\": {\n if (chunk.meta.type === \"tool-call\") {\n const toolCallId = chunk.meta.toolCallId;\n\n const controller = toolCallControllers.get(toolCallId);\n if (!controller)\n throw new Error(\"No controller found for tool call\");\n controller.appendArgsTextDelta(chunk.textDelta);\n }\n break;\n }\n case \"result\": {\n if (chunk.meta.type !== \"tool-call\") break;\n\n const { toolCallId } = chunk.meta;\n const controller = toolCallControllers.get(toolCallId);\n if (!controller)\n throw new Error(\"No controller found for tool call\");\n controller.setResponse(\n new ToolResponse({\n result: chunk.result,\n artifact: chunk.artifact,\n isError: chunk.isError,\n modelContent: chunk.modelContent,\n }),\n );\n break;\n }\n case \"tool-call-args-text-finish\": {\n if (chunk.meta.type !== \"tool-call\") break;\n\n const { toolCallId, toolName } = chunk.meta;\n const streamController = toolCallControllers.get(toolCallId)!;\n if (!streamController)\n throw new Error(\"No controller found for tool call\");\n\n let isExecuting = false;\n const promise = withPromiseOrValue(\n () => {\n let args: ReadonlyJSONObject;\n try {\n args = sjson.parse(\n streamController.argsText,\n ) as ReadonlyJSONObject;\n } catch (e) {\n throw new Error(\n `Function parameter parsing failed. ${JSON.stringify((e as Error).message)}`,\n );\n }\n\n const executeResult = options.execute({\n toolCallId,\n toolName,\n args,\n });\n\n // Only mark as executing if the tool has frontend execution\n if (executeResult !== undefined) {\n isExecuting = true;\n options.onExecutionStart?.(toolCallId, toolName);\n }\n\n return executeResult;\n },\n (c) => {\n if (isExecuting) {\n options.onExecutionEnd?.(toolCallId, toolName);\n }\n\n if (c === undefined) return;\n\n // TODO how to handle new ToolResult({ result: undefined })?\n const result = new ToolResponse({\n artifact: c.artifact,\n result: c.result,\n isError: c.isError,\n messages: c.messages,\n modelContent: c.modelContent,\n });\n streamController.setResponse(result);\n controller.enqueue({\n type: \"result\",\n path: chunk.path,\n ...result,\n });\n },\n (e) => {\n if (isExecuting) {\n options.onExecutionEnd?.(toolCallId, toolName);\n }\n\n const result = new ToolResponse({\n result: String(e),\n isError: true,\n });\n\n streamController.setResponse(result);\n controller.enqueue({\n type: \"result\",\n path: chunk.path,\n ...result,\n });\n },\n );\n if (promise) {\n toolCallPromises.set(toolCallId, promise);\n }\n break;\n }\n\n case \"part-finish\": {\n if (chunk.meta.type !== \"tool-call\") break;\n\n const { toolCallId } = chunk.meta;\n const toolCallPromise = toolCallPromises.get(toolCallId);\n if (toolCallPromise) {\n toolCallPromise.then(() => {\n toolCallPromises.delete(toolCallId);\n toolCallControllers.delete(toolCallId);\n\n controller.enqueue(chunk);\n });\n } else {\n controller.enqueue(chunk);\n }\n }\n }\n },\n async flush() {\n await Promise.all(toolCallPromises.values());\n },\n });\n\n return readable\n .pipeThrough(new AssistantMetaTransformStream())\n .pipeThrough(transform);\n });\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ToolExecutionStream.js","names":[],"sources":["../../../src/core/tool/ToolExecutionStream.ts"],"sourcesContent":["import sjson from \"secure-json-parse\";\nimport type { AssistantStreamChunk } from \"../AssistantStreamChunk\";\nimport {\n type AssistantMetaStreamChunk,\n AssistantMetaTransformStream,\n} from \"../utils/stream/AssistantMetaTransformStream\";\nimport { PipeableTransformStream } from \"../utils/stream/PipeableTransformStream\";\nimport type {\n ReadonlyJSONObject,\n ReadonlyJSONValue,\n} from \"../../utils/json/json-value\";\nimport { ToolResponse } from \"./ToolResponse\";\nimport { withPromiseOrValue } from \"../utils/withPromiseOrValue\";\nimport { ToolCallReaderImpl } from \"./ToolCallReader\";\nimport type { ToolCallReader } from \"./tool-types\";\n\ntype ToolCallback = (toolCall: {\n toolCallId: string;\n toolName: string;\n args: ReadonlyJSONObject;\n}) =>\n | Promise<ToolResponse<ReadonlyJSONValue>>\n | ToolResponse<ReadonlyJSONValue>\n | undefined;\n\ntype ToolStreamCallback = <\n TArgs extends ReadonlyJSONObject = ReadonlyJSONObject,\n TResult extends ReadonlyJSONValue = ReadonlyJSONValue,\n>(toolCall: {\n reader: ToolCallReader<TArgs, TResult>;\n toolCallId: string;\n toolName: string;\n}) => void;\n\ntype ToolExecutionOptions = {\n execute: ToolCallback;\n streamCall: ToolStreamCallback;\n onExecutionStart?:\n | ((toolCallId: string, toolName: string) => void)\n | undefined;\n onExecutionEnd?: ((toolCallId: string, toolName: string) => void) | undefined;\n};\n\nexport class ToolExecutionStream extends PipeableTransformStream<\n AssistantStreamChunk,\n AssistantStreamChunk\n> {\n constructor(options: ToolExecutionOptions) {\n const toolCallPromises = new Map<string, PromiseLike<void>>();\n const toolCallControllers = new Map<\n string,\n ToolCallReaderImpl<ReadonlyJSONObject, ReadonlyJSONValue>\n >();\n\n super((readable) => {\n const transform = new TransformStream<\n AssistantMetaStreamChunk,\n AssistantStreamChunk\n >({\n async transform(chunk, controller) {\n // forward everything\n if (chunk.type !== \"part-finish\" || chunk.meta.type !== \"tool-call\") {\n controller.enqueue(chunk);\n }\n\n const type = chunk.type;\n\n switch (type) {\n case \"part-start\":\n if (chunk.part.type === \"tool-call\") {\n const reader = new ToolCallReaderImpl<\n ReadonlyJSONObject,\n ReadonlyJSONValue\n >();\n toolCallControllers.set(chunk.part.toolCallId, reader);\n\n options.streamCall({\n reader,\n toolCallId: chunk.part.toolCallId,\n toolName: chunk.part.toolName,\n });\n }\n break;\n case \"text-delta\": {\n if (chunk.meta.type === \"tool-call\") {\n const toolCallId = chunk.meta.toolCallId;\n\n const controller = toolCallControllers.get(toolCallId);\n if (!controller)\n throw new Error(\"No controller found for tool call\");\n // Awaited so the writer lock is released (and argsText updated)\n // before the next chunk acquires the writer.\n await controller.appendArgsTextDelta(chunk.textDelta);\n }\n break;\n }\n case \"result\": {\n if (chunk.meta.type !== \"tool-call\") break;\n\n const { toolCallId } = chunk.meta;\n const controller = toolCallControllers.get(toolCallId);\n if (!controller)\n throw new Error(\"No controller found for tool call\");\n controller.setResponse(\n new ToolResponse({\n result: chunk.result,\n artifact: chunk.artifact,\n isError: chunk.isError,\n modelContent: chunk.modelContent,\n }),\n );\n break;\n }\n case \"tool-call-args-text-finish\": {\n if (chunk.meta.type !== \"tool-call\") break;\n\n const { toolCallId, toolName } = chunk.meta;\n const streamController = toolCallControllers.get(toolCallId)!;\n if (!streamController)\n throw new Error(\"No controller found for tool call\");\n\n // Args fully streamed: close the reader so awaited absent fields\n // resolve. Awaited so the close settles before the writer is reused.\n await streamController.finishArgsText();\n\n let isExecuting = false;\n const promise = withPromiseOrValue(\n () => {\n let args: ReadonlyJSONObject;\n try {\n args = sjson.parse(\n streamController.argsText,\n ) as ReadonlyJSONObject;\n } catch (e) {\n throw new Error(\n `Function parameter parsing failed. ${JSON.stringify((e as Error).message)}`,\n );\n }\n\n const executeResult = options.execute({\n toolCallId,\n toolName,\n args,\n });\n\n // Only mark as executing if the tool has frontend execution\n if (executeResult !== undefined) {\n isExecuting = true;\n options.onExecutionStart?.(toolCallId, toolName);\n }\n\n return executeResult;\n },\n (c) => {\n if (isExecuting) {\n options.onExecutionEnd?.(toolCallId, toolName);\n }\n\n if (c === undefined) return;\n\n // TODO how to handle new ToolResult({ result: undefined })?\n const result = new ToolResponse({\n artifact: c.artifact,\n result: c.result,\n isError: c.isError,\n messages: c.messages,\n modelContent: c.modelContent,\n });\n streamController.setResponse(result);\n controller.enqueue({\n type: \"result\",\n path: chunk.path,\n ...result,\n });\n },\n (e) => {\n if (isExecuting) {\n options.onExecutionEnd?.(toolCallId, toolName);\n }\n\n const result = new ToolResponse({\n result: String(e),\n isError: true,\n });\n\n streamController.setResponse(result);\n controller.enqueue({\n type: \"result\",\n path: chunk.path,\n ...result,\n });\n },\n );\n if (promise) {\n toolCallPromises.set(toolCallId, promise);\n }\n break;\n }\n\n case \"part-finish\": {\n if (chunk.meta.type !== \"tool-call\") break;\n\n const { toolCallId } = chunk.meta;\n const toolCallPromise = toolCallPromises.get(toolCallId);\n if (toolCallPromise) {\n toolCallPromise.then(() => {\n toolCallPromises.delete(toolCallId);\n toolCallControllers.delete(toolCallId);\n\n controller.enqueue(chunk);\n });\n } else {\n controller.enqueue(chunk);\n }\n }\n }\n },\n async flush() {\n await Promise.all(toolCallPromises.values());\n },\n });\n\n return readable\n .pipeThrough(new AssistantMetaTransformStream())\n .pipeThrough(transform);\n });\n }\n}\n"],"mappings":"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"}
|
|
@@ -15,6 +15,8 @@ type ToToolsJSONSchemaOptions = {
|
|
|
15
15
|
/**
|
|
16
16
|
* Filter to determine which tools to include.
|
|
17
17
|
* Defaults to excluding disabled tools and backend tools.
|
|
18
|
+
*
|
|
19
|
+
* Tools with backend-default parameters are always excluded.
|
|
18
20
|
*/
|
|
19
21
|
filter?: (name: string, tool: Tool) => boolean;
|
|
20
22
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema-utils.d.ts","names":[],"sources":["../../../src/core/tool/schema-utils.ts"],"mappings":";;;;;;;AAOA;KAAY,cAAA;EACV,WAAA;EACA,UAAA,EAAY,WAAA;EACZ,eAAA,GAAkB,eAAe;AAAA;AAAA,KAGvB,wBAAA;EAHV;;;AAAiC;
|
|
1
|
+
{"version":3,"file":"schema-utils.d.ts","names":[],"sources":["../../../src/core/tool/schema-utils.ts"],"mappings":";;;;;;;AAOA;KAAY,cAAA;EACV,WAAA;EACA,UAAA,EAAY,WAAA;EACZ,eAAA,GAAkB,eAAe;AAAA;AAAA,KAGvB,wBAAA;EAHV;;;AAAiC;AAGnC;;EAOE,MAAA,IAAU,IAAA,UAAc,IAAA,EAAM,IAAI;AAAA;;;;;;AAAA;AA8CpC;;;iBAAgB,YAAA,CACd,MAAA,EAAQ,gBAAA,GAAmB,WAAA,GAC1B,WAAA;;;;;iBA+Ca,mBAAA,CAAoB,MAAA,EAAQ,WAAA,GAAc,WAAW;;;;;AA/CvD;AA+Cd;;;;iBA2CgB,iBAAA,CACd,KAAA,EAAO,MAAA,SAAe,IAAA,eACtB,OAAA,GAAS,wBAAA,GACR,MAAA,SAAe,cAAA"}
|
|
@@ -45,7 +45,10 @@ function toPartialJSONSchema(schema) {
|
|
|
45
45
|
return result;
|
|
46
46
|
}
|
|
47
47
|
function defaultToolFilter(_name, tool) {
|
|
48
|
-
return !tool.disabled && tool.type !== "backend";
|
|
48
|
+
return !tool.disabled && tool.type !== "backend" && (tool.type !== "frontend" || tool.execute !== void 0);
|
|
49
|
+
}
|
|
50
|
+
function toolHasUploadableParameters(tool) {
|
|
51
|
+
return tool.parameters !== void 0 && !tool.unstable_backendDefault?.parameters;
|
|
49
52
|
}
|
|
50
53
|
/**
|
|
51
54
|
* Converts a record of tools to a record of tool definitions with JSON Schema parameters.
|
|
@@ -59,7 +62,7 @@ function defaultToolFilter(_name, tool) {
|
|
|
59
62
|
function toToolsJSONSchema(tools, options = {}) {
|
|
60
63
|
if (!tools) return {};
|
|
61
64
|
const filter = options.filter ?? defaultToolFilter;
|
|
62
|
-
return Object.fromEntries(Object.entries(tools).filter(([name, tool]) => filter(name, tool)
|
|
65
|
+
return Object.fromEntries(Object.entries(tools).filter(([name, tool]) => filter(name, tool)).filter((entry) => toolHasUploadableParameters(entry[1])).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([name, tool]) => [name, {
|
|
63
66
|
...tool.description && { description: tool.description },
|
|
64
67
|
parameters: toJSONSchema(tool.parameters),
|
|
65
68
|
...tool.providerOptions && { providerOptions: tool.providerOptions }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema-utils.js","names":[],"sources":["../../../src/core/tool/schema-utils.ts"],"sourcesContent":["import type { JSONSchema7 } from \"json-schema\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport type { ProviderOptions, Tool } from \"./tool-types\";\n\n/**\n * Type for a tool definition with JSON Schema parameters.\n */\nexport type ToolJSONSchema = {\n description?: string;\n parameters: JSONSchema7;\n providerOptions?: ProviderOptions;\n};\n\nexport type ToToolsJSONSchemaOptions = {\n /**\n * Filter to determine which tools to include.\n * Defaults to excluding disabled tools and backend tools.\n */\n filter?: (name: string, tool: Tool) => boolean;\n};\n\nfunction isStandardSchema(schema: unknown): schema is StandardSchemaV1 & {\n \"~standard\": StandardSchemaV1[\"~standard\"] & {\n toJSONSchema?: () => unknown;\n jsonSchema?: { input?: () => unknown; output?: () => unknown };\n };\n} {\n return (\n typeof schema === \"object\" &&\n schema !== null &&\n \"~standard\" in schema &&\n typeof (schema as StandardSchemaV1)[\"~standard\"] === \"object\"\n );\n}\n\nfunction hasToJSONSchemaMethod(\n schema: unknown,\n): schema is { toJSONSchema: () => unknown } {\n return (\n typeof schema === \"object\" &&\n schema !== null &&\n \"toJSONSchema\" in schema &&\n typeof (schema as { toJSONSchema: unknown }).toJSONSchema === \"function\"\n );\n}\n\nfunction hasToJSONMethod(schema: unknown): schema is { toJSON: () => unknown } {\n return (\n typeof schema === \"object\" &&\n schema !== null &&\n \"toJSON\" in schema &&\n typeof (schema as { toJSON: unknown }).toJSON === \"function\"\n );\n}\n\n/**\n * Converts a schema to JSONSchema7.\n * Supports:\n * - StandardSchemaV1 with ~standard.toJSONSchema (e.g., Zod v4)\n * - StandardSchemaV1 with ~standard.jsonSchema.input() (e.g., Zod v4)\n * - Objects with toJSONSchema() method (e.g., Zod v4)\n * - Objects with toJSON() method\n * - Plain JSONSchema7 objects (must have a \"type\" property)\n */\nexport function toJSONSchema(\n schema: StandardSchemaV1 | JSONSchema7,\n): JSONSchema7 {\n // StandardSchemaV1 with ~standard.toJSONSchema (e.g., Zod v4)\n if (isStandardSchema(schema)) {\n const toJSONSchemaMethod = schema[\"~standard\"].toJSONSchema;\n if (typeof toJSONSchemaMethod === \"function\") {\n return toJSONSchemaMethod() as JSONSchema7;\n }\n\n // StandardSchemaV1 with ~standard.jsonSchema.input()\n const jsonSchema = schema[\"~standard\"].jsonSchema;\n if (\n typeof jsonSchema === \"object\" &&\n jsonSchema !== null &&\n typeof jsonSchema.input === \"function\"\n ) {\n return jsonSchema.input() as JSONSchema7;\n }\n }\n\n // toJSONSchema method on the schema itself\n if (hasToJSONSchemaMethod(schema)) {\n return schema.toJSONSchema() as JSONSchema7;\n }\n\n // toJSON method on the schema\n if (hasToJSONMethod(schema)) {\n return schema.toJSON() as JSONSchema7;\n }\n\n // If it's a Standard Schema that we couldn't convert, throw a helpful error\n if (isStandardSchema(schema)) {\n throw new Error(\n \"Could not convert schema to JSON Schema. \" +\n \"The schema implements Standard Schema but does not support JSON Schema conversion. \" +\n \"If you are using Zod, please upgrade to Zod v4 (npm install zod@latest). \" +\n \"Alternatively, pass a plain JSON Schema object instead.\",\n );\n }\n\n // Already a plain JSONSchema7\n return schema as JSONSchema7;\n}\n\n/**\n * Returns a copy of the JSON Schema with `required` removed recursively,\n * making every property optional. Array item schemas are left unchanged.\n */\nexport function toPartialJSONSchema(schema: JSONSchema7): JSONSchema7 {\n const { required: _, ...result } = schema;\n\n if (result.properties) {\n result.properties = Object.fromEntries(\n Object.entries(result.properties).map(([key, prop]) => {\n if (typeof prop === \"object\" && prop !== null && !Array.isArray(prop)) {\n const p = prop as JSONSchema7;\n return [key, p.properties != null ? toPartialJSONSchema(p) : prop];\n }\n return [key, prop];\n }),\n );\n }\n\n return result;\n}\n\nfunction defaultToolFilter(_name: string, tool: Tool): boolean {\n return !tool.disabled
|
|
1
|
+
{"version":3,"file":"schema-utils.js","names":[],"sources":["../../../src/core/tool/schema-utils.ts"],"sourcesContent":["import type { JSONSchema7 } from \"json-schema\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport type { ProviderOptions, Tool } from \"./tool-types\";\n\n/**\n * Type for a tool definition with JSON Schema parameters.\n */\nexport type ToolJSONSchema = {\n description?: string;\n parameters: JSONSchema7;\n providerOptions?: ProviderOptions;\n};\n\nexport type ToToolsJSONSchemaOptions = {\n /**\n * Filter to determine which tools to include.\n * Defaults to excluding disabled tools and backend tools.\n *\n * Tools with backend-default parameters are always excluded.\n */\n filter?: (name: string, tool: Tool) => boolean;\n};\n\nfunction isStandardSchema(schema: unknown): schema is StandardSchemaV1 & {\n \"~standard\": StandardSchemaV1[\"~standard\"] & {\n toJSONSchema?: () => unknown;\n jsonSchema?: { input?: () => unknown; output?: () => unknown };\n };\n} {\n return (\n typeof schema === \"object\" &&\n schema !== null &&\n \"~standard\" in schema &&\n typeof (schema as StandardSchemaV1)[\"~standard\"] === \"object\"\n );\n}\n\nfunction hasToJSONSchemaMethod(\n schema: unknown,\n): schema is { toJSONSchema: () => unknown } {\n return (\n typeof schema === \"object\" &&\n schema !== null &&\n \"toJSONSchema\" in schema &&\n typeof (schema as { toJSONSchema: unknown }).toJSONSchema === \"function\"\n );\n}\n\nfunction hasToJSONMethod(schema: unknown): schema is { toJSON: () => unknown } {\n return (\n typeof schema === \"object\" &&\n schema !== null &&\n \"toJSON\" in schema &&\n typeof (schema as { toJSON: unknown }).toJSON === \"function\"\n );\n}\n\n/**\n * Converts a schema to JSONSchema7.\n * Supports:\n * - StandardSchemaV1 with ~standard.toJSONSchema (e.g., Zod v4)\n * - StandardSchemaV1 with ~standard.jsonSchema.input() (e.g., Zod v4)\n * - Objects with toJSONSchema() method (e.g., Zod v4)\n * - Objects with toJSON() method\n * - Plain JSONSchema7 objects (must have a \"type\" property)\n */\nexport function toJSONSchema(\n schema: StandardSchemaV1 | JSONSchema7,\n): JSONSchema7 {\n // StandardSchemaV1 with ~standard.toJSONSchema (e.g., Zod v4)\n if (isStandardSchema(schema)) {\n const toJSONSchemaMethod = schema[\"~standard\"].toJSONSchema;\n if (typeof toJSONSchemaMethod === \"function\") {\n return toJSONSchemaMethod() as JSONSchema7;\n }\n\n // StandardSchemaV1 with ~standard.jsonSchema.input()\n const jsonSchema = schema[\"~standard\"].jsonSchema;\n if (\n typeof jsonSchema === \"object\" &&\n jsonSchema !== null &&\n typeof jsonSchema.input === \"function\"\n ) {\n return jsonSchema.input() as JSONSchema7;\n }\n }\n\n // toJSONSchema method on the schema itself\n if (hasToJSONSchemaMethod(schema)) {\n return schema.toJSONSchema() as JSONSchema7;\n }\n\n // toJSON method on the schema\n if (hasToJSONMethod(schema)) {\n return schema.toJSON() as JSONSchema7;\n }\n\n // If it's a Standard Schema that we couldn't convert, throw a helpful error\n if (isStandardSchema(schema)) {\n throw new Error(\n \"Could not convert schema to JSON Schema. \" +\n \"The schema implements Standard Schema but does not support JSON Schema conversion. \" +\n \"If you are using Zod, please upgrade to Zod v4 (npm install zod@latest). \" +\n \"Alternatively, pass a plain JSON Schema object instead.\",\n );\n }\n\n // Already a plain JSONSchema7\n return schema as JSONSchema7;\n}\n\n/**\n * Returns a copy of the JSON Schema with `required` removed recursively,\n * making every property optional. Array item schemas are left unchanged.\n */\nexport function toPartialJSONSchema(schema: JSONSchema7): JSONSchema7 {\n const { required: _, ...result } = schema;\n\n if (result.properties) {\n result.properties = Object.fromEntries(\n Object.entries(result.properties).map(([key, prop]) => {\n if (typeof prop === \"object\" && prop !== null && !Array.isArray(prop)) {\n const p = prop as JSONSchema7;\n return [key, p.properties != null ? toPartialJSONSchema(p) : prop];\n }\n return [key, prop];\n }),\n );\n }\n\n return result;\n}\n\nfunction defaultToolFilter(_name: string, tool: Tool): boolean {\n return (\n !tool.disabled &&\n tool.type !== \"backend\" &&\n (tool.type !== \"frontend\" || tool.execute !== undefined)\n );\n}\n\nfunction toolHasUploadableParameters(\n tool: Tool,\n): tool is Tool & { parameters: NonNullable<Tool[\"parameters\"]> } {\n return (\n tool.parameters !== undefined && !tool.unstable_backendDefault?.parameters\n );\n}\n\n/**\n * Converts a record of tools to a record of tool definitions with JSON Schema parameters.\n * By default, filters out disabled tools and backend tools.\n *\n * Entries are emitted in alphabetical order so the resulting request body is\n * byte-identical regardless of the order in which tools were registered. This\n * keeps provider prompt caches stable across renders that mount tools in\n * different orders.\n */\nexport function toToolsJSONSchema(\n tools: Record<string, Tool> | undefined,\n options: ToToolsJSONSchemaOptions = {},\n): Record<string, ToolJSONSchema> {\n if (!tools) return {};\n\n const filter = options.filter ?? defaultToolFilter;\n\n return Object.fromEntries(\n Object.entries(tools)\n .filter(([name, tool]) => filter(name, tool))\n .filter(\n (\n entry,\n ): entry is [\n string,\n Tool & { parameters: NonNullable<Tool[\"parameters\"]> },\n ] => toolHasUploadableParameters(entry[1]),\n )\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n .map(([name, tool]) => [\n name,\n {\n ...(tool.description && { description: tool.description }),\n parameters: toJSONSchema(tool.parameters),\n ...(tool.providerOptions && {\n providerOptions: tool.providerOptions,\n }),\n },\n ]),\n );\n}\n"],"mappings":";AAuBA,SAAS,iBAAiB,QAKxB;CACA,OACE,OAAO,WAAW,YAClB,WAAW,QACX,eAAe,UACf,OAAQ,OAA4B,iBAAiB;AAEzD;AAEA,SAAS,sBACP,QAC2C;CAC3C,OACE,OAAO,WAAW,YAClB,WAAW,QACX,kBAAkB,UAClB,OAAQ,OAAqC,iBAAiB;AAElE;AAEA,SAAS,gBAAgB,QAAsD;CAC7E,OACE,OAAO,WAAW,YAClB,WAAW,QACX,YAAY,UACZ,OAAQ,OAA+B,WAAW;AAEtD;;;;;;;;;;AAWA,SAAgB,aACd,QACa;CAEb,IAAI,iBAAiB,MAAM,GAAG;EAC5B,MAAM,qBAAqB,OAAO,aAAa;EAC/C,IAAI,OAAO,uBAAuB,YAChC,OAAO,mBAAmB;EAI5B,MAAM,aAAa,OAAO,aAAa;EACvC,IACE,OAAO,eAAe,YACtB,eAAe,QACf,OAAO,WAAW,UAAU,YAE5B,OAAO,WAAW,MAAM;CAE5B;CAGA,IAAI,sBAAsB,MAAM,GAC9B,OAAO,OAAO,aAAa;CAI7B,IAAI,gBAAgB,MAAM,GACxB,OAAO,OAAO,OAAO;CAIvB,IAAI,iBAAiB,MAAM,GACzB,MAAM,IAAI,MACR,8PAIF;CAIF,OAAO;AACT;;;;;AAMA,SAAgB,oBAAoB,QAAkC;CACpE,MAAM,EAAE,UAAU,GAAG,GAAG,WAAW;CAEnC,IAAI,OAAO,YACT,OAAO,aAAa,OAAO,YACzB,OAAO,QAAQ,OAAO,UAAU,EAAE,KAAK,CAAC,KAAK,UAAU;EACrD,IAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,CAAC,MAAM,QAAQ,IAAI,GAAG;GACrE,MAAM,IAAI;GACV,OAAO,CAAC,KAAK,EAAE,cAAc,OAAO,oBAAoB,CAAC,IAAI,IAAI;EACnE;EACA,OAAO,CAAC,KAAK,IAAI;CACnB,CAAC,CACH;CAGF,OAAO;AACT;AAEA,SAAS,kBAAkB,OAAe,MAAqB;CAC7D,OACE,CAAC,KAAK,YACN,KAAK,SAAS,cACb,KAAK,SAAS,cAAc,KAAK,YAAY,KAAA;AAElD;AAEA,SAAS,4BACP,MACgE;CAChE,OACE,KAAK,eAAe,KAAA,KAAa,CAAC,KAAK,yBAAyB;AAEpE;;;;;;;;;;AAWA,SAAgB,kBACd,OACA,UAAoC,CAAC,GACL;CAChC,IAAI,CAAC,OAAO,OAAO,CAAC;CAEpB,MAAM,SAAS,QAAQ,UAAU;CAEjC,OAAO,OAAO,YACZ,OAAO,QAAQ,KAAK,EACjB,QAAQ,CAAC,MAAM,UAAU,OAAO,MAAM,IAAI,CAAC,EAC3C,QAEG,UAIG,4BAA4B,MAAM,EAAE,CAC3C,EACC,MAAM,CAAC,IAAI,CAAC,OAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,CAAE,EAC/C,KAAK,CAAC,MAAM,UAAU,CACrB,MACA;EACE,GAAI,KAAK,eAAe,EAAE,aAAa,KAAK,YAAY;EACxD,YAAY,aAAa,KAAK,UAAU;EACxC,GAAI,KAAK,mBAAmB,EAC1B,iBAAiB,KAAK,gBACxB;CACF,CACF,CAAC,CACL;AACF"}
|
|
@@ -190,7 +190,7 @@ type FrontendTool<TArgs extends Record<string, unknown> = Record<string, unknown
|
|
|
190
190
|
description?: string | undefined; /** Schema for the arguments the model must provide when calling the tool. */
|
|
191
191
|
parameters: StandardSchemaV1<TArgs> | JSONSchema7; /** Prevents the tool from being exposed to the model while true. */
|
|
192
192
|
disabled?: boolean; /** Executes the tool after the model provides valid arguments. */
|
|
193
|
-
execute
|
|
193
|
+
execute?: ToolExecuteFunction<TArgs, TResult>; /** Converts the execution result into model-visible output. */
|
|
194
194
|
toModelOutput?: ToolModelOutputFunction<TArgs, TResult>; /** Handles invalid tool arguments when schema validation fails. */
|
|
195
195
|
experimental_onSchemaValidationError?: OnSchemaValidationErrorFunction<TResult>;
|
|
196
196
|
providerOptions?: ProviderOptions;
|
|
@@ -206,6 +206,45 @@ type HumanTool<TArgs extends Record<string, unknown> = Record<string, unknown>,
|
|
|
206
206
|
experimental_onSchemaValidationError?: undefined;
|
|
207
207
|
providerOptions?: ProviderOptions;
|
|
208
208
|
};
|
|
209
|
+
type ProviderTool<TArgs extends Record<string, unknown> = Record<string, unknown>, TResult = unknown> = ToolBase<TArgs, TResult> & {
|
|
210
|
+
/** Tool executed by the LLM provider rather than assistant-ui. */type: "provider"; /** Provider-defined tool identifier, e.g. `openai.web_search_preview`. */
|
|
211
|
+
providerId: `${string}.${string}`; /** Schema used by adapters for validation and type plumbing. */
|
|
212
|
+
parameters?: StandardSchemaV1<TArgs> | JSONSchema7 | undefined; /** Provider-specific configuration for this tool. */
|
|
213
|
+
args: Record<string, unknown>;
|
|
214
|
+
/**
|
|
215
|
+
* Whether provider results may arrive after the originating response turn.
|
|
216
|
+
*/
|
|
217
|
+
supportsDeferredResults?: boolean;
|
|
218
|
+
description?: undefined;
|
|
219
|
+
disabled?: boolean;
|
|
220
|
+
execute?: undefined;
|
|
221
|
+
toModelOutput?: undefined;
|
|
222
|
+
experimental_onSchemaValidationError?: undefined;
|
|
223
|
+
providerOptions?: ProviderOptions;
|
|
224
|
+
};
|
|
225
|
+
type McpServerConfig = {
|
|
226
|
+
/** Connect to an MCP server over Streamable HTTP or server-sent events. */type: "http" | "sse";
|
|
227
|
+
url: string;
|
|
228
|
+
headers?: Record<string, string>;
|
|
229
|
+
redirect?: "follow" | "error";
|
|
230
|
+
} | {
|
|
231
|
+
/** Start and connect to a local MCP server over stdio. */type: "stdio";
|
|
232
|
+
command: string;
|
|
233
|
+
args?: readonly string[];
|
|
234
|
+
env?: Record<string, string>;
|
|
235
|
+
cwd?: string;
|
|
236
|
+
};
|
|
237
|
+
type McpTool = ToolBase<Record<string, unknown>, unknown> & {
|
|
238
|
+
/** Tools loaded from an MCP server by a server adapter. */type: "mcp";
|
|
239
|
+
server: McpServerConfig;
|
|
240
|
+
description?: undefined;
|
|
241
|
+
parameters?: undefined;
|
|
242
|
+
disabled?: boolean;
|
|
243
|
+
execute?: undefined;
|
|
244
|
+
toModelOutput?: undefined;
|
|
245
|
+
experimental_onSchemaValidationError?: undefined;
|
|
246
|
+
providerOptions?: undefined;
|
|
247
|
+
};
|
|
209
248
|
/**
|
|
210
249
|
* Definition for a tool that can be exposed to the assistant model.
|
|
211
250
|
*
|
|
@@ -247,20 +286,20 @@ type HumanTool<TArgs extends Record<string, unknown> = Record<string, unknown>,
|
|
|
247
286
|
* };
|
|
248
287
|
* ```
|
|
249
288
|
*/
|
|
250
|
-
type Tool<TArgs extends Record<string, unknown> = Record<string, unknown>, TResult = unknown> = FrontendTool<TArgs, TResult> | BackendTool<TArgs, TResult> | HumanTool<TArgs, TResult> | ToolWithoutType<TArgs, TResult>;
|
|
289
|
+
type Tool<TArgs extends Record<string, unknown> = Record<string, unknown>, TResult = unknown> = FrontendTool<TArgs, TResult> | BackendTool<TArgs, TResult> | HumanTool<TArgs, TResult> | ProviderTool<TArgs, TResult> | McpTool | ToolWithoutType<TArgs, TResult>;
|
|
251
290
|
/**
|
|
252
291
|
* A tool as *authored* — the permissive counterpart to {@link Tool}. Unlike
|
|
253
292
|
* {@link Tool}, a `backend` entry may declare `description`, `parameters`, and a
|
|
254
293
|
* server-side `execute`. Use this for the input of authoring helpers (e.g.
|
|
255
294
|
* `defineToolkit`); the canonical {@link Tool} is the output.
|
|
256
295
|
*/
|
|
257
|
-
type ToolDeclaration<TArgs extends Record<string, unknown> = Record<string, unknown>, TResult = unknown> = FrontendTool<TArgs, TResult> | BackendToolDeclaration<TArgs, TResult> | HumanTool<TArgs, TResult> | ToolWithoutType<TArgs, TResult>;
|
|
296
|
+
type ToolDeclaration<TArgs extends Record<string, unknown> = Record<string, unknown>, TResult = unknown> = FrontendTool<TArgs, TResult> | BackendToolDeclaration<TArgs, TResult> | HumanTool<TArgs, TResult> | ProviderTool<TArgs, TResult> | McpTool | ToolWithoutType<TArgs, TResult>;
|
|
258
297
|
/**
|
|
259
298
|
* @deprecated Use {@link Tool} with an explicit `type` field instead.
|
|
260
299
|
*/
|
|
261
|
-
type ToolWithoutType<TArgs extends Record<string, unknown> = Record<string, unknown>, TResult = unknown> = (Omit<FrontendTool<TArgs, TResult>, "type"> | Omit<BackendTool<TArgs, TResult>, "type"> | Omit<HumanTool<TArgs, TResult>, "type">) & {
|
|
300
|
+
type ToolWithoutType<TArgs extends Record<string, unknown> = Record<string, unknown>, TResult = unknown> = (Omit<FrontendTool<TArgs, TResult>, "type"> | Omit<BackendTool<TArgs, TResult>, "type"> | Omit<HumanTool<TArgs, TResult>, "type"> | Omit<ProviderTool<TArgs, TResult>, "type">) & {
|
|
262
301
|
type?: undefined;
|
|
263
302
|
};
|
|
264
303
|
//#endregion
|
|
265
|
-
export { ProviderOptions, Tool, ToolCallArgsReader, ToolCallReader, ToolCallResponseReader, ToolDeclaration, ToolExecuteFunction, ToolExecutionContext, ToolModelContentPart, ToolModelOutputFunction, ToolStreamCallFunction, ToolWithoutType };
|
|
304
|
+
export { McpServerConfig, ProviderOptions, Tool, ToolCallArgsReader, ToolCallReader, ToolCallResponseReader, ToolDeclaration, ToolExecuteFunction, ToolExecutionContext, ToolModelContentPart, ToolModelOutputFunction, ToolStreamCallFunction, ToolWithoutType };
|
|
266
305
|
//# sourceMappingURL=tool-types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-types.d.ts","names":[],"sources":["../../../src/core/tool/tool-types.ts"],"mappings":";;;;;;;KAMY,oBAAA;8EAGG,IAAA,UAHH;EAAA,SAKG,IAAA;AAAA;EALiB,4EASjB,IAAA;EAJA;;;;EAAA,SASA,IAAA,UAIQ;EAAA,SAFR,SAAA,UA8BH;EAAA,SA5BG,QAAA;AAAA;;;;;;;;;;;;;;;;;;;;AAqC0B;AAUzC;;;;;KAnBY,uBAAA,oBAA2C,OAAA;EA2BrC,2DAzBhB,UAAA,UA0B6B;EAxB7B,KAAA,EAAO,KAAA,EAwBJ;EAtBH,MAAA,EAAQ,OAAA;AAAA,eAEG,oBAAA,KACT,OAAA,UAAiB,oBAAA;;;;;;;;;UAUJ,kBAAA,eAAiC,MAAA;EA6BlC;;;;;;EAtBd,GAAA,eAAkB,QAAA,CAAS,KAAA,MACtB,SAAA,EAAW,KAAA,GACb,OAAA,CAAQ,UAAA,CAAW,KAAA,EAAO,KAAA;EA+Bb;;;;;;EAvBhB,YAAA,eAA2B,QAAA,CAAS,KAAA,MAC/B,SAAA,EAAW,KAAA,GACb,mBAAA,CAAoB,WAAA,CAAY,UAAA,CAAW,KAAA,EAAO,KAAA;EAuB9B;;;;;;EAfvB,UAAA,eAAyB,QAAA,CAAS,KAAA,MAC7B,SAAA,EAAW,KAAA,GACb,UAAA,CAAW,KAAA,EAAO,KAAA,+BACjB,mBAAA,CAAoB,CAAA;EAvBG;;;;;;EAgC3B,OAAA,eAAsB,QAAA,CAAS,KAAA,MAC1B,SAAA,EAAW,KAAA,GACb,UAAA,CAAW,KAAA,EAAO,KAAA,UAAe,KAAA,YAChC,mBAAA,CAAoB,CAAA;AAAA;AAAA,UAIT,sBAAA;EACf,GAAA,QAAW,OAAA,CAAQ,YAAA,CAAa,OAAA;AAAA;AAAA,UAGjB,cAAA,eACD,MAAA,oBAA0B,MAAA;EAGxC,IAAA,EAAM,kBAAA,CAAmB,KAAA;EACzB,QAAA,EAAU,sBAAA,CAAuB,OAAA;EApCV;;;;EA0CvB,MAAA;IACE,GAAA,QAAW,OAAA,CAAQ,OAAA;EAAA;AAAA;AAAA,KAIX,oBAAA;EAtCM,0DAwChB,UAAA,UAvCc;EAyCd,WAAA,EAAa,WAAA;EAzCuC;;;;EA8CpD,KAAA,GAAQ,OAAA,cAAqB,OAAO;AAAA;;;;KAM1B,mBAAA,oBACV,IAAA,EAAM,KAAA,EACN,OAAA,EAAS,oBAAA,KACN,OAAA,GAAU,OAAA,CAAQ,OAAA;AAAA,KAEX,sBAAA,eACI,MAAA,oBAA0B,MAAA,yCAGxC,MAAA,EAAQ,cAAA,CAAe,KAAA,EAAO,OAAA,GAC9B,OAAA,EAAS,oBAAA;AAAA,KAGN,+BAAA,YAA2C,mBAAmB,UAEjE,OAAA;;;;;;AAtDyB;AAI3B;;;;;;KAiEY,eAAA,GAAkB,MAAM,SAAS,MAAA;;;;;;;;AAhEJ;AAGzC;;;;;KA4EK,WAAA;AAAA,KAEA,QAAA,eACW,MAAA,oBAA0B,MAAA;EA1EP;;;EAgFjC,UAAA,GAAa,sBAAA,CAAuB,KAAA,EAAO,OAAA;EAzEvB;;;;;;;;;EAoFpB,OAAA,GAAU,WAAA;AAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"tool-types.d.ts","names":[],"sources":["../../../src/core/tool/tool-types.ts"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { TextStreamController } from "./core/modules/text.js";
|
|
2
|
-
import { ProviderOptions, Tool, ToolCallReader, ToolDeclaration, ToolModelContentPart, ToolModelOutputFunction } from "./core/tool/tool-types.js";
|
|
2
|
+
import { McpServerConfig, ProviderOptions, Tool, ToolCallReader, ToolDeclaration, ToolModelContentPart, ToolModelOutputFunction } from "./core/tool/tool-types.js";
|
|
3
3
|
import { ToolResponse, ToolResponseLike } from "./core/tool/ToolResponse.js";
|
|
4
4
|
import { ToolCallStreamController } from "./core/modules/tool-call.js";
|
|
5
5
|
import { AssistantMessage, AssistantMessageTiming, DataPart } from "./core/utils/types.js";
|
|
@@ -20,4 +20,4 @@ import { UIMessageStreamDecoder, UIMessageStreamDecoderOptions } from "./core/se
|
|
|
20
20
|
import { ToolExecutionStream } from "./core/tool/ToolExecutionStream.js";
|
|
21
21
|
import { ToToolsJSONSchemaOptions, ToolJSONSchema, toJSONSchema, toPartialJSONSchema, toToolsJSONSchema } from "./core/tool/schema-utils.js";
|
|
22
22
|
import { ToolResultStreamOptions, toolResultStream, unstable_runPendingTools } from "./core/tool/toolResultStream.js";
|
|
23
|
-
export { type AssistantMessage, AssistantMessageAccumulator, AssistantMessageStream, type AssistantMessageTiming, AssistantStream, type AssistantStreamChunk, type AssistantStreamController, AssistantTransportDecoder, AssistantTransportEncoder, type DataPart, DataStreamDecoder, DataStreamEncoder, type GenericAssistantMessage, type GenericFilePart, type GenericMessage, type GenericSystemMessage, type GenericTextPart, type GenericToolCallPart, type GenericToolMessage, type GenericToolResultPart, type GenericUserMessage, type ObjectStreamChunk, ObjectStreamResponse, PlainTextDecoder, PlainTextEncoder, type ProviderOptions, type TextStreamController, type ToToolsJSONSchemaOptions, type Tool, type ToolCallReader, type ToolCallStreamController, type ToolDeclaration, ToolExecutionStream, type ToolJSONSchema, type ToolModelContentPart, type ToolModelOutputFunction, ToolResponse, type ToolResponseLike, type ToolResultStreamOptions, type UIMessageStreamChunk, type UIMessageStreamDataChunk, UIMessageStreamDecoder, type UIMessageStreamDecoderOptions, createAssistantStream, createAssistantStreamController, createAssistantStreamResponse, createObjectStream, fromObjectStreamResponse, toGenericMessages, toJSONSchema, toPartialJSONSchema, toToolsJSONSchema, createInitialMessage as unstable_createInitialMessage, unstable_runPendingTools, toolResultStream as unstable_toolResultStream };
|
|
23
|
+
export { type AssistantMessage, AssistantMessageAccumulator, AssistantMessageStream, type AssistantMessageTiming, AssistantStream, type AssistantStreamChunk, type AssistantStreamController, AssistantTransportDecoder, AssistantTransportEncoder, type DataPart, DataStreamDecoder, DataStreamEncoder, type GenericAssistantMessage, type GenericFilePart, type GenericMessage, type GenericSystemMessage, type GenericTextPart, type GenericToolCallPart, type GenericToolMessage, type GenericToolResultPart, type GenericUserMessage, type McpServerConfig, type ObjectStreamChunk, ObjectStreamResponse, PlainTextDecoder, PlainTextEncoder, type ProviderOptions, type TextStreamController, type ToToolsJSONSchemaOptions, type Tool, type ToolCallReader, type ToolCallStreamController, type ToolDeclaration, ToolExecutionStream, type ToolJSONSchema, type ToolModelContentPart, type ToolModelOutputFunction, ToolResponse, type ToolResponseLike, type ToolResultStreamOptions, type UIMessageStreamChunk, type UIMessageStreamDataChunk, UIMessageStreamDecoder, type UIMessageStreamDecoderOptions, createAssistantStream, createAssistantStreamController, createAssistantStreamResponse, createObjectStream, fromObjectStreamResponse, toGenericMessages, toJSONSchema, toPartialJSONSchema, toToolsJSONSchema, createInitialMessage as unstable_createInitialMessage, unstable_runPendingTools, toolResultStream as unstable_toolResultStream };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "assistant-stream",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.20",
|
|
4
4
|
"description": "Streaming utilities for AI assistants",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"ioredis": "^5.11.0",
|
|
68
68
|
"redis": "^6.0.0",
|
|
69
69
|
"vitest": "^4.1.7",
|
|
70
|
-
"@assistant-ui/x-buildutils": "0.0.
|
|
70
|
+
"@assistant-ui/x-buildutils": "0.0.11"
|
|
71
71
|
},
|
|
72
72
|
"publishConfig": {
|
|
73
73
|
"access": "public",
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { ToolCallReaderImpl } from "./ToolCallReader";
|
|
3
|
+
|
|
4
|
+
type Args = {
|
|
5
|
+
required: string;
|
|
6
|
+
optional?: string;
|
|
7
|
+
items?: string[];
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const createReader = () => new ToolCallReaderImpl<Args, string>();
|
|
11
|
+
|
|
12
|
+
describe("ToolCallArgsReader.get", () => {
|
|
13
|
+
it("resolves with the value once the field is complete", async () => {
|
|
14
|
+
const reader = createReader();
|
|
15
|
+
const promise = reader.args.get("required");
|
|
16
|
+
|
|
17
|
+
await reader.appendArgsTextDelta('{"required":"hello"}');
|
|
18
|
+
|
|
19
|
+
expect(await promise).toBe("hello");
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("resolves to undefined for an absent field once args close", async () => {
|
|
23
|
+
const reader = createReader();
|
|
24
|
+
const promise = reader.args.get("optional");
|
|
25
|
+
|
|
26
|
+
await reader.appendArgsTextDelta('{"required":"hello"}');
|
|
27
|
+
await reader.finishArgsText();
|
|
28
|
+
|
|
29
|
+
// Previously this never resolved and deadlocked the tool.
|
|
30
|
+
expect(await promise).toBeUndefined();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("resolves to undefined for a field requested after args close", async () => {
|
|
34
|
+
const reader = createReader();
|
|
35
|
+
|
|
36
|
+
await reader.appendArgsTextDelta('{"required":"hello"}');
|
|
37
|
+
await reader.finishArgsText();
|
|
38
|
+
|
|
39
|
+
expect(await reader.args.get("optional")).toBeUndefined();
|
|
40
|
+
expect(await reader.args.get("required")).toBe("hello");
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("does not deadlock awaiting an optional arg inside a side effect", async () => {
|
|
44
|
+
const reader = createReader();
|
|
45
|
+
|
|
46
|
+
const sideEffect = (async () => {
|
|
47
|
+
const optional = await reader.args.get("optional");
|
|
48
|
+
return optional ?? "fallback";
|
|
49
|
+
})();
|
|
50
|
+
|
|
51
|
+
await reader.appendArgsTextDelta('{"required":"hello"}');
|
|
52
|
+
await reader.finishArgsText();
|
|
53
|
+
|
|
54
|
+
expect(await sideEffect).toBe("fallback");
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe("ToolCallArgsReader streams", () => {
|
|
59
|
+
it("closes streamValues when args close without the field", async () => {
|
|
60
|
+
const reader = createReader();
|
|
61
|
+
|
|
62
|
+
await reader.appendArgsTextDelta('{"required":"hello"}');
|
|
63
|
+
await reader.finishArgsText();
|
|
64
|
+
|
|
65
|
+
const seen: unknown[] = [];
|
|
66
|
+
for await (const value of reader.args.streamValues("items")) {
|
|
67
|
+
seen.push(value);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
expect(seen).toEqual([]);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("emits completed array items and closes via forEach", async () => {
|
|
74
|
+
const reader = createReader();
|
|
75
|
+
|
|
76
|
+
await reader.appendArgsTextDelta('{"required":"hi","items":["a","b"]}');
|
|
77
|
+
await reader.finishArgsText();
|
|
78
|
+
|
|
79
|
+
const seen: string[] = [];
|
|
80
|
+
for await (const item of reader.args.forEach("items")) {
|
|
81
|
+
seen.push(item);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
expect(seen).toEqual(["a", "b"]);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
@@ -32,6 +32,7 @@ function getField<T>(obj: T, fieldPath: (string | number)[]): unknown {
|
|
|
32
32
|
|
|
33
33
|
interface Handle {
|
|
34
34
|
update(args: unknown): void;
|
|
35
|
+
end(args: unknown): void;
|
|
35
36
|
dispose(): void;
|
|
36
37
|
}
|
|
37
38
|
|
|
@@ -74,6 +75,19 @@ class GetHandle<T, TValue> implements Handle {
|
|
|
74
75
|
}
|
|
75
76
|
}
|
|
76
77
|
|
|
78
|
+
end(args: unknown): void {
|
|
79
|
+
if (this.disposed) return;
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
const value = getField(args as T, this.fieldPath);
|
|
83
|
+
this.resolve(value as TValue);
|
|
84
|
+
} catch (e) {
|
|
85
|
+
this.reject(e);
|
|
86
|
+
} finally {
|
|
87
|
+
this.dispose();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
77
91
|
dispose(): void {
|
|
78
92
|
this.disposed = true;
|
|
79
93
|
}
|
|
@@ -118,6 +132,12 @@ class StreamValuesHandle<T> implements Handle {
|
|
|
118
132
|
}
|
|
119
133
|
}
|
|
120
134
|
|
|
135
|
+
end(): void {
|
|
136
|
+
if (this.disposed) return;
|
|
137
|
+
this.controller.close();
|
|
138
|
+
this.dispose();
|
|
139
|
+
}
|
|
140
|
+
|
|
121
141
|
dispose(): void {
|
|
122
142
|
this.disposed = true;
|
|
123
143
|
}
|
|
@@ -165,6 +185,12 @@ class StreamTextHandle<T> implements Handle {
|
|
|
165
185
|
}
|
|
166
186
|
}
|
|
167
187
|
|
|
188
|
+
end(): void {
|
|
189
|
+
if (this.disposed) return;
|
|
190
|
+
this.controller.close();
|
|
191
|
+
this.dispose();
|
|
192
|
+
}
|
|
193
|
+
|
|
168
194
|
dispose(): void {
|
|
169
195
|
this.disposed = true;
|
|
170
196
|
}
|
|
@@ -226,6 +252,12 @@ class ForEachHandle<T> implements Handle {
|
|
|
226
252
|
}
|
|
227
253
|
}
|
|
228
254
|
|
|
255
|
+
end(): void {
|
|
256
|
+
if (this.disposed) return;
|
|
257
|
+
this.controller.close();
|
|
258
|
+
this.dispose();
|
|
259
|
+
}
|
|
260
|
+
|
|
229
261
|
dispose(): void {
|
|
230
262
|
this.disposed = true;
|
|
231
263
|
}
|
|
@@ -238,6 +270,7 @@ export class ToolCallArgsReaderImpl<
|
|
|
238
270
|
private argTextDeltas: ReadableStream<string>;
|
|
239
271
|
private handles: Set<Handle> = new Set();
|
|
240
272
|
private args: unknown = parsePartialJsonObject("");
|
|
273
|
+
private finished = false;
|
|
241
274
|
|
|
242
275
|
constructor(argTextDeltas: ReadableStream<string>) {
|
|
243
276
|
this.argTextDeltas = argTextDeltas;
|
|
@@ -266,10 +299,12 @@ export class ToolCallArgsReaderImpl<
|
|
|
266
299
|
}
|
|
267
300
|
} catch (error) {
|
|
268
301
|
console.error("Error processing argument stream:", error);
|
|
269
|
-
|
|
302
|
+
} finally {
|
|
303
|
+
this.finished = true;
|
|
270
304
|
for (const handle of this.handles) {
|
|
271
|
-
handle.
|
|
305
|
+
handle.end(this.args);
|
|
272
306
|
}
|
|
307
|
+
this.handles.clear();
|
|
273
308
|
}
|
|
274
309
|
}
|
|
275
310
|
|
|
@@ -298,6 +333,11 @@ export class ToolCallArgsReaderImpl<
|
|
|
298
333
|
}
|
|
299
334
|
}
|
|
300
335
|
|
|
336
|
+
if (this.finished) {
|
|
337
|
+
handle.end(this.args);
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
|
|
301
341
|
this.handles.add(handle);
|
|
302
342
|
handle.update(this.args);
|
|
303
343
|
});
|
|
@@ -309,22 +349,23 @@ export class ToolCallArgsReaderImpl<
|
|
|
309
349
|
// Use a type assertion to convert the complex TypePath to a simple array
|
|
310
350
|
const simplePath = fieldPath as unknown as (string | number)[];
|
|
311
351
|
|
|
352
|
+
let handle: StreamValuesHandle<T> | undefined;
|
|
312
353
|
const stream = new ReadableStream<DeepPartial<TypeAtPath<T, PathT>>>({
|
|
313
354
|
start: (controller) => {
|
|
314
|
-
|
|
315
|
-
this.handles.add(handle);
|
|
355
|
+
handle = new StreamValuesHandle<T>(controller, simplePath);
|
|
356
|
+
if (!this.finished) this.handles.add(handle);
|
|
316
357
|
|
|
317
358
|
// Check current args immediately
|
|
318
359
|
handle.update(this.args);
|
|
360
|
+
|
|
361
|
+
if (this.finished) handle.end();
|
|
319
362
|
},
|
|
320
363
|
cancel: () => {
|
|
321
|
-
//
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
break;
|
|
327
|
-
}
|
|
364
|
+
// Dispose this stream's own handle (captured above) — scanning for the
|
|
365
|
+
// first match would dispose a concurrent streamValues()'s handle.
|
|
366
|
+
if (handle) {
|
|
367
|
+
handle.dispose();
|
|
368
|
+
this.handles.delete(handle);
|
|
328
369
|
}
|
|
329
370
|
},
|
|
330
371
|
});
|
|
@@ -340,22 +381,23 @@ export class ToolCallArgsReaderImpl<
|
|
|
340
381
|
// Use a type assertion to convert the complex TypePath to a simple array
|
|
341
382
|
const simplePath = fieldPath as unknown as (string | number)[];
|
|
342
383
|
|
|
384
|
+
let handle: StreamTextHandle<T> | undefined;
|
|
343
385
|
const stream = new ReadableStream<unknown>({
|
|
344
386
|
start: (controller) => {
|
|
345
|
-
|
|
346
|
-
this.handles.add(handle);
|
|
387
|
+
handle = new StreamTextHandle<T>(controller, simplePath);
|
|
388
|
+
if (!this.finished) this.handles.add(handle);
|
|
347
389
|
|
|
348
390
|
// Check current args immediately
|
|
349
391
|
handle.update(this.args);
|
|
392
|
+
|
|
393
|
+
if (this.finished) handle.end();
|
|
350
394
|
},
|
|
351
395
|
cancel: () => {
|
|
352
|
-
//
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
break;
|
|
358
|
-
}
|
|
396
|
+
// Dispose this stream's own handle (captured above) — scanning for the
|
|
397
|
+
// first match would dispose a concurrent streamText()'s handle.
|
|
398
|
+
if (handle) {
|
|
399
|
+
handle.dispose();
|
|
400
|
+
this.handles.delete(handle);
|
|
359
401
|
}
|
|
360
402
|
},
|
|
361
403
|
});
|
|
@@ -371,22 +413,23 @@ export class ToolCallArgsReaderImpl<
|
|
|
371
413
|
// Use a type assertion to convert the complex TypePath to a simple array
|
|
372
414
|
const simplePath = fieldPath as unknown as (string | number)[];
|
|
373
415
|
|
|
416
|
+
let handle: ForEachHandle<T> | undefined;
|
|
374
417
|
const stream = new ReadableStream<unknown>({
|
|
375
418
|
start: (controller) => {
|
|
376
|
-
|
|
377
|
-
this.handles.add(handle);
|
|
419
|
+
handle = new ForEachHandle<T>(controller, simplePath);
|
|
420
|
+
if (!this.finished) this.handles.add(handle);
|
|
378
421
|
|
|
379
422
|
// Check current args immediately
|
|
380
423
|
handle.update(this.args);
|
|
424
|
+
|
|
425
|
+
if (this.finished) handle.end();
|
|
381
426
|
},
|
|
382
427
|
cancel: () => {
|
|
383
|
-
//
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
break;
|
|
389
|
-
}
|
|
428
|
+
// Dispose this stream's own handle (captured above) — scanning for the
|
|
429
|
+
// first match would dispose a concurrent forEach()'s handle.
|
|
430
|
+
if (handle) {
|
|
431
|
+
handle.dispose();
|
|
432
|
+
this.handles.delete(handle);
|
|
390
433
|
}
|
|
391
434
|
},
|
|
392
435
|
});
|
|
@@ -439,6 +482,17 @@ export class ToolCallReaderImpl<
|
|
|
439
482
|
this.argsText += text;
|
|
440
483
|
}
|
|
441
484
|
|
|
485
|
+
async finishArgsText(): Promise<void> {
|
|
486
|
+
const writer = this.writable.getWriter();
|
|
487
|
+
try {
|
|
488
|
+
await writer.close();
|
|
489
|
+
} catch (err) {
|
|
490
|
+
console.warn(err);
|
|
491
|
+
} finally {
|
|
492
|
+
writer.releaseLock();
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
442
496
|
setResponse(value: ToolResponse<TResult>): void {
|
|
443
497
|
this.resolve(value);
|
|
444
498
|
}
|
|
@@ -57,7 +57,7 @@ export class ToolExecutionStream extends PipeableTransformStream<
|
|
|
57
57
|
AssistantMetaStreamChunk,
|
|
58
58
|
AssistantStreamChunk
|
|
59
59
|
>({
|
|
60
|
-
transform(chunk, controller) {
|
|
60
|
+
async transform(chunk, controller) {
|
|
61
61
|
// forward everything
|
|
62
62
|
if (chunk.type !== "part-finish" || chunk.meta.type !== "tool-call") {
|
|
63
63
|
controller.enqueue(chunk);
|
|
@@ -88,7 +88,9 @@ export class ToolExecutionStream extends PipeableTransformStream<
|
|
|
88
88
|
const controller = toolCallControllers.get(toolCallId);
|
|
89
89
|
if (!controller)
|
|
90
90
|
throw new Error("No controller found for tool call");
|
|
91
|
-
|
|
91
|
+
// Awaited so the writer lock is released (and argsText updated)
|
|
92
|
+
// before the next chunk acquires the writer.
|
|
93
|
+
await controller.appendArgsTextDelta(chunk.textDelta);
|
|
92
94
|
}
|
|
93
95
|
break;
|
|
94
96
|
}
|
|
@@ -117,6 +119,10 @@ export class ToolExecutionStream extends PipeableTransformStream<
|
|
|
117
119
|
if (!streamController)
|
|
118
120
|
throw new Error("No controller found for tool call");
|
|
119
121
|
|
|
122
|
+
// Args fully streamed: close the reader so awaited absent fields
|
|
123
|
+
// resolve. Awaited so the close settles before the writer is reused.
|
|
124
|
+
await streamController.finishArgsText();
|
|
125
|
+
|
|
120
126
|
let isExecuting = false;
|
|
121
127
|
const promise = withPromiseOrValue(
|
|
122
128
|
() => {
|
|
@@ -286,6 +286,19 @@ describe("toToolsJSONSchema", () => {
|
|
|
286
286
|
});
|
|
287
287
|
});
|
|
288
288
|
|
|
289
|
+
it("excludes frontend tools without execute by default", () => {
|
|
290
|
+
const tools: Record<string, Tool> = {
|
|
291
|
+
stubbedTool: {
|
|
292
|
+
type: "frontend",
|
|
293
|
+
description: "A frontend tool supplied by local overrides",
|
|
294
|
+
parameters: { type: "object", properties: {} },
|
|
295
|
+
},
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
const result = toToolsJSONSchema(tools);
|
|
299
|
+
expect(result).not.toHaveProperty("stubbedTool");
|
|
300
|
+
});
|
|
301
|
+
|
|
289
302
|
it("includes human tools", () => {
|
|
290
303
|
const tools: Record<string, Tool> = {
|
|
291
304
|
humanTool: {
|
|
@@ -299,6 +312,35 @@ describe("toToolsJSONSchema", () => {
|
|
|
299
312
|
expect(result).toHaveProperty("humanTool");
|
|
300
313
|
});
|
|
301
314
|
|
|
315
|
+
it("omits schemas for tools with backend parameter defaults", () => {
|
|
316
|
+
const tools: Record<string, Tool> = {
|
|
317
|
+
generatedFrontendTool: {
|
|
318
|
+
type: "frontend",
|
|
319
|
+
description: "A generated frontend tool",
|
|
320
|
+
parameters: { type: "object", properties: {} },
|
|
321
|
+
execute: async () => {},
|
|
322
|
+
unstable_backendDefault: { parameters: true },
|
|
323
|
+
},
|
|
324
|
+
generatedHumanTool: {
|
|
325
|
+
type: "human",
|
|
326
|
+
description: "A generated human tool",
|
|
327
|
+
parameters: { type: "object", properties: {} },
|
|
328
|
+
unstable_backendDefault: { parameters: true },
|
|
329
|
+
},
|
|
330
|
+
olderFrontendTool: {
|
|
331
|
+
type: "frontend",
|
|
332
|
+
description: "An older frontend tool",
|
|
333
|
+
parameters: { type: "object", properties: {} },
|
|
334
|
+
execute: async () => {},
|
|
335
|
+
},
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
const result = toToolsJSONSchema(tools);
|
|
339
|
+
expect(result).not.toHaveProperty("generatedFrontendTool");
|
|
340
|
+
expect(result).not.toHaveProperty("generatedHumanTool");
|
|
341
|
+
expect(result).toHaveProperty("olderFrontendTool");
|
|
342
|
+
});
|
|
343
|
+
|
|
302
344
|
it("excludes tools without parameters", () => {
|
|
303
345
|
const tools: Record<string, Tool> = {
|
|
304
346
|
withParams: {
|
|
@@ -340,6 +382,27 @@ describe("toToolsJSONSchema", () => {
|
|
|
340
382
|
expect(result).toHaveProperty("tool_c");
|
|
341
383
|
});
|
|
342
384
|
|
|
385
|
+
it("always excludes backend-default parameters with a custom filter", () => {
|
|
386
|
+
const tools: Record<string, Tool> = {
|
|
387
|
+
backendDefaultTool: {
|
|
388
|
+
type: "frontend",
|
|
389
|
+
parameters: { type: "object", properties: {} },
|
|
390
|
+
execute: async () => {},
|
|
391
|
+
unstable_backendDefault: { parameters: true },
|
|
392
|
+
},
|
|
393
|
+
normalTool: {
|
|
394
|
+
parameters: { type: "object", properties: {} },
|
|
395
|
+
},
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
const result = toToolsJSONSchema(tools, {
|
|
399
|
+
filter: () => true,
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
expect(result).not.toHaveProperty("backendDefaultTool");
|
|
403
|
+
expect(result).toHaveProperty("normalTool");
|
|
404
|
+
});
|
|
405
|
+
|
|
343
406
|
it("custom filter receives name and tool", () => {
|
|
344
407
|
const tools: Record<string, Tool> = {
|
|
345
408
|
prefixed_tool: {
|
|
@@ -15,6 +15,8 @@ export type ToToolsJSONSchemaOptions = {
|
|
|
15
15
|
/**
|
|
16
16
|
* Filter to determine which tools to include.
|
|
17
17
|
* Defaults to excluding disabled tools and backend tools.
|
|
18
|
+
*
|
|
19
|
+
* Tools with backend-default parameters are always excluded.
|
|
18
20
|
*/
|
|
19
21
|
filter?: (name: string, tool: Tool) => boolean;
|
|
20
22
|
};
|
|
@@ -130,7 +132,19 @@ export function toPartialJSONSchema(schema: JSONSchema7): JSONSchema7 {
|
|
|
130
132
|
}
|
|
131
133
|
|
|
132
134
|
function defaultToolFilter(_name: string, tool: Tool): boolean {
|
|
133
|
-
return
|
|
135
|
+
return (
|
|
136
|
+
!tool.disabled &&
|
|
137
|
+
tool.type !== "backend" &&
|
|
138
|
+
(tool.type !== "frontend" || tool.execute !== undefined)
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function toolHasUploadableParameters(
|
|
143
|
+
tool: Tool,
|
|
144
|
+
): tool is Tool & { parameters: NonNullable<Tool["parameters"]> } {
|
|
145
|
+
return (
|
|
146
|
+
tool.parameters !== undefined && !tool.unstable_backendDefault?.parameters
|
|
147
|
+
);
|
|
134
148
|
}
|
|
135
149
|
|
|
136
150
|
/**
|
|
@@ -152,13 +166,21 @@ export function toToolsJSONSchema(
|
|
|
152
166
|
|
|
153
167
|
return Object.fromEntries(
|
|
154
168
|
Object.entries(tools)
|
|
155
|
-
.filter(([name, tool]) => filter(name, tool)
|
|
169
|
+
.filter(([name, tool]) => filter(name, tool))
|
|
170
|
+
.filter(
|
|
171
|
+
(
|
|
172
|
+
entry,
|
|
173
|
+
): entry is [
|
|
174
|
+
string,
|
|
175
|
+
Tool & { parameters: NonNullable<Tool["parameters"]> },
|
|
176
|
+
] => toolHasUploadableParameters(entry[1]),
|
|
177
|
+
)
|
|
156
178
|
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
|
|
157
179
|
.map(([name, tool]) => [
|
|
158
180
|
name,
|
|
159
181
|
{
|
|
160
182
|
...(tool.description && { description: tool.description }),
|
|
161
|
-
parameters: toJSONSchema(tool.parameters
|
|
183
|
+
parameters: toJSONSchema(tool.parameters),
|
|
162
184
|
...(tool.providerOptions && {
|
|
163
185
|
providerOptions: tool.providerOptions,
|
|
164
186
|
}),
|
|
@@ -216,6 +216,16 @@ type ToolBase<
|
|
|
216
216
|
* @see ToolDisplay
|
|
217
217
|
*/
|
|
218
218
|
display?: ToolDisplay;
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* @internal Defaults already known by the backend for this tool. Client
|
|
222
|
+
* transports omit matching fields and only upload overrides.
|
|
223
|
+
*
|
|
224
|
+
* This is only meaningful for frontend and human tools.
|
|
225
|
+
*/
|
|
226
|
+
unstable_backendDefault?: {
|
|
227
|
+
parameters?: boolean;
|
|
228
|
+
};
|
|
219
229
|
};
|
|
220
230
|
|
|
221
231
|
type BackendTool<
|
|
@@ -276,7 +286,7 @@ type FrontendTool<
|
|
|
276
286
|
/** Prevents the tool from being exposed to the model while true. */
|
|
277
287
|
disabled?: boolean;
|
|
278
288
|
/** Executes the tool after the model provides valid arguments. */
|
|
279
|
-
execute
|
|
289
|
+
execute?: ToolExecuteFunction<TArgs, TResult>;
|
|
280
290
|
/** Converts the execution result into model-visible output. */
|
|
281
291
|
toModelOutput?: ToolModelOutputFunction<TArgs, TResult>;
|
|
282
292
|
/** Handles invalid tool arguments when schema validation fails. */
|
|
@@ -305,6 +315,63 @@ type HumanTool<
|
|
|
305
315
|
providerOptions?: ProviderOptions;
|
|
306
316
|
};
|
|
307
317
|
|
|
318
|
+
type ProviderTool<
|
|
319
|
+
TArgs extends Record<string, unknown> = Record<string, unknown>,
|
|
320
|
+
TResult = unknown,
|
|
321
|
+
> = ToolBase<TArgs, TResult> & {
|
|
322
|
+
/** Tool executed by the LLM provider rather than assistant-ui. */
|
|
323
|
+
type: "provider";
|
|
324
|
+
|
|
325
|
+
/** Provider-defined tool identifier, e.g. `openai.web_search_preview`. */
|
|
326
|
+
providerId: `${string}.${string}`;
|
|
327
|
+
/** Schema used by adapters for validation and type plumbing. */
|
|
328
|
+
parameters?: StandardSchemaV1<TArgs> | JSONSchema7 | undefined;
|
|
329
|
+
/** Provider-specific configuration for this tool. */
|
|
330
|
+
args: Record<string, unknown>;
|
|
331
|
+
/**
|
|
332
|
+
* Whether provider results may arrive after the originating response turn.
|
|
333
|
+
*/
|
|
334
|
+
supportsDeferredResults?: boolean;
|
|
335
|
+
|
|
336
|
+
description?: undefined;
|
|
337
|
+
disabled?: boolean;
|
|
338
|
+
execute?: undefined;
|
|
339
|
+
toModelOutput?: undefined;
|
|
340
|
+
experimental_onSchemaValidationError?: undefined;
|
|
341
|
+
providerOptions?: ProviderOptions;
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
export type McpServerConfig =
|
|
345
|
+
| {
|
|
346
|
+
/** Connect to an MCP server over Streamable HTTP or server-sent events. */
|
|
347
|
+
type: "http" | "sse";
|
|
348
|
+
url: string;
|
|
349
|
+
headers?: Record<string, string>;
|
|
350
|
+
redirect?: "follow" | "error";
|
|
351
|
+
}
|
|
352
|
+
| {
|
|
353
|
+
/** Start and connect to a local MCP server over stdio. */
|
|
354
|
+
type: "stdio";
|
|
355
|
+
command: string;
|
|
356
|
+
args?: readonly string[];
|
|
357
|
+
env?: Record<string, string>;
|
|
358
|
+
cwd?: string;
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
type McpTool = ToolBase<Record<string, unknown>, unknown> & {
|
|
362
|
+
/** Tools loaded from an MCP server by a server adapter. */
|
|
363
|
+
type: "mcp";
|
|
364
|
+
server: McpServerConfig;
|
|
365
|
+
|
|
366
|
+
description?: undefined;
|
|
367
|
+
parameters?: undefined;
|
|
368
|
+
disabled?: boolean;
|
|
369
|
+
execute?: undefined;
|
|
370
|
+
toModelOutput?: undefined;
|
|
371
|
+
experimental_onSchemaValidationError?: undefined;
|
|
372
|
+
providerOptions?: undefined;
|
|
373
|
+
};
|
|
374
|
+
|
|
308
375
|
/**
|
|
309
376
|
* Definition for a tool that can be exposed to the assistant model.
|
|
310
377
|
*
|
|
@@ -353,6 +420,8 @@ export type Tool<
|
|
|
353
420
|
| FrontendTool<TArgs, TResult>
|
|
354
421
|
| BackendTool<TArgs, TResult>
|
|
355
422
|
| HumanTool<TArgs, TResult>
|
|
423
|
+
| ProviderTool<TArgs, TResult>
|
|
424
|
+
| McpTool
|
|
356
425
|
| ToolWithoutType<TArgs, TResult>;
|
|
357
426
|
|
|
358
427
|
/**
|
|
@@ -368,6 +437,8 @@ export type ToolDeclaration<
|
|
|
368
437
|
| FrontendTool<TArgs, TResult>
|
|
369
438
|
| BackendToolDeclaration<TArgs, TResult>
|
|
370
439
|
| HumanTool<TArgs, TResult>
|
|
440
|
+
| ProviderTool<TArgs, TResult>
|
|
441
|
+
| McpTool
|
|
371
442
|
| ToolWithoutType<TArgs, TResult>;
|
|
372
443
|
|
|
373
444
|
/**
|
|
@@ -380,4 +451,5 @@ export type ToolWithoutType<
|
|
|
380
451
|
| Omit<FrontendTool<TArgs, TResult>, "type">
|
|
381
452
|
| Omit<BackendTool<TArgs, TResult>, "type">
|
|
382
453
|
| Omit<HumanTool<TArgs, TResult>, "type">
|
|
454
|
+
| Omit<ProviderTool<TArgs, TResult>, "type">
|
|
383
455
|
) & { type?: undefined };
|