assistant-stream 0.3.26 → 0.3.28

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 (145) hide show
  1. package/dist/core/AssistantStream.d.ts.map +1 -1
  2. package/dist/core/AssistantStreamChunk.d.ts +3 -3
  3. package/dist/core/accumulators/AssistantMessageStream.d.ts.map +1 -1
  4. package/dist/core/accumulators/AssistantMessageStream.js +0 -1
  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 +65 -28
  8. package/dist/core/accumulators/assistant-message-accumulator.js.map +1 -1
  9. package/dist/core/converters/toGenericMessages.js.map +1 -1
  10. package/dist/core/gorp/GorpStreamAccumulator.d.ts +15 -0
  11. package/dist/core/gorp/GorpStreamAccumulator.d.ts.map +1 -0
  12. package/dist/core/{object/ObjectStreamAccumulator.js → gorp/GorpStreamAccumulator.js} +11 -9
  13. package/dist/core/gorp/GorpStreamAccumulator.js.map +1 -0
  14. package/dist/core/gorp/GorpStreamDeltaTracker.d.ts +17 -0
  15. package/dist/core/gorp/GorpStreamDeltaTracker.d.ts.map +1 -0
  16. package/dist/core/gorp/GorpStreamDeltaTracker.js +38 -0
  17. package/dist/core/gorp/GorpStreamDeltaTracker.js.map +1 -0
  18. package/dist/core/gorp/GorpStreamResponse.d.ts +16 -0
  19. package/dist/core/gorp/GorpStreamResponse.d.ts.map +1 -0
  20. package/dist/core/gorp/GorpStreamResponse.js +51 -0
  21. package/dist/core/gorp/GorpStreamResponse.js.map +1 -0
  22. package/dist/core/gorp/changeTree.d.ts +16 -0
  23. package/dist/core/gorp/changeTree.d.ts.map +1 -0
  24. package/dist/core/gorp/changeTree.js +77 -0
  25. package/dist/core/gorp/changeTree.js.map +1 -0
  26. package/dist/core/gorp/createGorpStream.d.ts +16 -0
  27. package/dist/core/gorp/createGorpStream.d.ts.map +1 -0
  28. package/dist/core/{object/createObjectStream.js → gorp/createGorpStream.js} +13 -8
  29. package/dist/core/gorp/createGorpStream.js.map +1 -0
  30. package/dist/core/{object → gorp}/types.d.ts +6 -5
  31. package/dist/core/gorp/types.d.ts.map +1 -0
  32. package/dist/core/modules/assistant-stream.d.ts +5 -4
  33. package/dist/core/modules/assistant-stream.d.ts.map +1 -1
  34. package/dist/core/modules/assistant-stream.js +10 -6
  35. package/dist/core/modules/assistant-stream.js.map +1 -1
  36. package/dist/core/modules/tool-call.d.ts.map +1 -1
  37. package/dist/core/modules/tool-call.js +3 -3
  38. package/dist/core/modules/tool-call.js.map +1 -1
  39. package/dist/core/serialization/assistant-transport/AssistantTransport.d.ts.map +1 -1
  40. package/dist/core/serialization/assistant-transport/AssistantTransport.js +91 -1
  41. package/dist/core/serialization/assistant-transport/AssistantTransport.js.map +1 -1
  42. package/dist/core/serialization/data-stream/DataStream.js +48 -48
  43. package/dist/core/serialization/data-stream/DataStream.js.map +1 -1
  44. package/dist/core/serialization/data-stream/chunk-types.d.ts +25 -24
  45. package/dist/core/serialization/data-stream/chunk-types.d.ts.map +1 -1
  46. package/dist/core/serialization/data-stream/chunk-types.js +22 -23
  47. package/dist/core/serialization/data-stream/chunk-types.js.map +1 -1
  48. package/dist/core/serialization/data-stream/serialization.d.ts.map +1 -1
  49. package/dist/core/serialization/data-stream/serialization.js +14 -2
  50. package/dist/core/serialization/data-stream/serialization.js.map +1 -1
  51. package/dist/core/serialization/ui-message-stream/UIMessageStream.d.ts.map +1 -1
  52. package/dist/core/serialization/ui-message-stream/UIMessageStream.js +12 -1
  53. package/dist/core/serialization/ui-message-stream/UIMessageStream.js.map +1 -1
  54. package/dist/core/tool/ToolCallReader.d.ts.map +1 -1
  55. package/dist/core/tool/ToolCallReader.js.map +1 -1
  56. package/dist/core/tool/ToolExecutionStream.d.ts.map +1 -1
  57. package/dist/core/tool/ToolExecutionStream.js +9 -1
  58. package/dist/core/tool/ToolExecutionStream.js.map +1 -1
  59. package/dist/core/tool/ToolResponse.d.ts.map +1 -1
  60. package/dist/core/tool/tool-types.d.ts +6 -0
  61. package/dist/core/tool/tool-types.d.ts.map +1 -1
  62. package/dist/core/tool/toolResultStream.d.ts.map +1 -1
  63. package/dist/core/tool/toolResultStream.js +7 -2
  64. package/dist/core/tool/toolResultStream.js.map +1 -1
  65. package/dist/core/utils/stream/AssistantTransformStream.d.ts.map +1 -1
  66. package/dist/core/utils/stream/PipeableTransformStream.d.ts.map +1 -1
  67. package/dist/core/utils/stream/SSE.d.ts.map +1 -1
  68. package/dist/core/utils/stream/SSE.js +11 -2
  69. package/dist/core/utils/stream/SSE.js.map +1 -1
  70. package/dist/core/utils/stream/SSEEventDecoder.d.ts.map +1 -1
  71. package/dist/core/utils/stream/merge.d.ts +2 -0
  72. package/dist/core/utils/stream/merge.d.ts.map +1 -1
  73. package/dist/core/utils/stream/merge.js +27 -8
  74. package/dist/core/utils/stream/merge.js.map +1 -1
  75. package/dist/core/utils/stream/path-utils.d.ts.map +1 -1
  76. package/dist/index.d.ts +25 -4
  77. package/dist/index.d.ts.map +1 -0
  78. package/dist/index.js +14 -3
  79. package/dist/index.js.map +1 -0
  80. package/dist/resumable/ResumableStreamContext.d.ts.map +1 -1
  81. package/dist/resumable/ResumableStreamContext.js +12 -5
  82. package/dist/resumable/ResumableStreamContext.js.map +1 -1
  83. package/dist/resumable/errors.d.ts.map +1 -1
  84. package/dist/resumable/stores/redis-impl.d.ts.map +1 -1
  85. package/dist/utils/AsyncIterableStream.d.ts.map +1 -1
  86. package/dist/utils/AsyncIterableStream.js +18 -3
  87. package/dist/utils/AsyncIterableStream.js.map +1 -1
  88. package/dist/utils/promiseWithResolvers.d.ts.map +1 -1
  89. package/package.json +2 -2
  90. package/src/core/AssistantStreamChunk.ts +3 -3
  91. package/src/core/accumulators/AssistantMessageStream.ts +3 -1
  92. package/src/core/accumulators/assistant-message-accumulator.test.ts +374 -1
  93. package/src/core/accumulators/assistant-message-accumulator.ts +75 -26
  94. package/src/core/converters/toGenericMessages.test.ts +3 -39
  95. package/src/core/{object/ObjectStream.test.ts → gorp/GorpStream.test.ts} +86 -25
  96. package/src/core/gorp/GorpStreamAccumulator.test.ts +28 -0
  97. package/src/core/{object/ObjectStreamAccumulator.ts → gorp/GorpStreamAccumulator.ts} +13 -15
  98. package/src/core/gorp/GorpStreamDeltaTracker.test.ts +212 -0
  99. package/src/core/gorp/GorpStreamDeltaTracker.ts +55 -0
  100. package/src/core/{object/ObjectStreamResponse.test.ts → gorp/GorpStreamResponse.test.ts} +6 -6
  101. package/src/core/gorp/GorpStreamResponse.ts +88 -0
  102. package/src/core/gorp/changeTree.ts +96 -0
  103. package/src/core/{object/createObjectStream.ts → gorp/createGorpStream.ts} +23 -17
  104. package/src/core/{object → gorp}/types.ts +5 -3
  105. package/src/core/modules/assistant-stream.test.ts +154 -2
  106. package/src/core/modules/assistant-stream.ts +16 -6
  107. package/src/core/modules/tool-call.test.ts +67 -0
  108. package/src/core/modules/tool-call.ts +7 -4
  109. package/src/core/serialization/assistant-transport/AssistantTransport.test.ts +174 -1
  110. package/src/core/serialization/assistant-transport/AssistantTransport.ts +77 -1
  111. package/src/core/serialization/data-stream/DataStream.ts +1 -1
  112. package/src/core/serialization/data-stream/chunk-types.ts +26 -24
  113. package/src/core/serialization/data-stream/serialization.test.ts +133 -0
  114. package/src/core/serialization/data-stream/serialization.ts +20 -2
  115. package/src/core/serialization/ui-message-stream/UIMessageStream.test.ts +135 -0
  116. package/src/core/serialization/ui-message-stream/UIMessageStream.ts +21 -1
  117. package/src/core/tool/ToolCallReader.ts +5 -1
  118. package/src/core/tool/ToolExecutionStream.ts +10 -0
  119. package/src/core/tool/tool-types.ts +7 -0
  120. package/src/core/tool/toolResultStream.test.ts +50 -0
  121. package/src/core/tool/toolResultStream.ts +7 -2
  122. package/src/core/utils/stream/SSE.test.ts +97 -0
  123. package/src/core/utils/stream/SSE.ts +13 -2
  124. package/src/core/utils/stream/merge.ts +31 -9
  125. package/src/gorp.test.ts +32 -0
  126. package/src/index.ts +21 -6
  127. package/src/resumable/ResumableStreamContext.test.ts +116 -1
  128. package/src/resumable/ResumableStreamContext.ts +32 -5
  129. package/src/resumable/createResumableAssistantStreamResponse.test.ts +1 -25
  130. package/src/utils/AsyncIterableStream.test.ts +61 -0
  131. package/src/utils/AsyncIterableStream.ts +18 -3
  132. package/dist/core/object/ObjectStreamAccumulator.d.ts +0 -15
  133. package/dist/core/object/ObjectStreamAccumulator.d.ts.map +0 -1
  134. package/dist/core/object/ObjectStreamAccumulator.js.map +0 -1
  135. package/dist/core/object/ObjectStreamResponse.d.ts +0 -16
  136. package/dist/core/object/ObjectStreamResponse.d.ts.map +0 -1
  137. package/dist/core/object/ObjectStreamResponse.js +0 -57
  138. package/dist/core/object/ObjectStreamResponse.js.map +0 -1
  139. package/dist/core/object/createObjectStream.d.ts +0 -16
  140. package/dist/core/object/createObjectStream.d.ts.map +0 -1
  141. package/dist/core/object/createObjectStream.js.map +0 -1
  142. package/dist/core/object/types.d.ts.map +0 -1
  143. package/src/core/accumulators/TimingTracker.test.ts +0 -100
  144. package/src/core/object/ObjectStreamResponse.ts +0 -114
  145. /package/dist/core/{object → gorp}/types.js +0 -0
@@ -1,4 +1,4 @@
1
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
1
+ import { describe, expect, it } from "vitest";
2
2
  import {
3
3
  RESUMABLE_STREAM_ID_HEADER,
4
4
  createResumableAssistantStreamResponse,
@@ -140,25 +140,6 @@ describe("createResumableAssistantStreamResponse", () => {
140
140
  });
141
141
 
142
142
  describe("producer crash", () => {
143
- let suppressed: unknown[];
144
- let original: NodeJS.UnhandledRejectionListener[];
145
- const swallow: NodeJS.UnhandledRejectionListener = (reason) => {
146
- suppressed.push(reason);
147
- };
148
-
149
- beforeEach(() => {
150
- suppressed = [];
151
- original = process.listeners("unhandledRejection");
152
- for (const l of original) process.off("unhandledRejection", l);
153
- process.on("unhandledRejection", swallow);
154
- });
155
-
156
- afterEach(async () => {
157
- await new Promise((r) => setTimeout(r, 20));
158
- process.off("unhandledRejection", swallow);
159
- for (const l of original) process.on("unhandledRejection", l);
160
- });
161
-
162
143
  it("synchronous callback throw is encoded into the body and finalizes the stream", async () => {
163
144
  const ctx = createResumableStreamContext({
164
145
  store: createInMemoryResumableStreamStore(),
@@ -184,11 +165,6 @@ describe("createResumableAssistantStreamResponse", () => {
184
165
  expect(replay).not.toBeNull();
185
166
  const replayBytes = await new Response(replay).arrayBuffer();
186
167
  expect(new Uint8Array(replayBytes)).toEqual(new Uint8Array(firstBytes));
187
-
188
- while (suppressed.length === 0) {
189
- await new Promise((r) => setTimeout(r, 5));
190
- }
191
- expect(suppressed.map((e) => (e as Error).message)).toContain("boom");
192
168
  });
193
169
  });
194
170
 
@@ -0,0 +1,61 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { asAsyncIterableStream } from "./AsyncIterableStream";
3
+
4
+ const forcePolyfilledIterator = <T>(
5
+ stream: ReadableStream<T>,
6
+ ): AsyncIterable<T> & ReadableStream<T> => {
7
+ Object.defineProperty(stream, Symbol.asyncIterator, {
8
+ configurable: true,
9
+ value: undefined,
10
+ writable: true,
11
+ });
12
+ return asAsyncIterableStream(stream);
13
+ };
14
+
15
+ describe("asAsyncIterableStream", () => {
16
+ it("cancels the source when iteration stops early", async () => {
17
+ const cancel = vi.fn();
18
+ const stream = forcePolyfilledIterator(
19
+ new ReadableStream<string>({
20
+ start(controller) {
21
+ controller.enqueue("first");
22
+ controller.enqueue("second");
23
+ },
24
+ cancel,
25
+ }),
26
+ );
27
+
28
+ const values: string[] = [];
29
+ for await (const value of stream) {
30
+ values.push(value);
31
+ break;
32
+ }
33
+
34
+ expect(values).toEqual(["first"]);
35
+ expect(cancel).toHaveBeenCalledOnce();
36
+ expect(stream.locked).toBe(false);
37
+ });
38
+
39
+ it("does not cancel a fully consumed source", async () => {
40
+ const cancel = vi.fn();
41
+ const stream = forcePolyfilledIterator(
42
+ new ReadableStream<string>({
43
+ start(controller) {
44
+ controller.enqueue("first");
45
+ controller.enqueue("second");
46
+ controller.close();
47
+ },
48
+ cancel,
49
+ }),
50
+ );
51
+
52
+ const values: string[] = [];
53
+ for await (const value of stream) {
54
+ values.push(value);
55
+ }
56
+
57
+ expect(values).toEqual(["first", "second"]);
58
+ expect(cancel).not.toHaveBeenCalled();
59
+ expect(stream.locked).toBe(false);
60
+ });
61
+ });
@@ -4,14 +4,29 @@ async function* streamGeneratorPolyfill<T>(
4
4
  this: ReadableStream<T>,
5
5
  ): AsyncGenerator<T, undefined, unknown> {
6
6
  const reader = this.getReader();
7
+ let shouldCancel = true;
7
8
  try {
8
9
  while (true) {
9
- const { done, value } = await reader.read();
10
- if (done) break;
10
+ let result: ReadableStreamReadResult<T>;
11
+ try {
12
+ result = await reader.read();
13
+ } catch (error) {
14
+ shouldCancel = false;
15
+ throw error;
16
+ }
17
+ if (result.done) {
18
+ shouldCancel = false;
19
+ break;
20
+ }
21
+ const { value } = result;
11
22
  yield value;
12
23
  }
13
24
  } finally {
14
- reader.releaseLock();
25
+ try {
26
+ if (shouldCancel) await reader.cancel();
27
+ } finally {
28
+ reader.releaseLock();
29
+ }
15
30
  }
16
31
  }
17
32
 
@@ -1,15 +0,0 @@
1
- import { ReadonlyJSONValue } from "../../utils/json/json-value.js";
2
- import "../../utils.js";
3
- import { ObjectStreamOperation } from "./types.js";
4
- //#region src/core/object/ObjectStreamAccumulator.d.ts
5
- declare class ObjectStreamAccumulator {
6
- private _state;
7
- constructor(initialValue?: ReadonlyJSONValue);
8
- get state(): ReadonlyJSONValue;
9
- append(ops: readonly ObjectStreamOperation[]): void;
10
- private static apply;
11
- private static updatePath;
12
- }
13
- //#endregion
14
- export { ObjectStreamAccumulator };
15
- //# sourceMappingURL=ObjectStreamAccumulator.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ObjectStreamAccumulator.d.ts","names":[],"sources":["../../../src/core/object/ObjectStreamAccumulator.ts"],"mappings":";;;;cAGa;UACH;cAEI,eAAc;MAItB,SAAK;EAIT,OAAO,cAAc;iBAON;iBAuBA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"ObjectStreamAccumulator.js","names":["_exhaustiveCheck"],"sources":["../../../src/core/object/ObjectStreamAccumulator.ts"],"sourcesContent":["import type { ReadonlyJSONValue, ReadonlyJSONObject } from \"../../utils\";\nimport type { ObjectStreamOperation } from \"./types\";\n\nexport class ObjectStreamAccumulator {\n private _state: ReadonlyJSONValue;\n\n constructor(initialValue: ReadonlyJSONValue = null) {\n this._state = initialValue;\n }\n\n get state() {\n return this._state;\n }\n\n append(ops: readonly ObjectStreamOperation[]) {\n this._state = ops.reduce(\n (state, op) => ObjectStreamAccumulator.apply(state, op),\n this._state,\n );\n }\n\n private static apply(state: ReadonlyJSONValue, op: ObjectStreamOperation) {\n const type = op.type;\n switch (type) {\n case \"set\":\n return ObjectStreamAccumulator.updatePath(\n state,\n op.path,\n () => op.value,\n );\n case \"append-text\":\n return ObjectStreamAccumulator.updatePath(state, op.path, (current) => {\n if (typeof current !== \"string\")\n throw new Error(`Expected string at path [${op.path.join(\", \")}]`);\n return current + op.value;\n });\n\n default: {\n const _exhaustiveCheck: never = type;\n throw new Error(`Invalid operation type: ${_exhaustiveCheck}`);\n }\n }\n }\n\n private static updatePath(\n state: ReadonlyJSONValue | undefined,\n path: readonly string[],\n updater: (current: ReadonlyJSONValue | undefined) => ReadonlyJSONValue,\n ): ReadonlyJSONValue {\n if (path.length === 0) return updater(state);\n\n // Initialize state as empty object if it's null and we're trying to set a property\n state ??= {};\n\n if (typeof state !== \"object\") {\n throw new Error(`Invalid path: [${path.join(\", \")}]`);\n }\n\n const [key, ...rest] = path as [string, ...(readonly string[])];\n if (Array.isArray(state)) {\n const idx = Number(key);\n if (Number.isNaN(idx))\n throw new Error(`Expected array index at [${path.join(\", \")}]`);\n if (idx > state.length || idx < 0)\n throw new Error(`Insert array index out of bounds`);\n\n const nextState = [...state];\n nextState[idx] = ObjectStreamAccumulator.updatePath(\n nextState[idx],\n rest,\n updater,\n );\n\n return nextState;\n }\n\n const nextState = { ...(state as ReadonlyJSONObject) };\n nextState[key] = ObjectStreamAccumulator.updatePath(\n nextState[key],\n rest,\n updater,\n );\n\n return nextState;\n }\n}\n"],"mappings":";AAGA,IAAa,0BAAb,MAAa,wBAAwB;CACnC;CAEA,YAAY,eAAkC,MAAM;EAClD,KAAK,SAAS;CAChB;CAEA,IAAI,QAAQ;EACV,OAAO,KAAK;CACd;CAEA,OAAO,KAAuC;EAC5C,KAAK,SAAS,IAAI,QACf,OAAO,OAAO,wBAAwB,MAAM,OAAO,EAAE,GACtD,KAAK,MACP;CACF;CAEA,OAAe,MAAM,OAA0B,IAA2B;EACxE,MAAM,OAAO,GAAG;EAChB,QAAQ,MAAR;GACE,KAAK,OACH,OAAO,wBAAwB,WAC7B,OACA,GAAG,YACG,GAAG,KACX;GACF,KAAK,eACH,OAAO,wBAAwB,WAAW,OAAO,GAAG,OAAO,YAAY;IACrE,IAAI,OAAO,YAAY,UACrB,MAAM,IAAI,MAAM,4BAA4B,GAAG,KAAK,KAAK,IAAI,EAAE,EAAE;IACnE,OAAO,UAAU,GAAG;GACtB,CAAC;GAEH,SAEE,MAAM,IAAI,MAAM,2BAA2BA,MAAkB;EAEjE;CACF;CAEA,OAAe,WACb,OACA,MACA,SACmB;EACnB,IAAI,KAAK,WAAW,GAAG,OAAO,QAAQ,KAAK;EAG3C,UAAU,CAAC;EAEX,IAAI,OAAO,UAAU,UACnB,MAAM,IAAI,MAAM,kBAAkB,KAAK,KAAK,IAAI,EAAE,EAAE;EAGtD,MAAM,CAAC,KAAK,GAAG,QAAQ;EACvB,IAAI,MAAM,QAAQ,KAAK,GAAG;GACxB,MAAM,MAAM,OAAO,GAAG;GACtB,IAAI,OAAO,MAAM,GAAG,GAClB,MAAM,IAAI,MAAM,4BAA4B,KAAK,KAAK,IAAI,EAAE,EAAE;GAChE,IAAI,MAAM,MAAM,UAAU,MAAM,GAC9B,MAAM,IAAI,MAAM,kCAAkC;GAEpD,MAAM,YAAY,CAAC,GAAG,KAAK;GAC3B,UAAU,OAAO,wBAAwB,WACvC,UAAU,MACV,MACA,OACF;GAEA,OAAO;EACT;EAEA,MAAM,YAAY,EAAE,GAAI,MAA6B;EACrD,UAAU,OAAO,wBAAwB,WACvC,UAAU,MACV,MACA,OACF;EAEA,OAAO;CACT;AACF"}
@@ -1,16 +0,0 @@
1
- import { ObjectStreamChunk } from "./types.js";
2
- import { PipeableTransformStream } from "../utils/stream/PipeableTransformStream.js";
3
- //#region src/core/object/ObjectStreamResponse.d.ts
4
- declare class ObjectStreamEncoder extends PipeableTransformStream<ObjectStreamChunk, Uint8Array> {
5
- constructor();
6
- }
7
- declare class ObjectStreamDecoder extends PipeableTransformStream<Uint8Array<ArrayBuffer>, ObjectStreamChunk> {
8
- constructor();
9
- }
10
- declare class ObjectStreamResponse extends Response {
11
- constructor(body: ReadableStream<ObjectStreamChunk>);
12
- }
13
- declare const fromObjectStreamResponse: (response: Response) => ReadableStream<ObjectStreamChunk>;
14
- //#endregion
15
- export { ObjectStreamDecoder, ObjectStreamEncoder, ObjectStreamResponse, fromObjectStreamResponse };
16
- //# sourceMappingURL=ObjectStreamResponse.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ObjectStreamResponse.d.ts","names":[],"sources":["../../../src/core/object/ObjectStreamResponse.ts"],"mappings":";;;cAKa,4BAA4B,wBACvC,mBACA;;;cAgDW,4BAA4B,wBACvC,WAAW,cACX;;;cAyBW,6BAA6B;cAC5B,MAAM,eAAe;;cAYtB,2BACX,UAAU,aACT,eAAe"}
@@ -1,57 +0,0 @@
1
- import { PipeableTransformStream } from "../utils/stream/PipeableTransformStream.js";
2
- import { ObjectStreamAccumulator } from "./ObjectStreamAccumulator.js";
3
- import { SSEDecoder, SSEEncoder } from "../utils/stream/SSE.js";
4
- //#region src/core/object/ObjectStreamResponse.ts
5
- var ObjectStreamEncoder = class extends PipeableTransformStream {
6
- constructor() {
7
- super((readable) => readable.pipeThrough((() => {
8
- class ObjectStreamTransformer {
9
- #isFirstChunk = true;
10
- start() {}
11
- transform(chunk, controller) {
12
- if (this.#isFirstChunk && chunk.snapshot && Object.keys(chunk.snapshot).length > 0) controller.enqueue([{
13
- type: "set",
14
- path: [],
15
- value: chunk.snapshot
16
- }, ...chunk.operations]);
17
- else controller.enqueue(chunk.operations);
18
- this.#isFirstChunk = false;
19
- }
20
- }
21
- return new TransformStream(new ObjectStreamTransformer());
22
- })()).pipeThrough(new SSEEncoder()));
23
- }
24
- };
25
- var ObjectStreamDecoder = class extends PipeableTransformStream {
26
- constructor() {
27
- const accumulator = new ObjectStreamAccumulator();
28
- super((readable) => readable.pipeThrough(new SSEDecoder()).pipeThrough(new TransformStream({ transform(operations, controller) {
29
- accumulator.append(operations);
30
- controller.enqueue({
31
- snapshot: accumulator.state,
32
- operations
33
- });
34
- } })));
35
- }
36
- };
37
- var ObjectStreamResponse = class extends Response {
38
- constructor(body) {
39
- super(body.pipeThrough(new ObjectStreamEncoder()), { headers: new Headers({
40
- "Content-Type": "text/event-stream",
41
- "Cache-Control": "no-cache",
42
- Connection: "keep-alive",
43
- "Assistant-Stream-Format": "object-stream/v0"
44
- }) });
45
- }
46
- };
47
- const fromObjectStreamResponse = (response) => {
48
- if (!response.ok) throw new Error(`Response failed, status ${response.status}`);
49
- if (!response.body) throw new Error("Response body is null");
50
- if (response.headers.get("Content-Type")?.split(";", 1)[0]?.trim().toLowerCase() !== "text/event-stream") throw new Error("Response is not an event stream");
51
- if (response.headers.get("Assistant-Stream-Format") !== "object-stream/v0") throw new Error("Unsupported Assistant-Stream-Format header");
52
- return response.body.pipeThrough(new ObjectStreamDecoder());
53
- };
54
- //#endregion
55
- export { ObjectStreamDecoder, ObjectStreamEncoder, ObjectStreamResponse, fromObjectStreamResponse };
56
-
57
- //# sourceMappingURL=ObjectStreamResponse.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ObjectStreamResponse.js","names":["#isFirstChunk"],"sources":["../../../src/core/object/ObjectStreamResponse.ts"],"sourcesContent":["import { PipeableTransformStream } from \"../utils/stream/PipeableTransformStream\";\nimport { ObjectStreamAccumulator } from \"./ObjectStreamAccumulator\";\nimport { SSEDecoder, SSEEncoder } from \"../utils/stream/SSE\";\nimport type { ObjectStreamChunk, ObjectStreamOperation } from \"./types\";\n\nexport class ObjectStreamEncoder extends PipeableTransformStream<\n ObjectStreamChunk,\n Uint8Array\n> {\n constructor() {\n super((readable) =>\n readable\n .pipeThrough(\n (() => {\n class ObjectStreamTransformer implements Transformer<\n ObjectStreamChunk,\n readonly ObjectStreamOperation[]\n > {\n #isFirstChunk = true;\n\n start() {\n // Nothing needed here since we initialize in the field declaration\n }\n\n transform(\n chunk: ObjectStreamChunk,\n controller: TransformStreamDefaultController<\n readonly ObjectStreamOperation[]\n >,\n ) {\n if (\n this.#isFirstChunk &&\n chunk.snapshot &&\n Object.keys(chunk.snapshot).length > 0\n ) {\n // For the first chunk, if there's an initial state that's not empty,\n // prepend a set operation for the initial state\n controller.enqueue([\n { type: \"set\", path: [], value: chunk.snapshot },\n ...chunk.operations,\n ]);\n } else {\n controller.enqueue(chunk.operations);\n }\n this.#isFirstChunk = false;\n }\n }\n return new TransformStream(new ObjectStreamTransformer());\n })(),\n )\n .pipeThrough(new SSEEncoder()),\n );\n }\n}\n\nexport class ObjectStreamDecoder extends PipeableTransformStream<\n Uint8Array<ArrayBuffer>,\n ObjectStreamChunk\n> {\n constructor() {\n const accumulator = new ObjectStreamAccumulator();\n super((readable) =>\n readable\n .pipeThrough(new SSEDecoder<readonly ObjectStreamOperation[]>())\n .pipeThrough(\n new TransformStream<\n readonly ObjectStreamOperation[],\n ObjectStreamChunk\n >({\n transform(operations, controller) {\n accumulator.append(operations);\n controller.enqueue({\n snapshot: accumulator.state,\n operations,\n });\n },\n }),\n ),\n );\n }\n}\n\nexport class ObjectStreamResponse extends Response {\n constructor(body: ReadableStream<ObjectStreamChunk>) {\n super(body.pipeThrough(new ObjectStreamEncoder()), {\n headers: new Headers({\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n \"Assistant-Stream-Format\": \"object-stream/v0\",\n }),\n });\n }\n}\n\nexport const fromObjectStreamResponse = (\n response: Response,\n): ReadableStream<ObjectStreamChunk> => {\n if (!response.ok)\n throw new Error(`Response failed, status ${response.status}`);\n if (!response.body) throw new Error(\"Response body is null\");\n const mediaType = response.headers\n .get(\"Content-Type\")\n ?.split(\";\", 1)[0]\n ?.trim()\n .toLowerCase();\n if (mediaType !== \"text/event-stream\") {\n throw new Error(\"Response is not an event stream\");\n }\n if (response.headers.get(\"Assistant-Stream-Format\") !== \"object-stream/v0\") {\n throw new Error(\"Unsupported Assistant-Stream-Format header\");\n }\n return response.body.pipeThrough(new ObjectStreamDecoder());\n};\n"],"mappings":";;;;AAKA,IAAa,sBAAb,cAAyC,wBAGvC;CACA,cAAc;EACZ,OAAO,aACL,SACG,mBACQ;GACL,MAAM,wBAGJ;IACA,gBAAgB;IAEhB,QAAQ,CAER;IAEA,UACE,OACA,YAGA;KACA,IACE,KAAKA,iBACL,MAAM,YACN,OAAO,KAAK,MAAM,QAAQ,CAAC,CAAC,SAAS,GAIrC,WAAW,QAAQ,CACjB;MAAE,MAAM;MAAO,MAAM,CAAC;MAAG,OAAO,MAAM;KAAS,GAC/C,GAAG,MAAM,UACX,CAAC;UAED,WAAW,QAAQ,MAAM,UAAU;KAErC,KAAKA,gBAAgB;IACvB;GACF;GACA,OAAO,IAAI,gBAAgB,IAAI,wBAAwB,CAAC;EAC1D,EAAA,CAAG,CACL,CAAC,CACA,YAAY,IAAI,WAAW,CAAC,CACjC;CACF;AACF;AAEA,IAAa,sBAAb,cAAyC,wBAGvC;CACA,cAAc;EACZ,MAAM,cAAc,IAAI,wBAAwB;EAChD,OAAO,aACL,SACG,YAAY,IAAI,WAA6C,CAAC,CAAC,CAC/D,YACC,IAAI,gBAGF,EACA,UAAU,YAAY,YAAY;GAChC,YAAY,OAAO,UAAU;GAC7B,WAAW,QAAQ;IACjB,UAAU,YAAY;IACtB;GACF,CAAC;EACH,EACF,CAAC,CACH,CACJ;CACF;AACF;AAEA,IAAa,uBAAb,cAA0C,SAAS;CACjD,YAAY,MAAyC;EACnD,MAAM,KAAK,YAAY,IAAI,oBAAoB,CAAC,GAAG,EACjD,SAAS,IAAI,QAAQ;GACnB,gBAAgB;GAChB,iBAAiB;GACjB,YAAY;GACZ,2BAA2B;EAC7B,CAAC,EACH,CAAC;CACH;AACF;AAEA,MAAa,4BACX,aACsC;CACtC,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MAAM,2BAA2B,SAAS,QAAQ;CAC9D,IAAI,CAAC,SAAS,MAAM,MAAM,IAAI,MAAM,uBAAuB;CAM3D,IALkB,SAAS,QACxB,IAAI,cAAc,CAAC,EAClB,MAAM,KAAK,CAAC,CAAC,CAAC,EAAE,EAChB,KAAK,CAAC,CACP,YAAY,MACG,qBAChB,MAAM,IAAI,MAAM,iCAAiC;CAEnD,IAAI,SAAS,QAAQ,IAAI,yBAAyB,MAAM,oBACtD,MAAM,IAAI,MAAM,4CAA4C;CAE9D,OAAO,SAAS,KAAK,YAAY,IAAI,oBAAoB,CAAC;AAC5D"}
@@ -1,16 +0,0 @@
1
- import { ReadonlyJSONValue } from "../../utils/json/json-value.js";
2
- import "../../utils.js";
3
- import { ObjectStreamChunk, ObjectStreamOperation } from "./types.js";
4
- //#region src/core/object/createObjectStream.d.ts
5
- type ObjectStreamController = {
6
- readonly abortSignal: AbortSignal;
7
- enqueue(operations: readonly ObjectStreamOperation[]): void;
8
- };
9
- type CreateObjectStreamOptions = {
10
- execute: (controller: ObjectStreamController) => void | PromiseLike<void>;
11
- defaultValue?: ReadonlyJSONValue;
12
- };
13
- declare const createObjectStream: ({ execute, defaultValue }: CreateObjectStreamOptions) => ReadableStream<ObjectStreamChunk>;
14
- //#endregion
15
- export { createObjectStream };
16
- //# sourceMappingURL=createObjectStream.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"createObjectStream.d.ts","names":[],"sources":["../../../src/core/object/createObjectStream.ts"],"mappings":";;;;KAKK;WACM,aAAa;EAEtB,QAAQ,qBAAqB;;KAwD1B;EACH,UAAU,YAAY,kCAAkC;EACxD,eAAe;;cAGJ,uBAAsB,SAAA,gBAGhC,8BAAyB,eAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"createObjectStream.js","names":[],"sources":["../../../src/core/object/createObjectStream.ts"],"sourcesContent":["import type { ReadonlyJSONValue } from \"../../utils\";\nimport { withPromiseOrValue } from \"../utils/withPromiseOrValue\";\nimport { ObjectStreamAccumulator } from \"./ObjectStreamAccumulator\";\nimport type { ObjectStreamOperation, ObjectStreamChunk } from \"./types\";\n\ntype ObjectStreamController = {\n readonly abortSignal: AbortSignal;\n\n enqueue(operations: readonly ObjectStreamOperation[]): void;\n};\n\nclass ObjectStreamControllerImpl implements ObjectStreamController {\n private _controller: ReadableStreamDefaultController<ObjectStreamChunk>;\n private _abortController = new AbortController();\n private _accumulator: ObjectStreamAccumulator;\n\n get abortSignal() {\n return this._abortController.signal;\n }\n\n constructor(\n controller: ReadableStreamDefaultController<ObjectStreamChunk>,\n defaultValue: ReadonlyJSONValue,\n ) {\n this._controller = controller;\n this._accumulator = new ObjectStreamAccumulator(defaultValue);\n }\n\n enqueue(operations: readonly ObjectStreamOperation[]) {\n this._accumulator.append(operations);\n\n this._controller.enqueue({\n snapshot: this._accumulator.state,\n operations,\n });\n }\n\n __internalError(error: unknown) {\n this._controller.error(error);\n }\n\n __internalClose() {\n this._controller.close();\n }\n\n __internalCancel(reason?: unknown) {\n this._abortController.abort(reason);\n }\n}\n\nconst getStreamControllerPair = (defaultValue: ReadonlyJSONValue) => {\n let controller!: ObjectStreamControllerImpl;\n const stream = new ReadableStream<ObjectStreamChunk>({\n start(c) {\n controller = new ObjectStreamControllerImpl(c, defaultValue);\n },\n cancel(reason: unknown) {\n controller.__internalCancel(reason);\n },\n });\n\n return [stream, controller] as const;\n};\n\ntype CreateObjectStreamOptions = {\n execute: (controller: ObjectStreamController) => void | PromiseLike<void>;\n defaultValue?: ReadonlyJSONValue;\n};\n\nexport const createObjectStream = ({\n execute,\n defaultValue = {},\n}: CreateObjectStreamOptions) => {\n const [stream, controller] = getStreamControllerPair(defaultValue);\n\n withPromiseOrValue(\n () => execute(controller),\n () => {\n controller.__internalClose();\n },\n (e: unknown) => {\n controller.__internalError(e);\n },\n );\n\n return stream;\n};\n"],"mappings":";;;AAWA,IAAM,6BAAN,MAAmE;CACjE;CACA,mBAA2B,IAAI,gBAAgB;CAC/C;CAEA,IAAI,cAAc;EAChB,OAAO,KAAK,iBAAiB;CAC/B;CAEA,YACE,YACA,cACA;EACA,KAAK,cAAc;EACnB,KAAK,eAAe,IAAI,wBAAwB,YAAY;CAC9D;CAEA,QAAQ,YAA8C;EACpD,KAAK,aAAa,OAAO,UAAU;EAEnC,KAAK,YAAY,QAAQ;GACvB,UAAU,KAAK,aAAa;GAC5B;EACF,CAAC;CACH;CAEA,gBAAgB,OAAgB;EAC9B,KAAK,YAAY,MAAM,KAAK;CAC9B;CAEA,kBAAkB;EAChB,KAAK,YAAY,MAAM;CACzB;CAEA,iBAAiB,QAAkB;EACjC,KAAK,iBAAiB,MAAM,MAAM;CACpC;AACF;AAEA,MAAM,2BAA2B,iBAAoC;CACnE,IAAI;CAUJ,OAAO,CAAC,IATW,eAAkC;EACnD,MAAM,GAAG;GACP,aAAa,IAAI,2BAA2B,GAAG,YAAY;EAC7D;EACA,OAAO,QAAiB;GACtB,WAAW,iBAAiB,MAAM;EACpC;CACF,CAEa,GAAG,UAAU;AAC5B;AAOA,MAAa,sBAAsB,EACjC,SACA,eAAe,CAAC,QACe;CAC/B,MAAM,CAAC,QAAQ,cAAc,wBAAwB,YAAY;CAEjE,yBACQ,QAAQ,UAAU,SAClB;EACJ,WAAW,gBAAgB;CAC7B,IACC,MAAe;EACd,WAAW,gBAAgB,CAAC;CAC9B,CACF;CAEA,OAAO;AACT"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/core/object/types.ts"],"mappings":";;;KAEY;WAEG;WACA;WACA,OAAO;;WAGP;WACA;WACA;;KAGH;WACD,UAAU;WACV,qBAAqB"}
@@ -1,100 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
- import { TimingTracker } from "./TimingTracker";
3
-
4
- describe("TimingTracker", () => {
5
- beforeEach(() => {
6
- vi.useFakeTimers();
7
- vi.setSystemTime(1000);
8
- });
9
-
10
- afterEach(() => {
11
- vi.useRealTimers();
12
- });
13
-
14
- it("should record stream start time", () => {
15
- const tracker = new TimingTracker();
16
- const timing = tracker.getTiming();
17
- expect(timing.streamStartTime).toBe(1000);
18
- });
19
-
20
- it("should count chunks", () => {
21
- const tracker = new TimingTracker();
22
- tracker.recordChunk();
23
- tracker.recordChunk();
24
- tracker.recordChunk();
25
- const timing = tracker.getTiming();
26
- expect(timing.totalChunks).toBe(3);
27
- });
28
-
29
- it("should record first token time", () => {
30
- const tracker = new TimingTracker();
31
- vi.advanceTimersByTime(50);
32
- tracker.recordFirstToken();
33
- vi.advanceTimersByTime(100);
34
- tracker.recordFirstToken(); // should not overwrite
35
- const timing = tracker.getTiming();
36
- expect(timing.firstTokenTime).toBe(50);
37
- });
38
-
39
- it("should not set firstTokenTime if no text tokens", () => {
40
- const tracker = new TimingTracker();
41
- const timing = tracker.getTiming();
42
- expect(timing.firstTokenTime).toBeUndefined();
43
- });
44
-
45
- it("should track unique tool calls", () => {
46
- const tracker = new TimingTracker();
47
- tracker.recordToolCallStart("tc-1");
48
- tracker.recordToolCallStart("tc-2");
49
- tracker.recordToolCallStart("tc-1"); // duplicate
50
- const timing = tracker.getTiming();
51
- expect(timing.toolCallCount).toBe(2);
52
- });
53
-
54
- it("should compute totalStreamTime", () => {
55
- const tracker = new TimingTracker();
56
- vi.advanceTimersByTime(200);
57
- const timing = tracker.getTiming();
58
- expect(timing.totalStreamTime).toBe(200);
59
- });
60
-
61
- it("should use outputTokens when available", () => {
62
- const tracker = new TimingTracker();
63
- vi.advanceTimersByTime(1000);
64
- const timing = tracker.getTiming(42, "some text");
65
- expect(timing.tokenCount).toBe(42);
66
- expect(timing.tokensPerSecond).toBe(42);
67
- });
68
-
69
- it("should estimate tokens from text when no outputTokens", () => {
70
- const tracker = new TimingTracker();
71
- vi.advanceTimersByTime(1000);
72
- // 20 chars / 4 = 5 tokens
73
- const timing = tracker.getTiming(undefined, "12345678901234567890");
74
- expect(timing.tokenCount).toBe(5);
75
- expect(timing.tokensPerSecond).toBe(5);
76
- });
77
-
78
- it("should handle zero outputTokens by falling back to text", () => {
79
- const tracker = new TimingTracker();
80
- vi.advanceTimersByTime(1000);
81
- const timing = tracker.getTiming(0, "1234567890"); // 10 chars -> 3 tokens
82
- expect(timing.tokenCount).toBe(3);
83
- });
84
-
85
- it("should handle no tokens and no text", () => {
86
- const tracker = new TimingTracker();
87
- vi.advanceTimersByTime(100);
88
- const timing = tracker.getTiming();
89
- expect(timing.tokenCount).toBeUndefined();
90
- expect(timing.tokensPerSecond).toBeUndefined();
91
- });
92
-
93
- it("should handle zero stream time (no division by zero)", () => {
94
- const tracker = new TimingTracker();
95
- // Don't advance time — totalStreamTime = 0
96
- const timing = tracker.getTiming(100, "text");
97
- expect(timing.totalStreamTime).toBe(0);
98
- expect(timing.tokensPerSecond).toBeUndefined();
99
- });
100
- });
@@ -1,114 +0,0 @@
1
- import { PipeableTransformStream } from "../utils/stream/PipeableTransformStream";
2
- import { ObjectStreamAccumulator } from "./ObjectStreamAccumulator";
3
- import { SSEDecoder, SSEEncoder } from "../utils/stream/SSE";
4
- import type { 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
- (() => {
15
- class ObjectStreamTransformer implements Transformer<
16
- ObjectStreamChunk,
17
- readonly ObjectStreamOperation[]
18
- > {
19
- #isFirstChunk = true;
20
-
21
- start() {
22
- // Nothing needed here since we initialize in the field declaration
23
- }
24
-
25
- transform(
26
- chunk: ObjectStreamChunk,
27
- controller: TransformStreamDefaultController<
28
- readonly ObjectStreamOperation[]
29
- >,
30
- ) {
31
- if (
32
- this.#isFirstChunk &&
33
- chunk.snapshot &&
34
- Object.keys(chunk.snapshot).length > 0
35
- ) {
36
- // For the first chunk, if there's an initial state that's not empty,
37
- // prepend a set operation for the initial state
38
- controller.enqueue([
39
- { type: "set", path: [], value: chunk.snapshot },
40
- ...chunk.operations,
41
- ]);
42
- } else {
43
- controller.enqueue(chunk.operations);
44
- }
45
- this.#isFirstChunk = false;
46
- }
47
- }
48
- return new TransformStream(new ObjectStreamTransformer());
49
- })(),
50
- )
51
- .pipeThrough(new SSEEncoder()),
52
- );
53
- }
54
- }
55
-
56
- export class ObjectStreamDecoder extends PipeableTransformStream<
57
- Uint8Array<ArrayBuffer>,
58
- ObjectStreamChunk
59
- > {
60
- constructor() {
61
- const accumulator = new ObjectStreamAccumulator();
62
- super((readable) =>
63
- readable
64
- .pipeThrough(new SSEDecoder<readonly ObjectStreamOperation[]>())
65
- .pipeThrough(
66
- new TransformStream<
67
- readonly ObjectStreamOperation[],
68
- ObjectStreamChunk
69
- >({
70
- transform(operations, controller) {
71
- accumulator.append(operations);
72
- controller.enqueue({
73
- snapshot: accumulator.state,
74
- operations,
75
- });
76
- },
77
- }),
78
- ),
79
- );
80
- }
81
- }
82
-
83
- export class ObjectStreamResponse extends Response {
84
- constructor(body: ReadableStream<ObjectStreamChunk>) {
85
- super(body.pipeThrough(new ObjectStreamEncoder()), {
86
- headers: new Headers({
87
- "Content-Type": "text/event-stream",
88
- "Cache-Control": "no-cache",
89
- Connection: "keep-alive",
90
- "Assistant-Stream-Format": "object-stream/v0",
91
- }),
92
- });
93
- }
94
- }
95
-
96
- export const fromObjectStreamResponse = (
97
- response: Response,
98
- ): ReadableStream<ObjectStreamChunk> => {
99
- if (!response.ok)
100
- throw new Error(`Response failed, status ${response.status}`);
101
- if (!response.body) throw new Error("Response body is null");
102
- const mediaType = response.headers
103
- .get("Content-Type")
104
- ?.split(";", 1)[0]
105
- ?.trim()
106
- .toLowerCase();
107
- if (mediaType !== "text/event-stream") {
108
- throw new Error("Response is not an event stream");
109
- }
110
- if (response.headers.get("Assistant-Stream-Format") !== "object-stream/v0") {
111
- throw new Error("Unsupported Assistant-Stream-Format header");
112
- }
113
- return response.body.pipeThrough(new ObjectStreamDecoder());
114
- };
File without changes