assistant-stream 0.2.6 → 0.2.7

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 (83) hide show
  1. package/dist/core/AssistantStreamChunk.d.ts +5 -1
  2. package/dist/core/AssistantStreamChunk.d.ts.map +1 -1
  3. package/dist/core/accumulators/AssistantMessageStream.d.ts.map +1 -1
  4. package/dist/core/accumulators/AssistantMessageStream.js +1 -0
  5. package/dist/core/accumulators/AssistantMessageStream.js.map +1 -1
  6. package/dist/core/accumulators/assistant-message-accumulator.d.ts.map +1 -1
  7. package/dist/core/accumulators/assistant-message-accumulator.js +19 -1
  8. package/dist/core/accumulators/assistant-message-accumulator.js.map +1 -1
  9. package/dist/core/index.d.ts +3 -0
  10. package/dist/core/index.d.ts.map +1 -1
  11. package/dist/core/index.js +9 -1
  12. package/dist/core/index.js.map +1 -1
  13. package/dist/core/modules/tool-call.d.ts.map +1 -1
  14. package/dist/core/modules/tool-call.js +1 -1
  15. package/dist/core/modules/tool-call.js.map +1 -1
  16. package/dist/core/object/ObjectStream.test.d.ts +2 -0
  17. package/dist/core/object/ObjectStream.test.d.ts.map +1 -0
  18. package/dist/core/object/ObjectStreamAccumulator.d.ts +11 -0
  19. package/dist/core/object/ObjectStreamAccumulator.d.ts.map +1 -0
  20. package/dist/core/object/ObjectStreamAccumulator.js +64 -0
  21. package/dist/core/object/ObjectStreamAccumulator.js.map +1 -0
  22. package/dist/core/object/ObjectStreamResponse.d.ts +13 -0
  23. package/dist/core/object/ObjectStreamResponse.d.ts.map +1 -0
  24. package/dist/core/object/ObjectStreamResponse.js +66 -0
  25. package/dist/core/object/ObjectStreamResponse.js.map +1 -0
  26. package/dist/core/object/createObjectStream.d.ts +13 -0
  27. package/dist/core/object/createObjectStream.d.ts.map +1 -0
  28. package/dist/core/object/createObjectStream.js +63 -0
  29. package/dist/core/object/createObjectStream.js.map +1 -0
  30. package/dist/core/object/types.d.ts +15 -0
  31. package/dist/core/object/types.d.ts.map +1 -0
  32. package/dist/core/object/types.js +1 -0
  33. package/dist/core/object/types.js.map +1 -0
  34. package/dist/core/serialization/PlainText.d.ts.map +1 -1
  35. package/dist/core/serialization/PlainText.js.map +1 -1
  36. package/dist/core/serialization/data-stream/DataStream.d.ts.map +1 -1
  37. package/dist/core/serialization/data-stream/DataStream.js +14 -0
  38. package/dist/core/serialization/data-stream/DataStream.js.map +1 -1
  39. package/dist/core/serialization/data-stream/chunk-types.d.ts +4 -1
  40. package/dist/core/serialization/data-stream/chunk-types.d.ts.map +1 -1
  41. package/dist/core/serialization/data-stream/chunk-types.js +1 -0
  42. package/dist/core/serialization/data-stream/chunk-types.js.map +1 -1
  43. package/dist/core/tool/ToolCallReader.d.ts.map +1 -1
  44. package/dist/core/tool/ToolCallReader.js.map +1 -1
  45. package/dist/core/tool/ToolResponse.d.ts +1 -1
  46. package/dist/core/tool/ToolResponse.d.ts.map +1 -1
  47. package/dist/core/tool/ToolResponse.js +3 -1
  48. package/dist/core/tool/ToolResponse.js.map +1 -1
  49. package/dist/core/tool/toolResultStream.d.ts.map +1 -1
  50. package/dist/core/tool/toolResultStream.js +1 -1
  51. package/dist/core/tool/toolResultStream.js.map +1 -1
  52. package/dist/core/tool/type-path-utils.d.ts.map +1 -1
  53. package/dist/core/utils/stream/SSE.d.ts +10 -0
  54. package/dist/core/utils/stream/SSE.d.ts.map +1 -0
  55. package/dist/core/utils/stream/SSE.js +102 -0
  56. package/dist/core/utils/stream/SSE.js.map +1 -0
  57. package/dist/core/utils/types.d.ts +14 -3
  58. package/dist/core/utils/types.d.ts.map +1 -1
  59. package/dist/utils/index.d.ts +1 -1
  60. package/dist/utils/index.d.ts.map +1 -1
  61. package/dist/utils/index.js +3 -1
  62. package/dist/utils/index.js.map +1 -1
  63. package/package.json +1 -1
  64. package/src/core/AssistantStreamChunk.ts +6 -1
  65. package/src/core/accumulators/AssistantMessageStream.ts +1 -0
  66. package/src/core/accumulators/assistant-message-accumulator.ts +25 -1
  67. package/src/core/index.ts +7 -0
  68. package/src/core/modules/tool-call.ts +3 -1
  69. package/src/core/object/ObjectStream.test.ts +376 -0
  70. package/src/core/object/ObjectStreamAccumulator.ts +80 -0
  71. package/src/core/object/ObjectStreamResponse.ts +81 -0
  72. package/src/core/object/createObjectStream.ts +87 -0
  73. package/src/core/object/types.ts +18 -0
  74. package/src/core/serialization/PlainText.ts +2 -1
  75. package/src/core/serialization/data-stream/DataStream.ts +16 -0
  76. package/src/core/serialization/data-stream/chunk-types.ts +6 -0
  77. package/src/core/tool/ToolCallReader.ts +0 -3
  78. package/src/core/tool/ToolResponse.ts +4 -2
  79. package/src/core/tool/toolResultStream.ts +3 -2
  80. package/src/core/tool/type-path-utils.ts +0 -2
  81. package/src/core/utils/stream/SSE.ts +116 -0
  82. package/src/core/utils/types.ts +18 -3
  83. package/src/utils/index.ts +4 -1
@@ -0,0 +1,81 @@
1
+ import { PipeableTransformStream } from "../utils/stream/PipeableTransformStream";
2
+ import { ObjectStreamAccumulator } from "./ObjectStreamAccumulator";
3
+ import { SSEDecoder, SSEEncoder } from "../utils/stream/SSE";
4
+ import { ObjectStreamChunk, ObjectStreamOperation } from "./types";
5
+
6
+ export class ObjectStreamEncoder extends PipeableTransformStream<
7
+ ObjectStreamChunk,
8
+ Uint8Array
9
+ > {
10
+ constructor() {
11
+ super((readable) =>
12
+ readable
13
+ .pipeThrough(
14
+ new TransformStream<
15
+ ObjectStreamChunk,
16
+ readonly ObjectStreamOperation[]
17
+ >({
18
+ transform(chunk, controller) {
19
+ controller.enqueue(chunk.operations);
20
+ },
21
+ }),
22
+ )
23
+ .pipeThrough(new SSEEncoder()),
24
+ );
25
+ }
26
+ }
27
+
28
+ export class ObjectStreamDecoder extends PipeableTransformStream<
29
+ Uint8Array,
30
+ ObjectStreamChunk
31
+ > {
32
+ constructor() {
33
+ const accumulator = new ObjectStreamAccumulator();
34
+ super((readable) =>
35
+ readable
36
+ .pipeThrough(new SSEDecoder<readonly ObjectStreamOperation[]>())
37
+ .pipeThrough(
38
+ new TransformStream<
39
+ readonly ObjectStreamOperation[],
40
+ ObjectStreamChunk
41
+ >({
42
+ transform(operations, controller) {
43
+ accumulator.append(operations);
44
+ controller.enqueue({
45
+ snapshot: accumulator.state,
46
+ operations,
47
+ });
48
+ },
49
+ }),
50
+ ),
51
+ );
52
+ }
53
+ }
54
+
55
+ export class ObjectStreamResponse extends Response {
56
+ constructor(body: ReadableStream<ObjectStreamChunk>) {
57
+ super(body.pipeThrough(new ObjectStreamEncoder()), {
58
+ headers: new Headers({
59
+ "Content-Type": "text/event-stream",
60
+ "Cache-Control": "no-cache",
61
+ Connection: "keep-alive",
62
+ "Assistant-Stream-Format": "object-stream/v0",
63
+ }),
64
+ });
65
+ }
66
+ }
67
+
68
+ export const fromObjectStreamResponse = (
69
+ response: Response,
70
+ ): ReadableStream<ObjectStreamChunk> => {
71
+ if (!response.ok)
72
+ throw new Error(`Response failed, status ${response.status}`);
73
+ if (!response.body) throw new Error("Response body is null");
74
+ if (response.headers.get("Content-Type") !== "text/event-stream") {
75
+ throw new Error("Response is not an event stream");
76
+ }
77
+ if (response.headers.get("Assistant-Stream-Format") !== "object-stream/v0") {
78
+ throw new Error("Unsupported Assistant-Stream-Format header");
79
+ }
80
+ return response.body.pipeThrough(new ObjectStreamDecoder());
81
+ };
@@ -0,0 +1,87 @@
1
+ import { ReadonlyJSONValue } from "../../utils";
2
+ import { withPromiseOrValue } from "../utils/withPromiseOrValue";
3
+ import { ObjectStreamAccumulator } from "./ObjectStreamAccumulator";
4
+ import { ObjectStreamOperation, ObjectStreamChunk } from "./types";
5
+
6
+ type ObjectStreamController = {
7
+ readonly abortSignal: AbortSignal;
8
+
9
+ enqueue(operations: readonly ObjectStreamOperation[]): void;
10
+ };
11
+
12
+ class ObjectStreamControllerImpl implements ObjectStreamController {
13
+ private _controller: ReadableStreamDefaultController<ObjectStreamChunk>;
14
+ private _abortController = new AbortController();
15
+ private _accumulator: ObjectStreamAccumulator;
16
+
17
+ get abortSignal() {
18
+ return this._abortController.signal;
19
+ }
20
+
21
+ constructor(
22
+ controller: ReadableStreamDefaultController<ObjectStreamChunk>,
23
+ defaultValue: ReadonlyJSONValue,
24
+ ) {
25
+ this._controller = controller;
26
+ this._accumulator = new ObjectStreamAccumulator(defaultValue);
27
+ }
28
+
29
+ enqueue(operations: readonly ObjectStreamOperation[]) {
30
+ this._accumulator.append(operations);
31
+
32
+ this._controller.enqueue({
33
+ snapshot: this._accumulator.state,
34
+ operations,
35
+ });
36
+ }
37
+
38
+ __internalError(error: unknown) {
39
+ this._controller.error(error);
40
+ }
41
+
42
+ __internalClose() {
43
+ this._controller.close();
44
+ }
45
+
46
+ __internalCancel(reason?: unknown) {
47
+ this._abortController.abort(reason);
48
+ }
49
+ }
50
+
51
+ const getStreamControllerPair = (defaultValue: ReadonlyJSONValue) => {
52
+ let controller!: ObjectStreamControllerImpl;
53
+ const stream = new ReadableStream<ObjectStreamChunk>({
54
+ start(c) {
55
+ controller = new ObjectStreamControllerImpl(c, defaultValue);
56
+ },
57
+ cancel(reason: unknown) {
58
+ controller.__internalCancel(reason);
59
+ },
60
+ });
61
+
62
+ return [stream, controller] as const;
63
+ };
64
+
65
+ type CreateObjectStreamOptions = {
66
+ execute: (controller: ObjectStreamController) => void | PromiseLike<void>;
67
+ defaultValue?: ReadonlyJSONValue;
68
+ };
69
+
70
+ export const createObjectStream = ({
71
+ execute,
72
+ defaultValue = {},
73
+ }: CreateObjectStreamOptions) => {
74
+ const [stream, controller] = getStreamControllerPair(defaultValue);
75
+
76
+ withPromiseOrValue(
77
+ () => execute(controller),
78
+ () => {
79
+ controller.__internalClose();
80
+ },
81
+ (e: unknown) => {
82
+ controller.__internalError(e);
83
+ },
84
+ );
85
+
86
+ return stream;
87
+ };
@@ -0,0 +1,18 @@
1
+ import { ReadonlyJSONValue } from "../../utils";
2
+
3
+ export type ObjectStreamOperation =
4
+ | {
5
+ readonly type: "set";
6
+ readonly path: readonly string[];
7
+ readonly value: ReadonlyJSONValue;
8
+ }
9
+ | {
10
+ readonly type: "append-text";
11
+ readonly path: readonly string[];
12
+ readonly value: string;
13
+ };
14
+
15
+ export type ObjectStreamChunk = {
16
+ readonly snapshot: ReadonlyJSONValue;
17
+ readonly operations: readonly ObjectStreamOperation[];
18
+ };
@@ -35,7 +35,8 @@ export class PlainTextEncoder
35
35
  | "tool-call-begin"
36
36
  | "tool-call-delta"
37
37
  | "result"
38
- | "error" = type;
38
+ | "error"
39
+ | "update-state" = type;
39
40
  throw new Error(`unsupported chunk type: ${unsupportedType}`);
40
41
  }
41
42
  },
@@ -150,6 +150,14 @@ export class DataStreamEncoder
150
150
  break;
151
151
  }
152
152
 
153
+ case "update-state": {
154
+ controller.enqueue({
155
+ type: DataStreamStreamChunkType.AuiUpdateStateOperations,
156
+ value: chunk.operations,
157
+ });
158
+ break;
159
+ }
160
+
153
161
  // TODO ignore for now
154
162
  // in the future, we should create a handler that waits for text parts to finish before continuing
155
163
  case "tool-call-args-text-finish":
@@ -326,6 +334,14 @@ export class DataStreamDecoder extends PipeableTransformStream<
326
334
  });
327
335
  break;
328
336
 
337
+ case DataStreamStreamChunkType.AuiUpdateStateOperations:
338
+ controller.enqueue({
339
+ type: "update-state",
340
+ path: [],
341
+ operations: value,
342
+ });
343
+ break;
344
+
329
345
  case DataStreamStreamChunkType.ReasoningSignature:
330
346
  case DataStreamStreamChunkType.RedactedReasoning:
331
347
  // ignore these for now
@@ -2,6 +2,7 @@ import {
2
2
  ReadonlyJSONObject,
3
3
  ReadonlyJSONValue,
4
4
  } from "../../../utils/json/json-value";
5
+ import { ObjectStreamOperation } from "../../object/types";
5
6
 
6
7
  export type DataStreamChunk = {
7
8
  [K in DataStreamStreamChunkType]: {
@@ -41,6 +42,8 @@ export enum DataStreamStreamChunkType {
41
42
  RedactedReasoning = "i",
42
43
  ReasoningSignature = "j",
43
44
  File = "k",
45
+
46
+ AuiUpdateStateOperations = "aui-state",
44
47
  }
45
48
  type DataStreamStreamChunkValue = {
46
49
  [DataStreamStreamChunkType.TextDelta]: string;
@@ -90,4 +93,7 @@ type DataStreamStreamChunkValue = {
90
93
  [DataStreamStreamChunkType.RedactedReasoning]: { data: string };
91
94
  [DataStreamStreamChunkType.ReasoningSignature]: { signature: string };
92
95
  [DataStreamStreamChunkType.File]: { data: string; mimeType: string };
96
+
97
+ // aui-extensions
98
+ [DataStreamStreamChunkType.AuiUpdateStateOperations]: ObjectStreamOperation[];
93
99
  };
@@ -329,7 +329,6 @@ export class ToolCallArgsReaderImpl<T extends ReadonlyJSONObject>
329
329
  },
330
330
  });
331
331
 
332
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
333
332
  return asAsyncIterableStream(stream) as any;
334
333
  }
335
334
 
@@ -361,7 +360,6 @@ export class ToolCallArgsReaderImpl<T extends ReadonlyJSONObject>
361
360
  },
362
361
  });
363
362
 
364
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
365
363
  return asAsyncIterableStream(stream) as any;
366
364
  }
367
365
 
@@ -393,7 +391,6 @@ export class ToolCallArgsReaderImpl<T extends ReadonlyJSONObject>
393
391
  },
394
392
  });
395
393
 
396
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
397
394
  return asAsyncIterableStream(stream) as any;
398
395
  }
399
396
  }
@@ -13,12 +13,14 @@ export class ToolResponse<TResult> {
13
13
  return true;
14
14
  }
15
15
 
16
- readonly artifact?: ReadonlyJSONValue | undefined;
16
+ readonly artifact?: ReadonlyJSONValue;
17
17
  readonly result: TResult;
18
18
  readonly isError: boolean;
19
19
 
20
20
  constructor(options: ToolResponseInit<TResult>) {
21
- this.artifact = options.artifact;
21
+ if (options.artifact !== undefined) {
22
+ this.artifact = options.artifact;
23
+ }
22
24
  this.result = options.result;
23
25
  this.isError = options.isError ?? false;
24
26
  }
@@ -65,7 +65,6 @@ function getToolResponse(
65
65
  function getToolStreamResponse(
66
66
  tools: Record<string, Tool> | undefined,
67
67
  abortSignal: AbortSignal,
68
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
69
68
  reader: ToolCallReader<any, ReadonlyJSONValue>,
70
69
  context: {
71
70
  toolCallId: string;
@@ -94,7 +93,9 @@ export async function unstable_runPendingTools(
94
93
  return {
95
94
  ...p,
96
95
  state: "result" as const,
97
- artifact: result.artifact,
96
+ ...(result.artifact !== undefined
97
+ ? { artifact: result.artifact }
98
+ : {}),
98
99
  result: result.result as ReadonlyJSONValue,
99
100
  isError: result.isError,
100
101
  };
@@ -1,5 +1,3 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
-
3
1
  type AsNumber<K> = K extends `${infer N extends number}` ? N | K : never;
4
2
  type TupleIndex<T extends readonly any[]> = Exclude<keyof T, keyof any[]>;
5
3
  type ObjectKey<T> = keyof T & (string | number);
@@ -0,0 +1,116 @@
1
+ import { PipeableTransformStream } from "./PipeableTransformStream";
2
+ import { LineDecoderStream } from "./LineDecoderStream";
3
+
4
+ export class SSEEncoder<T> extends PipeableTransformStream<T, Uint8Array> {
5
+ static readonly headers = new Headers({
6
+ "Content-Type": "text/event-stream",
7
+ "Cache-Control": "no-cache",
8
+ Connection: "keep-alive",
9
+ });
10
+
11
+ headers = SSEEncoder.headers;
12
+
13
+ constructor() {
14
+ super((readable) =>
15
+ readable
16
+ .pipeThrough(
17
+ new TransformStream<T, string>({
18
+ transform(chunk, controller) {
19
+ controller.enqueue(`data: ${JSON.stringify(chunk)}\n\n`);
20
+ },
21
+ }),
22
+ )
23
+ .pipeThrough(new TextEncoderStream()),
24
+ );
25
+ }
26
+ }
27
+
28
+ type SSEEvent = {
29
+ event: string;
30
+ data: string;
31
+ id?: string | undefined;
32
+ retry?: number | undefined;
33
+ };
34
+
35
+ class SSEEventStream extends TransformStream<string, SSEEvent> {
36
+ constructor() {
37
+ let eventBuffer: Partial<SSEEvent> = {};
38
+ let dataLines: string[] = [];
39
+
40
+ super({
41
+ start() {
42
+ eventBuffer = {};
43
+ dataLines = [];
44
+ },
45
+ transform(line, controller) {
46
+ if (line.startsWith(":")) return; // Ignore comments
47
+
48
+ if (line === "") {
49
+ if (dataLines.length > 0) {
50
+ controller.enqueue({
51
+ event: eventBuffer.event || "message",
52
+ data: dataLines.join("\n"),
53
+ id: eventBuffer.id,
54
+ retry: eventBuffer.retry,
55
+ });
56
+ }
57
+ eventBuffer = {};
58
+ dataLines = [];
59
+ return;
60
+ }
61
+
62
+ const [field, ...rest] = line.split(":");
63
+ const value = rest.join(":").trimStart();
64
+
65
+ switch (field) {
66
+ case "event":
67
+ eventBuffer.event = value;
68
+ break;
69
+ case "data":
70
+ dataLines.push(value);
71
+ break;
72
+ case "id":
73
+ eventBuffer.id = value;
74
+ break;
75
+ case "retry":
76
+ eventBuffer.retry = Number(value);
77
+ break;
78
+ }
79
+ },
80
+ flush(controller) {
81
+ if (dataLines.length > 0) {
82
+ controller.enqueue({
83
+ event: eventBuffer.event || "message",
84
+ data: dataLines.join("\n"),
85
+ id: eventBuffer.id,
86
+ retry: eventBuffer.retry,
87
+ });
88
+ }
89
+ },
90
+ });
91
+ }
92
+ }
93
+
94
+ export class SSEDecoder<T> extends PipeableTransformStream<Uint8Array, T> {
95
+ constructor() {
96
+ super((readable) =>
97
+ readable
98
+ .pipeThrough(new TextDecoderStream())
99
+ .pipeThrough(new LineDecoderStream())
100
+ .pipeThrough(new SSEEventStream())
101
+ .pipeThrough(
102
+ new TransformStream<SSEEvent, T>({
103
+ transform(event, controller) {
104
+ switch (event.event) {
105
+ case "message":
106
+ controller.enqueue(JSON.parse(event.data));
107
+ break;
108
+ default:
109
+ throw new Error(`Unknown SSE event type: ${event.event}`);
110
+ }
111
+ },
112
+ }),
113
+ ),
114
+ );
115
+ }
116
+ }
@@ -50,19 +50,32 @@ type ToolCallStatus =
50
50
  reason: "cancelled" | "length" | "content-filter" | "other";
51
51
  };
52
52
 
53
- export type ToolCallPart = {
53
+ type ToolCallPartBase = {
54
54
  type: "tool-call";
55
- state: "partial-call" | "call" | "result";
56
55
  status: ToolCallStatus;
57
56
  toolCallId: string;
58
57
  toolName: string;
59
58
  argsText: string;
60
59
  args: ReadonlyJSONObject;
61
- artifact?: unknown;
60
+ artifact?: ReadonlyJSONValue;
62
61
  result?: ReadonlyJSONValue;
63
62
  isError?: boolean;
64
63
  };
65
64
 
65
+ type ToolCallPartWithoutResult = ToolCallPartBase & {
66
+ state: "partial-call" | "call";
67
+ result?: undefined;
68
+ };
69
+
70
+ type ToolCallPartWithResult = ToolCallPartBase & {
71
+ state: "result";
72
+ result: ReadonlyJSONValue;
73
+ artifact?: ReadonlyJSONValue;
74
+ isError?: boolean;
75
+ };
76
+
77
+ export type ToolCallPart = ToolCallPartWithoutResult | ToolCallPartWithResult;
78
+
66
79
  export type SourcePart = {
67
80
  type: "source";
68
81
  sourceType: "url";
@@ -141,7 +154,9 @@ export type AssistantMessage = {
141
154
  * @deprecated Use `parts` instead.
142
155
  */
143
156
  content: AssistantMessagePart[];
157
+
144
158
  metadata: {
159
+ unstable_state: ReadonlyJSONValue;
145
160
  unstable_data: ReadonlyJSONValue[];
146
161
  unstable_annotations: ReadonlyJSONValue[];
147
162
  steps: AssistantMessageStepMetadata[];
@@ -1,2 +1,5 @@
1
- export { asAsyncIterableStream, type AsyncIterableStream } from "./AsyncIterableStream";
1
+ export {
2
+ asAsyncIterableStream,
3
+ type AsyncIterableStream,
4
+ } from "./AsyncIterableStream";
2
5
  export { type ReadonlyJSONObject, type ReadonlyJSONValue } from "./json";