assistant-stream 0.3.16 → 0.3.18
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/accumulators/AssistantMessageStream.d.ts.map +1 -1
- package/dist/core/accumulators/TimingTracker.d.ts.map +1 -1
- package/dist/core/modules/assistant-stream.d.ts.map +1 -1
- package/dist/core/modules/assistant-stream.js +6 -4
- package/dist/core/modules/assistant-stream.js.map +1 -1
- package/dist/core/object/ObjectStreamAccumulator.d.ts.map +1 -1
- package/dist/core/object/ObjectStreamResponse.js.map +1 -1
- package/dist/core/serialization/data-stream/DataStream.js.map +1 -1
- package/dist/core/serialization/ui-message-stream/UIMessageStream.js.map +1 -1
- package/dist/core/tool/ToolCallReader.d.ts +1 -1
- package/dist/core/tool/ToolCallReader.d.ts.map +1 -1
- package/dist/core/tool/ToolCallReader.js.map +1 -1
- package/dist/core/tool/ToolResponse.d.ts.map +1 -1
- package/dist/core/tool/ToolResponse.js.map +1 -1
- package/dist/core/tool/schema-utils.d.ts +7 -1
- package/dist/core/tool/schema-utils.d.ts.map +1 -1
- package/dist/core/tool/schema-utils.js +8 -2
- package/dist/core/tool/schema-utils.js.map +1 -1
- package/dist/core/tool/tool-types.d.ts +68 -3
- package/dist/core/tool/tool-types.d.ts.map +1 -1
- package/dist/core/utils/Counter.d.ts.map +1 -1
- package/dist/core/utils/withPromiseOrValue.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/node_modules/.pnpm/@types_json-schema@7.0.15/node_modules/@types/json-schema/index.d.ts.map +1 -1
- package/dist/utils/AsyncIterableStream.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/core/modules/assistant-stream.test.ts +104 -0
- package/src/core/modules/assistant-stream.ts +13 -4
- package/src/core/object/ObjectStreamResponse.ts +4 -4
- package/src/core/serialization/data-stream/DataStream.ts +0 -1
- package/src/core/serialization/ui-message-stream/UIMessageStream.ts +0 -1
- package/src/core/tool/ToolCallReader.ts +8 -9
- package/src/core/tool/ToolResponse.ts +1 -0
- package/src/core/tool/schema-utils.test.ts +56 -0
- package/src/core/tool/schema-utils.ts +11 -1
- package/src/core/tool/tool-types.ts +89 -1
- package/src/index.ts +2 -1
- package/src/resumable/index.ts +1 -4
|
@@ -77,7 +77,7 @@ interface ToolCallArgsReader<TArgs extends Record<string, unknown>> {
|
|
|
77
77
|
*
|
|
78
78
|
* @param fieldPath An array of object keys or array indices.
|
|
79
79
|
*/
|
|
80
|
-
streamText<PathT extends TypePath<TArgs>>(...fieldPath: PathT): TypeAtPath<TArgs, PathT> extends string & infer U ? AsyncIterableStream<U> : never;
|
|
80
|
+
streamText<PathT extends TypePath<TArgs>>(...fieldPath: PathT): TypeAtPath<TArgs, PathT> extends string & (infer U) ? AsyncIterableStream<U> : never;
|
|
81
81
|
/**
|
|
82
82
|
* Returns a stream that will emit complete items in the array
|
|
83
83
|
* at the given path, as they are generated by the LLM.
|
|
@@ -115,11 +115,48 @@ type ToolExecutionContext = {
|
|
|
115
115
|
type ToolExecuteFunction<TArgs, TResult> = (args: TArgs, context: ToolExecutionContext) => TResult | Promise<TResult>;
|
|
116
116
|
type ToolStreamCallFunction<TArgs extends Record<string, unknown> = Record<string, unknown>, TResult = unknown> = (reader: ToolCallReader<TArgs, TResult>, context: ToolExecutionContext) => void;
|
|
117
117
|
type OnSchemaValidationErrorFunction<TResult> = ToolExecuteFunction<unknown, TResult>;
|
|
118
|
+
/**
|
|
119
|
+
* Per-provider metadata forwarded into the wire request body verbatim.
|
|
120
|
+
* assistant-ui does not interpret these values; downstream adapters (AI SDK,
|
|
121
|
+
* custom routes) pass them to the model provider as-is.
|
|
122
|
+
*
|
|
123
|
+
* The outer key is the provider name (`anthropic`, `openai`, ...); the inner
|
|
124
|
+
* object is whatever shape that provider's SDK expects under
|
|
125
|
+
* `tool.providerOptions[providerName]`. Use this to enable provider-specific
|
|
126
|
+
* tool behaviors such as Anthropic's `defer_loading`
|
|
127
|
+
* (`{ anthropic: { deferLoading: true } }`) without adding provider-aware
|
|
128
|
+
* code in assistant-ui.
|
|
129
|
+
*/
|
|
130
|
+
type ProviderOptions = Record<string, Record<string, unknown>>;
|
|
131
|
+
/**
|
|
132
|
+
* Controls how a tool call's UI is presented relative to the assistant's
|
|
133
|
+
* chain-of-thought trace.
|
|
134
|
+
*
|
|
135
|
+
* - `"inline"` — the tool call is part of the reasoning trace and is folded
|
|
136
|
+
* into the chain-of-thought grouping alongside other routine tool calls.
|
|
137
|
+
* - `"standalone"` — the tool call is surfaced on its own, outside the
|
|
138
|
+
* chain-of-thought grouping (e.g. a human-in-the-loop prompt, a generative
|
|
139
|
+
* UI surface, or an important action UI worth showing prominently).
|
|
140
|
+
*
|
|
141
|
+
* This is a client-side presentation hint only; it does not affect how the
|
|
142
|
+
* tool is exposed to or executed by the model.
|
|
143
|
+
*/
|
|
144
|
+
type ToolDisplay = "standalone" | "inline";
|
|
118
145
|
type ToolBase<TArgs extends Record<string, unknown> = Record<string, unknown>, TResult = unknown> = {
|
|
119
146
|
/**
|
|
120
147
|
* @deprecated Experimental, API may change.
|
|
121
148
|
*/
|
|
122
149
|
streamCall?: ToolStreamCallFunction<TArgs, TResult>;
|
|
150
|
+
/**
|
|
151
|
+
* How this tool's UI is presented relative to the chain-of-thought trace.
|
|
152
|
+
*
|
|
153
|
+
* Defaults to `"inline"` (folded into the chain-of-thought grouping).
|
|
154
|
+
* Set `"standalone"` to surface the tool call on its own. `human` tools are
|
|
155
|
+
* always `"standalone"` and cannot opt out.
|
|
156
|
+
*
|
|
157
|
+
* @see ToolDisplay
|
|
158
|
+
*/
|
|
159
|
+
display?: ToolDisplay;
|
|
123
160
|
};
|
|
124
161
|
type BackendTool<TArgs extends Record<string, unknown> = Record<string, unknown>, TResult = unknown> = ToolBase<TArgs, TResult> & {
|
|
125
162
|
/** Tool that is executed by the backend rather than in the browser. */type: "backend";
|
|
@@ -129,6 +166,24 @@ type BackendTool<TArgs extends Record<string, unknown> = Record<string, unknown>
|
|
|
129
166
|
execute?: undefined;
|
|
130
167
|
toModelOutput?: undefined;
|
|
131
168
|
experimental_onSchemaValidationError?: undefined;
|
|
169
|
+
providerOptions?: undefined;
|
|
170
|
+
};
|
|
171
|
+
/**
|
|
172
|
+
* Backend tool as *authored* (a {@link ToolDeclaration}), before the build
|
|
173
|
+
* splits it: it may declare a `description`, `parameters`, and a server-side
|
|
174
|
+
* `execute`. The canonical {@link BackendTool} keeps these `undefined` because,
|
|
175
|
+
* once split, the client never sees them and the server consumes them through a
|
|
176
|
+
* server adapter rather than the shared {@link Tool} shape.
|
|
177
|
+
*/
|
|
178
|
+
type BackendToolDeclaration<TArgs extends Record<string, unknown> = Record<string, unknown>, TResult = unknown> = ToolBase<TArgs, TResult> & {
|
|
179
|
+
type: "backend"; /** Natural-language description shown to the model when selecting tools. */
|
|
180
|
+
description?: string | undefined; /** Schema for the arguments the model must provide when calling the tool. */
|
|
181
|
+
parameters?: StandardSchemaV1<TArgs> | JSONSchema7 | undefined; /** Prevents the tool from being exposed to the model while true. */
|
|
182
|
+
disabled?: boolean; /** Executes the tool on the server after the model provides valid arguments. */
|
|
183
|
+
execute?: ToolExecuteFunction<TArgs, TResult>; /** Converts the execution result into model-visible output. */
|
|
184
|
+
toModelOutput?: ToolModelOutputFunction<TArgs, TResult>; /** Handles invalid tool arguments when schema validation fails. */
|
|
185
|
+
experimental_onSchemaValidationError?: OnSchemaValidationErrorFunction<TResult>;
|
|
186
|
+
providerOptions?: ProviderOptions;
|
|
132
187
|
};
|
|
133
188
|
type FrontendTool<TArgs extends Record<string, unknown> = Record<string, unknown>, TResult = unknown> = ToolBase<TArgs, TResult> & {
|
|
134
189
|
/** Tool that is executed in the frontend runtime. */type: "frontend"; /** Natural-language description shown to the model when selecting tools. */
|
|
@@ -138,15 +193,18 @@ type FrontendTool<TArgs extends Record<string, unknown> = Record<string, unknown
|
|
|
138
193
|
execute: ToolExecuteFunction<TArgs, TResult>; /** Converts the execution result into model-visible output. */
|
|
139
194
|
toModelOutput?: ToolModelOutputFunction<TArgs, TResult>; /** Handles invalid tool arguments when schema validation fails. */
|
|
140
195
|
experimental_onSchemaValidationError?: OnSchemaValidationErrorFunction<TResult>;
|
|
196
|
+
providerOptions?: ProviderOptions;
|
|
141
197
|
};
|
|
142
198
|
type HumanTool<TArgs extends Record<string, unknown> = Record<string, unknown>, TResult = unknown> = ToolBase<TArgs, TResult> & {
|
|
143
199
|
/** Tool that pauses the run until a user or UI supplies a result. */type: "human"; /** Natural-language description shown to the model when selecting tools. */
|
|
144
200
|
description?: string | undefined; /** Schema for the arguments the model must provide when requesting input. */
|
|
145
201
|
parameters: StandardSchemaV1<TArgs> | JSONSchema7; /** Prevents the tool from being exposed to the model while true. */
|
|
146
|
-
disabled?: boolean;
|
|
202
|
+
disabled?: boolean; /** Human tools are always surfaced standalone and cannot opt out. */
|
|
203
|
+
display?: "standalone";
|
|
147
204
|
execute?: undefined;
|
|
148
205
|
toModelOutput?: undefined;
|
|
149
206
|
experimental_onSchemaValidationError?: undefined;
|
|
207
|
+
providerOptions?: ProviderOptions;
|
|
150
208
|
};
|
|
151
209
|
/**
|
|
152
210
|
* Definition for a tool that can be exposed to the assistant model.
|
|
@@ -190,6 +248,13 @@ type HumanTool<TArgs extends Record<string, unknown> = Record<string, unknown>,
|
|
|
190
248
|
* ```
|
|
191
249
|
*/
|
|
192
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>;
|
|
251
|
+
/**
|
|
252
|
+
* A tool as *authored* — the permissive counterpart to {@link Tool}. Unlike
|
|
253
|
+
* {@link Tool}, a `backend` entry may declare `description`, `parameters`, and a
|
|
254
|
+
* server-side `execute`. Use this for the input of authoring helpers (e.g.
|
|
255
|
+
* `defineToolkit`); the canonical {@link Tool} is the output.
|
|
256
|
+
*/
|
|
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>;
|
|
193
258
|
/**
|
|
194
259
|
* @deprecated Use {@link Tool} with an explicit `type` field instead.
|
|
195
260
|
*/
|
|
@@ -197,5 +262,5 @@ type ToolWithoutType<TArgs extends Record<string, unknown> = Record<string, unkn
|
|
|
197
262
|
type?: undefined;
|
|
198
263
|
};
|
|
199
264
|
//#endregion
|
|
200
|
-
export { Tool, ToolCallArgsReader, ToolCallReader, ToolCallResponseReader, ToolExecuteFunction, ToolExecutionContext, ToolModelContentPart, ToolModelOutputFunction, ToolStreamCallFunction, ToolWithoutType };
|
|
265
|
+
export { ProviderOptions, Tool, ToolCallArgsReader, ToolCallReader, ToolCallResponseReader, ToolDeclaration, ToolExecuteFunction, ToolExecutionContext, ToolModelContentPart, ToolModelOutputFunction, ToolStreamCallFunction, ToolWithoutType };
|
|
201
266
|
//# 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,
|
|
1
|
+
{"version":3,"file":"tool-types.d.ts","names":[],"sources":["../../../src/core/tool/tool-types.ts"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Counter.d.ts","names":[],"sources":["../../../src/core/utils/Counter.ts"],"mappings":";cAAa,OAAA;EACJ,KAAA;EAEP,EAAE
|
|
1
|
+
{"version":3,"file":"Counter.d.ts","names":[],"sources":["../../../src/core/utils/Counter.ts"],"mappings":";cAAa,OAAA;EACJ,KAAA;EAEP,EAAE;AAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"withPromiseOrValue.d.ts","names":[],"sources":["../../../src/core/utils/withPromiseOrValue.ts"],"mappings":";iBAAgB,kBAAA,
|
|
1
|
+
{"version":3,"file":"withPromiseOrValue.d.ts","names":[],"sources":["../../../src/core/utils/withPromiseOrValue.ts"],"mappings":";iBAAgB,kBAAA,IACd,QAAA,QAAgB,CAAA,GAAI,WAAA,CAAY,CAAA,GAChC,WAAA,GAAc,KAAA,EAAO,CAAA,KAAM,WAAA,eAC3B,YAAA,GAAe,KAAA,cAAmB,WAAA,gBACjC,WAAA"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { TextStreamController } from "./core/modules/text.js";
|
|
2
|
-
import { Tool, ToolCallReader, ToolModelContentPart, ToolModelOutputFunction } from "./core/tool/tool-types.js";
|
|
2
|
+
import { 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 TextStreamController, type ToToolsJSONSchemaOptions, type Tool, type ToolCallReader, type ToolCallStreamController, 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 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 };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":["
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":["JSONSchema4Object","JSONSchema4Array","key","JSONSchema4Type","Array","id","$ref","$schema","JSONSchema4Version","title","description","default","multipleOf","maximum","exclusiveMaximum","minimum","exclusiveMinimum","maxLength","minLength","pattern","additionalItems","JSONSchema4","items","maxItems","minItems","uniqueItems","maxProperties","minProperties","required","additionalProperties","definitions","k","properties","patternProperties","dependencies","enum","type","JSONSchema4TypeName","allOf","anyOf","oneOf","not","extends","format","JSONSchema6Object","JSONSchema6Array","JSONSchema6Type","JSONSchema6","$id","JSONSchema6Version","JSONSchema6Definition","contains","propertyNames","const","JSONSchema6TypeName","examples","JSONSchema7Object","JSONSchema7Array","JSONSchema7Type","JSONSchema7","JSONSchema7Version","$comment","$defs","JSONSchema7Definition","JSONSchema7TypeName","if","then","else","contentMediaType","contentEncoding","readOnly","writeOnly","valid","errors","ValidationError","property","message","instance","schema","ValidationResult","value","result"],"sources":["../../../../../../../../../node_modules/.pnpm/@types+json-schema@7.0.15/node_modules/@types/json-schema/index.d.ts"],"x_google_ignoreList":[0],"mappings":";;;;;;;;;;KA+iBY,mBAAA;AAAA;;;;;KAaA,eAAA;AAAA,qBAIN,iBAAA,GACA,gBAAgB;AAAA;AAAA,UAIL,iBAAA;EAAA,CACZE,GAAAA,WAAc,eAAe;AAAA;AAAA;AAAA;AAAA,UAKjB,gBAAA,SAAyB,KAAK,CAAC,eAAA;;;;;;;;;;;;KAapC,kBAAA;;;;;KAMA,qBAAA,GAAwB,WAAW;AAAA,UAC9B,WAAA;EACb8C,GAAAA;EACA1C,IAAAA;EACAC,OAAAA,GAAU,kBAAA;EACVsD,QAAAA;;;;;EAMAC,KAAAA;IAAAA,CACK5D,GAAAA,WAAc,qBAAA;EAAA;;;;EAMnBkC,IAAAA,GAAO,mBAAA,GAAsB,mBAAA;EAC7BD,IAAAA,GAAO,eAAA;EACPkB,KAAAA,GAAQ,eAAA;;;;EAKRzC,UAAAA;EACAC,OAAAA;EACAC,gBAAAA;EACAC,OAAAA;EACAC,gBAAAA;;;;EAKAC,SAAAA;EACAC,SAAAA;EACAC,OAAAA;;;;EAKAG,KAAAA,GAAQ,qBAAA,GAAwB,qBAAA;EAChCF,eAAAA,GAAkB,qBAAA;EAClBG,QAAAA;EACAC,QAAAA;EACAC,WAAAA;EACA0B,QAAAA,GAAW,qBAAA;;;;EAKXzB,aAAAA;EACAC,aAAAA;EACAC,QAAAA;EACAI,UAAAA;IAAAA,CACK9B,GAAAA,WAAc,qBAAA;EAAA;EAEnB+B,iBAAAA;IAAAA,CACK/B,GAAAA,WAAc,qBAAA;EAAA;EAEnB2B,oBAAAA,GAAuB,qBAAA;EACvBK,YAAAA;IAAAA,CACKhC,GAAAA,WAAc,qBAAA;EAAA;EAEnBkD,aAAAA,GAAgB,qBAAA;;;;EAKhBa,EAAAA,GAAK,qBAAA;EACLC,IAAAA,GAAO,qBAAA;EACPC,IAAAA,GAAO,qBAAA;;;;EAKP7B,KAAAA,GAAQ,qBAAA;EACRC,KAAAA,GAAQ,qBAAA;EACRC,KAAAA,GAAQ,qBAAA;EACRC,GAAAA,GAAM,qBAAA;;;;EAKNE,MAAAA;;;;EAKAyB,gBAAAA;EACAC,eAAAA;;;;EAKAvC,WAAAA;IAAAA,CACK5B,GAAAA,WAAc,qBAAA;EAAA;;;;EAMnBO,KAAAA;EACAC,WAAAA;EACAC,OAAAA,GAAU,eAAA;EACV2D,QAAAA;EACAC,SAAAA;EACAhB,QAAAA,GAAW,eAAA;AAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AsyncIterableStream.d.ts","names":[],"sources":["../../src/utils/AsyncIterableStream.ts"],"mappings":";KAAY,mBAAA,MAAyB,aAAA,CAAc,CAAA,IAAK,cAAA,CAAe,CAAA;AAAA,iBAiBvD,qBAAA,
|
|
1
|
+
{"version":3,"file":"AsyncIterableStream.d.ts","names":[],"sources":["../../src/utils/AsyncIterableStream.ts"],"mappings":";KAAY,mBAAA,MAAyB,aAAA,CAAc,CAAA,IAAK,cAAA,CAAe,CAAA;AAAA,iBAiBvD,qBAAA,IACd,MAAA,EAAQ,cAAA,CAAe,CAAA,IACtB,mBAAA,CAAoB,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "assistant-stream",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.18",
|
|
4
4
|
"description": "Streaming utilities for AI assistants",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -64,10 +64,10 @@
|
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
66
|
"@types/json-schema": "^7.0.15",
|
|
67
|
-
"ioredis": "^5.
|
|
68
|
-
"redis": "^
|
|
67
|
+
"ioredis": "^5.11.0",
|
|
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.10"
|
|
71
71
|
},
|
|
72
72
|
"publishConfig": {
|
|
73
73
|
"access": "public",
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { createAssistantStreamResponse } from "./assistant-stream";
|
|
3
|
+
import { AssistantStream } from "../AssistantStream";
|
|
4
|
+
import { DataStreamDecoder } from "../serialization/data-stream/DataStream";
|
|
5
|
+
import { AssistantMessageAccumulator } from "../accumulators/assistant-message-accumulator";
|
|
6
|
+
import type { AssistantMessage } from "../utils/types";
|
|
7
|
+
|
|
8
|
+
const accumulate = async (response: Response): Promise<AssistantMessage> => {
|
|
9
|
+
const stream = AssistantStream.fromResponse(
|
|
10
|
+
response,
|
|
11
|
+
new DataStreamDecoder(),
|
|
12
|
+
);
|
|
13
|
+
let last: AssistantMessage | undefined;
|
|
14
|
+
await stream.pipeThrough(new AssistantMessageAccumulator()).pipeTo(
|
|
15
|
+
new WritableStream({
|
|
16
|
+
write(message) {
|
|
17
|
+
last = message;
|
|
18
|
+
},
|
|
19
|
+
}),
|
|
20
|
+
);
|
|
21
|
+
return last!;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
describe("AssistantStreamController withParentId", () => {
|
|
25
|
+
it("attaches parentId to text parts across a data-stream round trip", async () => {
|
|
26
|
+
const response = createAssistantStreamResponse((controller) => {
|
|
27
|
+
controller.appendText("intro");
|
|
28
|
+
const group = controller.withParentId("group-1");
|
|
29
|
+
group.appendSource({
|
|
30
|
+
type: "source",
|
|
31
|
+
sourceType: "url",
|
|
32
|
+
id: "s1",
|
|
33
|
+
url: "https://example.com",
|
|
34
|
+
title: "Example",
|
|
35
|
+
});
|
|
36
|
+
group.appendText("grouped text");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const message = await accumulate(response);
|
|
40
|
+
const intro = message.parts.find(
|
|
41
|
+
(p) => p.type === "text" && p.text === "intro",
|
|
42
|
+
);
|
|
43
|
+
const grouped = message.parts.find(
|
|
44
|
+
(p) => p.type === "text" && p.text === "grouped text",
|
|
45
|
+
);
|
|
46
|
+
const source = message.parts.find((p) => p.type === "source");
|
|
47
|
+
|
|
48
|
+
expect(intro?.parentId).toBeUndefined();
|
|
49
|
+
expect(grouped?.parentId).toBe("group-1");
|
|
50
|
+
expect(source?.parentId).toBe("group-1");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("attaches parentId to reasoning parts across a data-stream round trip", async () => {
|
|
54
|
+
const response = createAssistantStreamResponse((controller) => {
|
|
55
|
+
controller.appendReasoning("thinking out loud");
|
|
56
|
+
const group = controller.withParentId("group-1");
|
|
57
|
+
group.appendReasoning("grouped reasoning");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const message = await accumulate(response);
|
|
61
|
+
const ungrouped = message.parts.find(
|
|
62
|
+
(p) => p.type === "reasoning" && p.text === "thinking out loud",
|
|
63
|
+
);
|
|
64
|
+
const grouped = message.parts.find(
|
|
65
|
+
(p) => p.type === "reasoning" && p.text === "grouped reasoning",
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
expect(ungrouped?.parentId).toBeUndefined();
|
|
69
|
+
expect(grouped?.parentId).toBe("group-1");
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("opens a new text part when withParentId switches between ids", async () => {
|
|
73
|
+
const response = createAssistantStreamResponse((controller) => {
|
|
74
|
+
controller.withParentId("group-1").appendText("first");
|
|
75
|
+
controller.withParentId("group-2").appendText("second");
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const message = await accumulate(response);
|
|
79
|
+
const first = message.parts.find(
|
|
80
|
+
(p) => p.type === "text" && p.text === "first",
|
|
81
|
+
);
|
|
82
|
+
const second = message.parts.find(
|
|
83
|
+
(p) => p.type === "text" && p.text === "second",
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
expect(first?.parentId).toBe("group-1");
|
|
87
|
+
expect(second?.parentId).toBe("group-2");
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("attaches parentId on addTextPart called directly inside a withParentId scope", async () => {
|
|
91
|
+
const response = createAssistantStreamResponse((controller) => {
|
|
92
|
+
const part = controller.withParentId("group-1").addTextPart();
|
|
93
|
+
part.append("explicit");
|
|
94
|
+
part.close();
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const message = await accumulate(response);
|
|
98
|
+
const text = message.parts.find(
|
|
99
|
+
(p) => p.type === "text" && p.text === "explicit",
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
expect(text?.parentId).toBe("group-1");
|
|
103
|
+
});
|
|
104
|
+
});
|
|
@@ -96,6 +96,7 @@ type AssistantStreamControllerState = {
|
|
|
96
96
|
| {
|
|
97
97
|
controller: TextStreamController;
|
|
98
98
|
kind: "text" | "reasoning";
|
|
99
|
+
parentId: string | undefined;
|
|
99
100
|
}
|
|
100
101
|
| undefined;
|
|
101
102
|
contentCounter: Counter;
|
|
@@ -150,9 +151,13 @@ class AssistantStreamControllerImpl implements AssistantStreamController {
|
|
|
150
151
|
}
|
|
151
152
|
|
|
152
153
|
appendText(textDelta: string) {
|
|
153
|
-
if (
|
|
154
|
+
if (
|
|
155
|
+
this._state.append?.kind !== "text" ||
|
|
156
|
+
this._state.append.parentId !== this._parentId
|
|
157
|
+
) {
|
|
154
158
|
this._state.append = {
|
|
155
159
|
kind: "text",
|
|
160
|
+
parentId: this._parentId,
|
|
156
161
|
controller: this.addTextPart(),
|
|
157
162
|
};
|
|
158
163
|
}
|
|
@@ -160,9 +165,13 @@ class AssistantStreamControllerImpl implements AssistantStreamController {
|
|
|
160
165
|
}
|
|
161
166
|
|
|
162
167
|
appendReasoning(textDelta: string) {
|
|
163
|
-
if (
|
|
168
|
+
if (
|
|
169
|
+
this._state.append?.kind !== "reasoning" ||
|
|
170
|
+
this._state.append.parentId !== this._parentId
|
|
171
|
+
) {
|
|
164
172
|
this._state.append = {
|
|
165
173
|
kind: "reasoning",
|
|
174
|
+
parentId: this._parentId,
|
|
166
175
|
controller: this.addReasoningPart(),
|
|
167
176
|
};
|
|
168
177
|
}
|
|
@@ -171,13 +180,13 @@ class AssistantStreamControllerImpl implements AssistantStreamController {
|
|
|
171
180
|
|
|
172
181
|
addTextPart() {
|
|
173
182
|
const [stream, controller] = createTextStreamController();
|
|
174
|
-
this._addPart({ type: "text" }, stream);
|
|
183
|
+
this._addPart(this._withParentIdOption({ type: "text" }), stream);
|
|
175
184
|
return controller;
|
|
176
185
|
}
|
|
177
186
|
|
|
178
187
|
addReasoningPart() {
|
|
179
188
|
const [stream, controller] = createTextStreamController();
|
|
180
|
-
this._addPart({ type: "reasoning" }, stream);
|
|
189
|
+
this._addPart(this._withParentIdOption({ type: "reasoning" }), stream);
|
|
181
190
|
return controller;
|
|
182
191
|
}
|
|
183
192
|
|
|
@@ -12,10 +12,10 @@ export class ObjectStreamEncoder extends PipeableTransformStream<
|
|
|
12
12
|
readable
|
|
13
13
|
.pipeThrough(
|
|
14
14
|
(() => {
|
|
15
|
-
class ObjectStreamTransformer
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
{
|
|
15
|
+
class ObjectStreamTransformer implements Transformer<
|
|
16
|
+
ObjectStreamChunk,
|
|
17
|
+
readonly ObjectStreamOperation[]
|
|
18
|
+
> {
|
|
19
19
|
#isFirstChunk = true;
|
|
20
20
|
|
|
21
21
|
start() {
|
|
@@ -419,7 +419,6 @@ export class DataStreamDecoder extends PipeableTransformStream<
|
|
|
419
419
|
flush() {
|
|
420
420
|
activeToolCallArgsText?.close();
|
|
421
421
|
activeToolCallArgsText = undefined;
|
|
422
|
-
// biome-ignore lint/suspicious/useIterableCallbackReturn: forEach callback intentionally has no return
|
|
423
422
|
toolCallControllers.forEach((controller) => controller.close());
|
|
424
423
|
toolCallControllers.clear();
|
|
425
424
|
},
|
|
@@ -264,7 +264,6 @@ export class UIMessageStreamDecoder extends PipeableTransformStream<
|
|
|
264
264
|
},
|
|
265
265
|
flush() {
|
|
266
266
|
activeToolCallArgsText?.close();
|
|
267
|
-
// biome-ignore lint/suspicious/useIterableCallbackReturn: forEach callback intentionally has no return
|
|
268
267
|
toolCallControllers.forEach((ctrl) => ctrl.close());
|
|
269
268
|
toolCallControllers.clear();
|
|
270
269
|
},
|
|
@@ -232,9 +232,9 @@ class ForEachHandle<T> implements Handle {
|
|
|
232
232
|
}
|
|
233
233
|
|
|
234
234
|
// Implementation of ToolCallReader that uses stream of partial JSON
|
|
235
|
-
export class ToolCallArgsReaderImpl<
|
|
236
|
-
|
|
237
|
-
{
|
|
235
|
+
export class ToolCallArgsReaderImpl<
|
|
236
|
+
T extends ReadonlyJSONObject,
|
|
237
|
+
> implements ToolCallArgsReader<T> {
|
|
238
238
|
private argTextDeltas: ReadableStream<string>;
|
|
239
239
|
private handles: Set<Handle> = new Set();
|
|
240
240
|
private args: unknown = parsePartialJsonObject("");
|
|
@@ -334,7 +334,7 @@ export class ToolCallArgsReaderImpl<T extends ReadonlyJSONObject>
|
|
|
334
334
|
|
|
335
335
|
streamText<PathT extends TypePath<T>>(
|
|
336
336
|
...fieldPath: PathT
|
|
337
|
-
): TypeAtPath<T, PathT> extends string & infer U
|
|
337
|
+
): TypeAtPath<T, PathT> extends string & (infer U)
|
|
338
338
|
? AsyncIterableStream<U>
|
|
339
339
|
: never {
|
|
340
340
|
// Use a type assertion to convert the complex TypePath to a simple array
|
|
@@ -395,9 +395,9 @@ export class ToolCallArgsReaderImpl<T extends ReadonlyJSONObject>
|
|
|
395
395
|
}
|
|
396
396
|
}
|
|
397
397
|
|
|
398
|
-
export class ToolCallResponseReaderImpl<
|
|
399
|
-
|
|
400
|
-
{
|
|
398
|
+
export class ToolCallResponseReaderImpl<
|
|
399
|
+
TResult extends ReadonlyJSONValue,
|
|
400
|
+
> implements ToolCallResponseReader<TResult> {
|
|
401
401
|
constructor(private readonly promise: Promise<ToolResponse<TResult>>) {}
|
|
402
402
|
|
|
403
403
|
public get() {
|
|
@@ -408,8 +408,7 @@ export class ToolCallResponseReaderImpl<TResult extends ReadonlyJSONValue>
|
|
|
408
408
|
export class ToolCallReaderImpl<
|
|
409
409
|
TArgs extends ReadonlyJSONObject,
|
|
410
410
|
TResult extends ReadonlyJSONValue,
|
|
411
|
-
> implements ToolCallReader<TArgs, TResult>
|
|
412
|
-
{
|
|
411
|
+
> implements ToolCallReader<TArgs, TResult> {
|
|
413
412
|
public readonly args: ToolCallArgsReaderImpl<TArgs>;
|
|
414
413
|
public readonly response: ToolCallResponseReaderImpl<TResult>;
|
|
415
414
|
private readonly writable: WritableStream<string>;
|
|
@@ -430,6 +430,62 @@ describe("toToolsJSONSchema", () => {
|
|
|
430
430
|
properties: { converted: { type: "boolean" } },
|
|
431
431
|
});
|
|
432
432
|
});
|
|
433
|
+
|
|
434
|
+
it("forwards providerOptions verbatim when present", () => {
|
|
435
|
+
const tools: Record<string, Tool> = {
|
|
436
|
+
myTool: {
|
|
437
|
+
description: "Test",
|
|
438
|
+
parameters: { type: "object", properties: {} },
|
|
439
|
+
providerOptions: { anthropic: { deferLoading: true } },
|
|
440
|
+
},
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
const result = toToolsJSONSchema(tools);
|
|
444
|
+
expect(result.myTool).toEqual({
|
|
445
|
+
description: "Test",
|
|
446
|
+
parameters: { type: "object", properties: {} },
|
|
447
|
+
providerOptions: { anthropic: { deferLoading: true } },
|
|
448
|
+
});
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
it("omits providerOptions when absent", () => {
|
|
452
|
+
const tools: Record<string, Tool> = {
|
|
453
|
+
myTool: {
|
|
454
|
+
parameters: { type: "object", properties: {} },
|
|
455
|
+
},
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
const result = toToolsJSONSchema(tools);
|
|
459
|
+
expect(result.myTool).not.toHaveProperty("providerOptions");
|
|
460
|
+
});
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
describe("stable ordering", () => {
|
|
464
|
+
it("emits tool names in alphabetical order", () => {
|
|
465
|
+
const tools: Record<string, Tool> = {
|
|
466
|
+
zebra: { parameters: { type: "object", properties: {} } },
|
|
467
|
+
apple: { parameters: { type: "object", properties: {} } },
|
|
468
|
+
mango: { parameters: { type: "object", properties: {} } },
|
|
469
|
+
};
|
|
470
|
+
|
|
471
|
+
const result = toToolsJSONSchema(tools);
|
|
472
|
+
expect(Object.keys(result)).toEqual(["apple", "mango", "zebra"]);
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
it("produces byte-identical output regardless of insertion order", () => {
|
|
476
|
+
const a: Record<string, Tool> = {
|
|
477
|
+
zebra: { parameters: { type: "object", properties: {} } },
|
|
478
|
+
apple: { parameters: { type: "object", properties: {} } },
|
|
479
|
+
};
|
|
480
|
+
const b: Record<string, Tool> = {
|
|
481
|
+
apple: { parameters: { type: "object", properties: {} } },
|
|
482
|
+
zebra: { parameters: { type: "object", properties: {} } },
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
expect(JSON.stringify(toToolsJSONSchema(a))).toBe(
|
|
486
|
+
JSON.stringify(toToolsJSONSchema(b)),
|
|
487
|
+
);
|
|
488
|
+
});
|
|
433
489
|
});
|
|
434
490
|
|
|
435
491
|
describe("edge cases", () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { JSONSchema7 } from "json-schema";
|
|
2
2
|
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
3
|
-
import type { Tool } from "./tool-types";
|
|
3
|
+
import type { ProviderOptions, Tool } from "./tool-types";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Type for a tool definition with JSON Schema parameters.
|
|
@@ -8,6 +8,7 @@ import type { Tool } from "./tool-types";
|
|
|
8
8
|
export type ToolJSONSchema = {
|
|
9
9
|
description?: string;
|
|
10
10
|
parameters: JSONSchema7;
|
|
11
|
+
providerOptions?: ProviderOptions;
|
|
11
12
|
};
|
|
12
13
|
|
|
13
14
|
export type ToToolsJSONSchemaOptions = {
|
|
@@ -135,6 +136,11 @@ function defaultToolFilter(_name: string, tool: Tool): boolean {
|
|
|
135
136
|
/**
|
|
136
137
|
* Converts a record of tools to a record of tool definitions with JSON Schema parameters.
|
|
137
138
|
* By default, filters out disabled tools and backend tools.
|
|
139
|
+
*
|
|
140
|
+
* Entries are emitted in alphabetical order so the resulting request body is
|
|
141
|
+
* byte-identical regardless of the order in which tools were registered. This
|
|
142
|
+
* keeps provider prompt caches stable across renders that mount tools in
|
|
143
|
+
* different orders.
|
|
138
144
|
*/
|
|
139
145
|
export function toToolsJSONSchema(
|
|
140
146
|
tools: Record<string, Tool> | undefined,
|
|
@@ -147,11 +153,15 @@ export function toToolsJSONSchema(
|
|
|
147
153
|
return Object.fromEntries(
|
|
148
154
|
Object.entries(tools)
|
|
149
155
|
.filter(([name, tool]) => filter(name, tool) && tool.parameters)
|
|
156
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
|
|
150
157
|
.map(([name, tool]) => [
|
|
151
158
|
name,
|
|
152
159
|
{
|
|
153
160
|
...(tool.description && { description: tool.description }),
|
|
154
161
|
parameters: toJSONSchema(tool.parameters!),
|
|
162
|
+
...(tool.providerOptions && {
|
|
163
|
+
providerOptions: tool.providerOptions,
|
|
164
|
+
}),
|
|
155
165
|
},
|
|
156
166
|
]),
|
|
157
167
|
);
|