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.
Files changed (38) hide show
  1. package/dist/core/accumulators/AssistantMessageStream.d.ts.map +1 -1
  2. package/dist/core/accumulators/TimingTracker.d.ts.map +1 -1
  3. package/dist/core/modules/assistant-stream.d.ts.map +1 -1
  4. package/dist/core/modules/assistant-stream.js +6 -4
  5. package/dist/core/modules/assistant-stream.js.map +1 -1
  6. package/dist/core/object/ObjectStreamAccumulator.d.ts.map +1 -1
  7. package/dist/core/object/ObjectStreamResponse.js.map +1 -1
  8. package/dist/core/serialization/data-stream/DataStream.js.map +1 -1
  9. package/dist/core/serialization/ui-message-stream/UIMessageStream.js.map +1 -1
  10. package/dist/core/tool/ToolCallReader.d.ts +1 -1
  11. package/dist/core/tool/ToolCallReader.d.ts.map +1 -1
  12. package/dist/core/tool/ToolCallReader.js.map +1 -1
  13. package/dist/core/tool/ToolResponse.d.ts.map +1 -1
  14. package/dist/core/tool/ToolResponse.js.map +1 -1
  15. package/dist/core/tool/schema-utils.d.ts +7 -1
  16. package/dist/core/tool/schema-utils.d.ts.map +1 -1
  17. package/dist/core/tool/schema-utils.js +8 -2
  18. package/dist/core/tool/schema-utils.js.map +1 -1
  19. package/dist/core/tool/tool-types.d.ts +68 -3
  20. package/dist/core/tool/tool-types.d.ts.map +1 -1
  21. package/dist/core/utils/Counter.d.ts.map +1 -1
  22. package/dist/core/utils/withPromiseOrValue.d.ts.map +1 -1
  23. package/dist/index.d.ts +2 -2
  24. package/dist/node_modules/.pnpm/@types_json-schema@7.0.15/node_modules/@types/json-schema/index.d.ts.map +1 -1
  25. package/dist/utils/AsyncIterableStream.d.ts.map +1 -1
  26. package/package.json +4 -4
  27. package/src/core/modules/assistant-stream.test.ts +104 -0
  28. package/src/core/modules/assistant-stream.ts +13 -4
  29. package/src/core/object/ObjectStreamResponse.ts +4 -4
  30. package/src/core/serialization/data-stream/DataStream.ts +0 -1
  31. package/src/core/serialization/ui-message-stream/UIMessageStream.ts +0 -1
  32. package/src/core/tool/ToolCallReader.ts +8 -9
  33. package/src/core/tool/ToolResponse.ts +1 -0
  34. package/src/core/tool/schema-utils.test.ts +56 -0
  35. package/src/core/tool/schema-utils.ts +11 -1
  36. package/src/core/tool/tool-types.ts +89 -1
  37. package/src/index.ts +2 -1
  38. 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":"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"}
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,CAAA;AAAA"}
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,GAAA,CACd,QAAA,QAAgB,CAAA,GAAI,WAAA,CAAY,CAAA,GAChC,WAAA,GAAc,KAAA,EAAO,CAAA,KAAM,WAAA,eAC3B,YAAA,GAAe,KAAA,cAAmB,WAAA,gBACjC,WAAA"}
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":["JSONSchema4TypeName","JSONSchema4Type","JSONSchema4Object","JSONSchema4Array","key","Array","JSONSchema4Version","JSONSchema4","id","$ref","$schema","title","description","default","multipleOf","maximum","exclusiveMaximum","minimum","exclusiveMinimum","maxLength","minLength","pattern","additionalItems","items","maxItems","minItems","uniqueItems","maxProperties","minProperties","required","additionalProperties","definitions","k","properties","patternProperties","dependencies","enum","type","allOf","anyOf","oneOf","not","extends","format","JSONSchema6TypeName","JSONSchema6Type","JSONSchema6Object","JSONSchema6Array","JSONSchema6Version","JSONSchema6Definition","JSONSchema6","$id","contains","propertyNames","const","examples","JSONSchema7TypeName","JSONSchema7Type","JSONSchema7Object","JSONSchema7Array","JSONSchema7Version","JSONSchema7Definition","JSONSchema7","$comment","$defs","if","then","else","contentMediaType","contentEncoding","readOnly","writeOnly","ValidationResult","ValidationError","valid","errors","property","message","validate","instance","schema","checkPropertyChange","value","mustBeValid","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+iBYwD,mBAAAA;AAAAA;;;;;KAaAC,eAAAA;AAAAA,qBAINC,iBAAAA,GACAC,gBAAgB;AAAA;AAAA,UAILD,iBAAAA;EAAAA,CACZtD,GAAAA,WAAcqD,eAAe;AAAA;AAAA;AAAA;AAAA,UAKjBE,gBAAAA,SAAyBtD,KAAK,CAACoD,eAAAA;;;;;;;;;;;;KAapCG,kBAAAA;;;;;KAMAC,qBAAAA,GAAwBC,WAAW;AAAA,UAC9BA,WAAAA;EACbX,GAAAA;EACA1C,IAAAA;EACAC,OAAAA,GAAUkD,kBAAAA;EACVG,QAAAA;;;;;EAMAC,KAAAA;IAAAA,CACK5D,GAAAA,WAAcyD,qBAAAA;EAAAA;;;;EAMnBxB,IAAAA,GAAOmB,mBAAAA,GAAsBA,mBAAAA;EAC7BpB,IAAAA,GAAOqB,eAAAA;EACPH,KAAAA,GAAQG,eAAAA;;;;EAKR3C,UAAAA;EACAC,OAAAA;EACAC,gBAAAA;EACAC,OAAAA;EACAC,gBAAAA;;;;EAKAC,SAAAA;EACAC,SAAAA;EACAC,OAAAA;;;;EAKAE,KAAAA,GAAQsC,qBAAAA,GAAwBA,qBAAAA;EAChCvC,eAAAA,GAAkBuC,qBAAAA;EAClBrC,QAAAA;EACAC,QAAAA;EACAC,WAAAA;EACA0B,QAAAA,GAAWS,qBAAAA;;;;EAKXlC,aAAAA;EACAC,aAAAA;EACAC,QAAAA;EACAI,UAAAA;IAAAA,CACK7B,GAAAA,WAAcyD,qBAAAA;EAAAA;EAEnB3B,iBAAAA;IAAAA,CACK9B,GAAAA,WAAcyD,qBAAAA;EAAAA;EAEnB/B,oBAAAA,GAAuB+B,qBAAAA;EACvB1B,YAAAA;IAAAA,CACK/B,GAAAA,WAAcyD,qBAAAA;EAAAA;EAEnBR,aAAAA,GAAgBQ,qBAAAA;;;;EAKhBI,EAAAA,GAAKJ,qBAAAA;EACLK,IAAAA,GAAOL,qBAAAA;EACPM,IAAAA,GAAON,qBAAAA;;;;EAKPvB,KAAAA,GAAQuB,qBAAAA;EACRtB,KAAAA,GAAQsB,qBAAAA;EACRrB,KAAAA,GAAQqB,qBAAAA;EACRpB,GAAAA,GAAMoB,qBAAAA;;;;EAKNlB,MAAAA;;;;EAKAyB,gBAAAA;EACAC,eAAAA;;;;EAKAtC,WAAAA;IAAAA,CACK3B,GAAAA,WAAcyD,qBAAAA;EAAAA;;;;EAMnBlD,KAAAA;EACAC,WAAAA;EACAC,OAAAA,GAAU4C,eAAAA;EACVa,QAAAA;EACAC,SAAAA;EACAhB,QAAAA,GAAWE,eAAAA;AAAAA"}
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,GAAA,CACd,MAAA,EAAQ,cAAA,CAAe,CAAA,IACtB,mBAAA,CAAoB,CAAA"}
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.16",
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.10.1",
68
- "redis": "^5.12.1",
67
+ "ioredis": "^5.11.0",
68
+ "redis": "^6.0.0",
69
69
  "vitest": "^4.1.7",
70
- "@assistant-ui/x-buildutils": "0.0.9"
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 (this._state.append?.kind !== "text") {
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 (this._state.append?.kind !== "reasoning") {
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
- implements
17
- Transformer<ObjectStreamChunk, readonly ObjectStreamOperation[]>
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<T extends ReadonlyJSONObject>
236
- implements ToolCallArgsReader<T>
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<TResult extends ReadonlyJSONValue>
399
- implements ToolCallResponseReader<TResult>
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>;
@@ -1,6 +1,7 @@
1
1
  import type { ReadonlyJSONValue } from "../../utils/json/json-value";
2
2
  import type {
3
3
  ToolModelContentPart,
4
+ // oxlint-disable-next-line no-unused-vars -- referenced from JSDoc {@link} below
4
5
  ToolModelOutputFunction,
5
6
  } from "./tool-types";
6
7
 
@@ -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
  );