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.
- package/dist/core/AssistantStream.d.ts.map +1 -1
- package/dist/core/AssistantStreamChunk.d.ts +3 -3
- package/dist/core/accumulators/AssistantMessageStream.d.ts.map +1 -1
- package/dist/core/accumulators/AssistantMessageStream.js +0 -1
- package/dist/core/accumulators/AssistantMessageStream.js.map +1 -1
- package/dist/core/accumulators/assistant-message-accumulator.d.ts.map +1 -1
- package/dist/core/accumulators/assistant-message-accumulator.js +65 -28
- package/dist/core/accumulators/assistant-message-accumulator.js.map +1 -1
- package/dist/core/converters/toGenericMessages.js.map +1 -1
- package/dist/core/gorp/GorpStreamAccumulator.d.ts +15 -0
- package/dist/core/gorp/GorpStreamAccumulator.d.ts.map +1 -0
- package/dist/core/{object/ObjectStreamAccumulator.js → gorp/GorpStreamAccumulator.js} +11 -9
- package/dist/core/gorp/GorpStreamAccumulator.js.map +1 -0
- package/dist/core/gorp/GorpStreamDeltaTracker.d.ts +17 -0
- package/dist/core/gorp/GorpStreamDeltaTracker.d.ts.map +1 -0
- package/dist/core/gorp/GorpStreamDeltaTracker.js +38 -0
- package/dist/core/gorp/GorpStreamDeltaTracker.js.map +1 -0
- package/dist/core/gorp/GorpStreamResponse.d.ts +16 -0
- package/dist/core/gorp/GorpStreamResponse.d.ts.map +1 -0
- package/dist/core/gorp/GorpStreamResponse.js +51 -0
- package/dist/core/gorp/GorpStreamResponse.js.map +1 -0
- package/dist/core/gorp/changeTree.d.ts +16 -0
- package/dist/core/gorp/changeTree.d.ts.map +1 -0
- package/dist/core/gorp/changeTree.js +77 -0
- package/dist/core/gorp/changeTree.js.map +1 -0
- package/dist/core/gorp/createGorpStream.d.ts +16 -0
- package/dist/core/gorp/createGorpStream.d.ts.map +1 -0
- package/dist/core/{object/createObjectStream.js → gorp/createGorpStream.js} +13 -8
- package/dist/core/gorp/createGorpStream.js.map +1 -0
- package/dist/core/{object → gorp}/types.d.ts +6 -5
- package/dist/core/gorp/types.d.ts.map +1 -0
- package/dist/core/modules/assistant-stream.d.ts +5 -4
- package/dist/core/modules/assistant-stream.d.ts.map +1 -1
- package/dist/core/modules/assistant-stream.js +10 -6
- package/dist/core/modules/assistant-stream.js.map +1 -1
- package/dist/core/modules/tool-call.d.ts.map +1 -1
- package/dist/core/modules/tool-call.js +3 -3
- package/dist/core/modules/tool-call.js.map +1 -1
- package/dist/core/serialization/assistant-transport/AssistantTransport.d.ts.map +1 -1
- package/dist/core/serialization/assistant-transport/AssistantTransport.js +91 -1
- package/dist/core/serialization/assistant-transport/AssistantTransport.js.map +1 -1
- package/dist/core/serialization/data-stream/DataStream.js +48 -48
- package/dist/core/serialization/data-stream/DataStream.js.map +1 -1
- package/dist/core/serialization/data-stream/chunk-types.d.ts +25 -24
- package/dist/core/serialization/data-stream/chunk-types.d.ts.map +1 -1
- package/dist/core/serialization/data-stream/chunk-types.js +22 -23
- package/dist/core/serialization/data-stream/chunk-types.js.map +1 -1
- package/dist/core/serialization/data-stream/serialization.d.ts.map +1 -1
- package/dist/core/serialization/data-stream/serialization.js +14 -2
- package/dist/core/serialization/data-stream/serialization.js.map +1 -1
- package/dist/core/serialization/ui-message-stream/UIMessageStream.d.ts.map +1 -1
- package/dist/core/serialization/ui-message-stream/UIMessageStream.js +12 -1
- package/dist/core/serialization/ui-message-stream/UIMessageStream.js.map +1 -1
- package/dist/core/tool/ToolCallReader.d.ts.map +1 -1
- package/dist/core/tool/ToolCallReader.js.map +1 -1
- package/dist/core/tool/ToolExecutionStream.d.ts.map +1 -1
- package/dist/core/tool/ToolExecutionStream.js +9 -1
- package/dist/core/tool/ToolExecutionStream.js.map +1 -1
- package/dist/core/tool/ToolResponse.d.ts.map +1 -1
- package/dist/core/tool/tool-types.d.ts +6 -0
- package/dist/core/tool/tool-types.d.ts.map +1 -1
- package/dist/core/tool/toolResultStream.d.ts.map +1 -1
- package/dist/core/tool/toolResultStream.js +7 -2
- package/dist/core/tool/toolResultStream.js.map +1 -1
- package/dist/core/utils/stream/AssistantTransformStream.d.ts.map +1 -1
- package/dist/core/utils/stream/PipeableTransformStream.d.ts.map +1 -1
- package/dist/core/utils/stream/SSE.d.ts.map +1 -1
- package/dist/core/utils/stream/SSE.js +11 -2
- package/dist/core/utils/stream/SSE.js.map +1 -1
- package/dist/core/utils/stream/SSEEventDecoder.d.ts.map +1 -1
- package/dist/core/utils/stream/merge.d.ts +2 -0
- package/dist/core/utils/stream/merge.d.ts.map +1 -1
- package/dist/core/utils/stream/merge.js +27 -8
- package/dist/core/utils/stream/merge.js.map +1 -1
- package/dist/core/utils/stream/path-utils.d.ts.map +1 -1
- package/dist/index.d.ts +25 -4
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -3
- package/dist/index.js.map +1 -0
- package/dist/resumable/ResumableStreamContext.d.ts.map +1 -1
- package/dist/resumable/ResumableStreamContext.js +12 -5
- package/dist/resumable/ResumableStreamContext.js.map +1 -1
- package/dist/resumable/errors.d.ts.map +1 -1
- package/dist/resumable/stores/redis-impl.d.ts.map +1 -1
- package/dist/utils/AsyncIterableStream.d.ts.map +1 -1
- package/dist/utils/AsyncIterableStream.js +18 -3
- package/dist/utils/AsyncIterableStream.js.map +1 -1
- package/dist/utils/promiseWithResolvers.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/core/AssistantStreamChunk.ts +3 -3
- package/src/core/accumulators/AssistantMessageStream.ts +3 -1
- package/src/core/accumulators/assistant-message-accumulator.test.ts +374 -1
- package/src/core/accumulators/assistant-message-accumulator.ts +75 -26
- package/src/core/converters/toGenericMessages.test.ts +3 -39
- package/src/core/{object/ObjectStream.test.ts → gorp/GorpStream.test.ts} +86 -25
- package/src/core/gorp/GorpStreamAccumulator.test.ts +28 -0
- package/src/core/{object/ObjectStreamAccumulator.ts → gorp/GorpStreamAccumulator.ts} +13 -15
- package/src/core/gorp/GorpStreamDeltaTracker.test.ts +212 -0
- package/src/core/gorp/GorpStreamDeltaTracker.ts +55 -0
- package/src/core/{object/ObjectStreamResponse.test.ts → gorp/GorpStreamResponse.test.ts} +6 -6
- package/src/core/gorp/GorpStreamResponse.ts +88 -0
- package/src/core/gorp/changeTree.ts +96 -0
- package/src/core/{object/createObjectStream.ts → gorp/createGorpStream.ts} +23 -17
- package/src/core/{object → gorp}/types.ts +5 -3
- package/src/core/modules/assistant-stream.test.ts +154 -2
- package/src/core/modules/assistant-stream.ts +16 -6
- package/src/core/modules/tool-call.test.ts +67 -0
- package/src/core/modules/tool-call.ts +7 -4
- package/src/core/serialization/assistant-transport/AssistantTransport.test.ts +174 -1
- package/src/core/serialization/assistant-transport/AssistantTransport.ts +77 -1
- package/src/core/serialization/data-stream/DataStream.ts +1 -1
- package/src/core/serialization/data-stream/chunk-types.ts +26 -24
- package/src/core/serialization/data-stream/serialization.test.ts +133 -0
- package/src/core/serialization/data-stream/serialization.ts +20 -2
- package/src/core/serialization/ui-message-stream/UIMessageStream.test.ts +135 -0
- package/src/core/serialization/ui-message-stream/UIMessageStream.ts +21 -1
- package/src/core/tool/ToolCallReader.ts +5 -1
- package/src/core/tool/ToolExecutionStream.ts +10 -0
- package/src/core/tool/tool-types.ts +7 -0
- package/src/core/tool/toolResultStream.test.ts +50 -0
- package/src/core/tool/toolResultStream.ts +7 -2
- package/src/core/utils/stream/SSE.test.ts +97 -0
- package/src/core/utils/stream/SSE.ts +13 -2
- package/src/core/utils/stream/merge.ts +31 -9
- package/src/gorp.test.ts +32 -0
- package/src/index.ts +21 -6
- package/src/resumable/ResumableStreamContext.test.ts +116 -1
- package/src/resumable/ResumableStreamContext.ts +32 -5
- package/src/resumable/createResumableAssistantStreamResponse.test.ts +1 -25
- package/src/utils/AsyncIterableStream.test.ts +61 -0
- package/src/utils/AsyncIterableStream.ts +18 -3
- package/dist/core/object/ObjectStreamAccumulator.d.ts +0 -15
- package/dist/core/object/ObjectStreamAccumulator.d.ts.map +0 -1
- package/dist/core/object/ObjectStreamAccumulator.js.map +0 -1
- package/dist/core/object/ObjectStreamResponse.d.ts +0 -16
- package/dist/core/object/ObjectStreamResponse.d.ts.map +0 -1
- package/dist/core/object/ObjectStreamResponse.js +0 -57
- package/dist/core/object/ObjectStreamResponse.js.map +0 -1
- package/dist/core/object/createObjectStream.d.ts +0 -16
- package/dist/core/object/createObjectStream.d.ts.map +0 -1
- package/dist/core/object/createObjectStream.js.map +0 -1
- package/dist/core/object/types.d.ts.map +0 -1
- package/src/core/accumulators/TimingTracker.test.ts +0 -100
- package/src/core/object/ObjectStreamResponse.ts +0 -114
- /package/dist/core/{object → gorp}/types.js +0 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { PipeableTransformStream } from "../utils/stream/PipeableTransformStream.js";
|
|
2
|
+
import { GorpStreamAccumulator } from "./GorpStreamAccumulator.js";
|
|
3
|
+
import { SSEDecoder, SSEEncoder } from "../utils/stream/SSE.js";
|
|
4
|
+
//#region src/core/gorp/GorpStreamResponse.ts
|
|
5
|
+
var GorpStreamEncoder = class extends PipeableTransformStream {
|
|
6
|
+
constructor() {
|
|
7
|
+
let isFirstChunk = true;
|
|
8
|
+
super((readable) => readable.pipeThrough(new TransformStream({ transform(chunk, controller) {
|
|
9
|
+
if (isFirstChunk) controller.enqueue([{
|
|
10
|
+
type: "set",
|
|
11
|
+
path: [],
|
|
12
|
+
value: chunk.snapshot
|
|
13
|
+
}]);
|
|
14
|
+
else controller.enqueue(chunk.operations);
|
|
15
|
+
isFirstChunk = false;
|
|
16
|
+
} })).pipeThrough(new SSEEncoder()));
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
var GorpStreamDecoder = class extends PipeableTransformStream {
|
|
20
|
+
constructor() {
|
|
21
|
+
const accumulator = new GorpStreamAccumulator();
|
|
22
|
+
super((readable) => readable.pipeThrough(new SSEDecoder()).pipeThrough(new TransformStream({ transform(operations, controller) {
|
|
23
|
+
accumulator.append(operations);
|
|
24
|
+
controller.enqueue({
|
|
25
|
+
snapshot: accumulator.state,
|
|
26
|
+
operations
|
|
27
|
+
});
|
|
28
|
+
} })));
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
var GorpStreamResponse = class extends Response {
|
|
32
|
+
constructor(body) {
|
|
33
|
+
super(body.pipeThrough(new GorpStreamEncoder()), { headers: new Headers({
|
|
34
|
+
"Content-Type": "text/event-stream",
|
|
35
|
+
"Cache-Control": "no-cache",
|
|
36
|
+
Connection: "keep-alive",
|
|
37
|
+
"Assistant-Stream-Format": "object-stream/v0"
|
|
38
|
+
}) });
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
const fromGorpStreamResponse = (response) => {
|
|
42
|
+
if (!response.ok) throw new Error(`Response failed, status ${response.status}`);
|
|
43
|
+
if (!response.body) throw new Error("Response body is null");
|
|
44
|
+
if (response.headers.get("Content-Type")?.split(";", 1)[0]?.trim().toLowerCase() !== "text/event-stream") throw new Error("Response is not an event stream");
|
|
45
|
+
if (response.headers.get("Assistant-Stream-Format") !== "object-stream/v0") throw new Error("Unsupported Assistant-Stream-Format header");
|
|
46
|
+
return response.body.pipeThrough(new GorpStreamDecoder());
|
|
47
|
+
};
|
|
48
|
+
//#endregion
|
|
49
|
+
export { GorpStreamDecoder, GorpStreamEncoder, GorpStreamResponse, fromGorpStreamResponse };
|
|
50
|
+
|
|
51
|
+
//# sourceMappingURL=GorpStreamResponse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GorpStreamResponse.js","names":[],"sources":["../../../src/core/gorp/GorpStreamResponse.ts"],"sourcesContent":["import { PipeableTransformStream } from \"../utils/stream/PipeableTransformStream\";\nimport { GorpStreamAccumulator } from \"./GorpStreamAccumulator\";\nimport { SSEDecoder, SSEEncoder } from \"../utils/stream/SSE\";\nimport type { GorpStreamChunk, GorpStreamOperation } from \"./types\";\n\nexport class GorpStreamEncoder extends PipeableTransformStream<\n GorpStreamChunk,\n Uint8Array\n> {\n constructor() {\n let isFirstChunk = true;\n super((readable) =>\n readable\n .pipeThrough(\n new TransformStream<GorpStreamChunk, readonly GorpStreamOperation[]>({\n transform(chunk, controller) {\n if (isFirstChunk) {\n controller.enqueue([\n { type: \"set\", path: [], value: chunk.snapshot },\n ]);\n } else {\n controller.enqueue(chunk.operations);\n }\n isFirstChunk = false;\n },\n }),\n )\n .pipeThrough(new SSEEncoder()),\n );\n }\n}\n\nexport class GorpStreamDecoder extends PipeableTransformStream<\n Uint8Array<ArrayBuffer>,\n GorpStreamChunk\n> {\n constructor() {\n const accumulator = new GorpStreamAccumulator();\n super((readable) =>\n readable\n .pipeThrough(new SSEDecoder<readonly GorpStreamOperation[]>())\n .pipeThrough(\n new TransformStream<readonly GorpStreamOperation[], GorpStreamChunk>({\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 GorpStreamResponse extends Response {\n constructor(body: ReadableStream<GorpStreamChunk>) {\n super(body.pipeThrough(new GorpStreamEncoder()), {\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 fromGorpStreamResponse = (\n response: Response,\n): ReadableStream<GorpStreamChunk> => {\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 GorpStreamDecoder());\n};\n"],"mappings":";;;;AAKA,IAAa,oBAAb,cAAuC,wBAGrC;CACA,cAAc;EACZ,IAAI,eAAe;EACnB,OAAO,aACL,SACG,YACC,IAAI,gBAAiE,EACnE,UAAU,OAAO,YAAY;GAC3B,IAAI,cACF,WAAW,QAAQ,CACjB;IAAE,MAAM;IAAO,MAAM,CAAC;IAAG,OAAO,MAAM;GAAS,CACjD,CAAC;QAED,WAAW,QAAQ,MAAM,UAAU;GAErC,eAAe;EACjB,EACF,CAAC,CACH,CAAC,CACA,YAAY,IAAI,WAAW,CAAC,CACjC;CACF;AACF;AAEA,IAAa,oBAAb,cAAuC,wBAGrC;CACA,cAAc;EACZ,MAAM,cAAc,IAAI,sBAAsB;EAC9C,OAAO,aACL,SACG,YAAY,IAAI,WAA2C,CAAC,CAAC,CAC7D,YACC,IAAI,gBAAiE,EACnE,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,qBAAb,cAAwC,SAAS;CAC/C,YAAY,MAAuC;EACjD,MAAM,KAAK,YAAY,IAAI,kBAAkB,CAAC,GAAG,EAC/C,SAAS,IAAI,QAAQ;GACnB,gBAAgB;GAChB,iBAAiB;GACjB,YAAY;GACZ,2BAA2B;EAC7B,CAAC,EACH,CAAC;CACH;AACF;AAEA,MAAa,0BACX,aACoC;CACpC,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,kBAAkB,CAAC;AAC1D"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region src/core/gorp/changeTree.d.ts
|
|
2
|
+
type ChangeNode = true | {
|
|
3
|
+
[key: string]: ChangeNode;
|
|
4
|
+
};
|
|
5
|
+
declare function createChangeNode(): {
|
|
6
|
+
[key: string]: ChangeNode;
|
|
7
|
+
};
|
|
8
|
+
declare function assertSafePathSegment(segment: string): void;
|
|
9
|
+
declare function markChanged(node: ChangeNode, path: readonly string[]): ChangeNode;
|
|
10
|
+
declare function mergeChanged(target: ChangeNode, source: ChangeNode): ChangeNode;
|
|
11
|
+
declare function lookupChange(node: ChangeNode, path: readonly string[]): ChangeNode | false;
|
|
12
|
+
declare function lookupValue(state: unknown, path: readonly string[]): unknown;
|
|
13
|
+
declare function diffKeys(newNode: unknown, oldNode: unknown): string[];
|
|
14
|
+
//#endregion
|
|
15
|
+
export { ChangeNode, assertSafePathSegment, createChangeNode, diffKeys, lookupChange, lookupValue, markChanged, mergeChanged };
|
|
16
|
+
//# sourceMappingURL=changeTree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"changeTree.d.ts","names":[],"sources":["../../../src/core/gorp/changeTree.ts"],"mappings":";KAAY;GAAuB,cAAc;;iBAEjC;GAAuB,cAAc;;iBAMrC,sBAAsB;iBAMtB,YACd,MAAM,YACN,0BACC;iBAiBa,aACd,QAAQ,YACR,QAAQ,aACP;iBAwBa,aACd,MAAM,YACN,0BACC;iBAUa,YAAY,gBAAgB;iBAY5B,SAAS,kBAAkB"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
//#region src/core/gorp/changeTree.ts
|
|
2
|
+
function createChangeNode() {
|
|
3
|
+
return Object.create(null);
|
|
4
|
+
}
|
|
5
|
+
const UNSAFE_PATH_SEGMENTS = /* @__PURE__ */ new Set([
|
|
6
|
+
"__proto__",
|
|
7
|
+
"constructor",
|
|
8
|
+
"prototype"
|
|
9
|
+
]);
|
|
10
|
+
function assertSafePathSegment(segment) {
|
|
11
|
+
if (UNSAFE_PATH_SEGMENTS.has(segment)) throw new Error(`Unsafe gorp path segment: ${segment}`);
|
|
12
|
+
}
|
|
13
|
+
function markChanged(node, path) {
|
|
14
|
+
if (node === true || path.length === 0) return true;
|
|
15
|
+
let cursor = node;
|
|
16
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
17
|
+
const key = path[i];
|
|
18
|
+
assertSafePathSegment(key);
|
|
19
|
+
const next = Object.hasOwn(cursor, key) ? cursor[key] : void 0;
|
|
20
|
+
if (next === true) return node;
|
|
21
|
+
if (next === void 0) cursor[key] = createChangeNode();
|
|
22
|
+
cursor = cursor[key];
|
|
23
|
+
}
|
|
24
|
+
const leaf = path[path.length - 1];
|
|
25
|
+
assertSafePathSegment(leaf);
|
|
26
|
+
cursor[leaf] = true;
|
|
27
|
+
return node;
|
|
28
|
+
}
|
|
29
|
+
function mergeChanged(target, source) {
|
|
30
|
+
if (target === true || source === true) return true;
|
|
31
|
+
for (const key of Object.keys(source)) {
|
|
32
|
+
assertSafePathSegment(key);
|
|
33
|
+
const sChild = source[key];
|
|
34
|
+
const tChild = Object.hasOwn(target, key) ? target[key] : void 0;
|
|
35
|
+
target[key] = tChild === void 0 ? cloneChangeNode(sChild) : mergeChanged(tChild, sChild);
|
|
36
|
+
}
|
|
37
|
+
return target;
|
|
38
|
+
}
|
|
39
|
+
function cloneChangeNode(source) {
|
|
40
|
+
if (source === true) return true;
|
|
41
|
+
const clone = createChangeNode();
|
|
42
|
+
for (const key of Object.keys(source)) {
|
|
43
|
+
assertSafePathSegment(key);
|
|
44
|
+
clone[key] = cloneChangeNode(source[key]);
|
|
45
|
+
}
|
|
46
|
+
return clone;
|
|
47
|
+
}
|
|
48
|
+
function lookupChange(node, path) {
|
|
49
|
+
for (const key of path) {
|
|
50
|
+
if (node === true) return true;
|
|
51
|
+
const next = Object.hasOwn(node, key) ? node[key] : void 0;
|
|
52
|
+
if (next === void 0) return false;
|
|
53
|
+
node = next;
|
|
54
|
+
}
|
|
55
|
+
return node;
|
|
56
|
+
}
|
|
57
|
+
function lookupValue(state, path) {
|
|
58
|
+
let node = state;
|
|
59
|
+
for (const key of path) {
|
|
60
|
+
assertSafePathSegment(key);
|
|
61
|
+
if (typeof node !== "object" || node === null) return void 0;
|
|
62
|
+
node = Object.hasOwn(node, key) ? node[key] : void 0;
|
|
63
|
+
}
|
|
64
|
+
return node;
|
|
65
|
+
}
|
|
66
|
+
function diffKeys(newNode, oldNode) {
|
|
67
|
+
const newKeys = typeof newNode === "object" && newNode !== null ? Object.keys(newNode) : [];
|
|
68
|
+
const oldKeys = typeof oldNode === "object" && oldNode !== null ? Object.keys(oldNode) : [];
|
|
69
|
+
if (oldKeys.length === 0) return newKeys;
|
|
70
|
+
const newSet = new Set(newKeys);
|
|
71
|
+
const deletedKeys = oldKeys.filter((k) => !newSet.has(k)).reverse();
|
|
72
|
+
return [...newKeys, ...deletedKeys];
|
|
73
|
+
}
|
|
74
|
+
//#endregion
|
|
75
|
+
export { assertSafePathSegment, createChangeNode, diffKeys, lookupChange, lookupValue, markChanged, mergeChanged };
|
|
76
|
+
|
|
77
|
+
//# sourceMappingURL=changeTree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"changeTree.js","names":[],"sources":["../../../src/core/gorp/changeTree.ts"],"sourcesContent":["export type ChangeNode = true | { [key: string]: ChangeNode };\n\nexport function createChangeNode(): { [key: string]: ChangeNode } {\n return Object.create(null);\n}\n\nconst UNSAFE_PATH_SEGMENTS = new Set([\"__proto__\", \"constructor\", \"prototype\"]);\n\nexport function assertSafePathSegment(segment: string): void {\n if (UNSAFE_PATH_SEGMENTS.has(segment)) {\n throw new Error(`Unsafe gorp path segment: ${segment}`);\n }\n}\n\nexport function markChanged(\n node: ChangeNode,\n path: readonly string[],\n): ChangeNode {\n if (node === true || path.length === 0) return true;\n let cursor = node;\n for (let i = 0; i < path.length - 1; i++) {\n const key = path[i]!;\n assertSafePathSegment(key);\n const next = Object.hasOwn(cursor, key) ? cursor[key] : undefined;\n if (next === true) return node;\n if (next === undefined) cursor[key] = createChangeNode();\n cursor = cursor[key] as { [k: string]: ChangeNode };\n }\n const leaf = path[path.length - 1]!;\n assertSafePathSegment(leaf);\n cursor[leaf] = true;\n return node;\n}\n\nexport function mergeChanged(\n target: ChangeNode,\n source: ChangeNode,\n): ChangeNode {\n if (target === true || source === true) return true;\n for (const key of Object.keys(source)) {\n assertSafePathSegment(key);\n const sChild = source[key]!;\n const tChild = Object.hasOwn(target, key) ? target[key] : undefined;\n target[key] =\n tChild === undefined\n ? cloneChangeNode(sChild)\n : mergeChanged(tChild, sChild);\n }\n return target;\n}\n\nfunction cloneChangeNode(source: ChangeNode): ChangeNode {\n if (source === true) return true;\n const clone = createChangeNode();\n for (const key of Object.keys(source)) {\n assertSafePathSegment(key);\n clone[key] = cloneChangeNode(source[key]!);\n }\n return clone;\n}\n\nexport function lookupChange(\n node: ChangeNode,\n path: readonly string[],\n): ChangeNode | false {\n for (const key of path) {\n if (node === true) return true;\n const next = Object.hasOwn(node, key) ? node[key] : undefined;\n if (next === undefined) return false;\n node = next;\n }\n return node;\n}\n\nexport function lookupValue(state: unknown, path: readonly string[]): unknown {\n let node = state;\n for (const key of path) {\n assertSafePathSegment(key);\n if (typeof node !== \"object\" || node === null) return undefined;\n node = Object.hasOwn(node, key)\n ? (node as Record<string, unknown>)[key]\n : undefined;\n }\n return node;\n}\n\nexport function diffKeys(newNode: unknown, oldNode: unknown): string[] {\n const newKeys =\n typeof newNode === \"object\" && newNode !== null ? Object.keys(newNode) : [];\n const oldKeys =\n typeof oldNode === \"object\" && oldNode !== null ? Object.keys(oldNode) : [];\n if (oldKeys.length === 0) return newKeys;\n const newSet = new Set(newKeys);\n const deletedKeys = oldKeys.filter((k) => !newSet.has(k)).reverse();\n return [...newKeys, ...deletedKeys];\n}\n"],"mappings":";AAEA,SAAgB,mBAAkD;CAChE,OAAO,OAAO,OAAO,IAAI;AAC3B;AAEA,MAAM,uCAAuB,IAAI,IAAI;CAAC;CAAa;CAAe;AAAW,CAAC;AAE9E,SAAgB,sBAAsB,SAAuB;CAC3D,IAAI,qBAAqB,IAAI,OAAO,GAClC,MAAM,IAAI,MAAM,6BAA6B,SAAS;AAE1D;AAEA,SAAgB,YACd,MACA,MACY;CACZ,IAAI,SAAS,QAAQ,KAAK,WAAW,GAAG,OAAO;CAC/C,IAAI,SAAS;CACb,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,SAAS,GAAG,KAAK;EACxC,MAAM,MAAM,KAAK;EACjB,sBAAsB,GAAG;EACzB,MAAM,OAAO,OAAO,OAAO,QAAQ,GAAG,IAAI,OAAO,OAAO,KAAA;EACxD,IAAI,SAAS,MAAM,OAAO;EAC1B,IAAI,SAAS,KAAA,GAAW,OAAO,OAAO,iBAAiB;EACvD,SAAS,OAAO;CAClB;CACA,MAAM,OAAO,KAAK,KAAK,SAAS;CAChC,sBAAsB,IAAI;CAC1B,OAAO,QAAQ;CACf,OAAO;AACT;AAEA,SAAgB,aACd,QACA,QACY;CACZ,IAAI,WAAW,QAAQ,WAAW,MAAM,OAAO;CAC/C,KAAK,MAAM,OAAO,OAAO,KAAK,MAAM,GAAG;EACrC,sBAAsB,GAAG;EACzB,MAAM,SAAS,OAAO;EACtB,MAAM,SAAS,OAAO,OAAO,QAAQ,GAAG,IAAI,OAAO,OAAO,KAAA;EAC1D,OAAO,OACL,WAAW,KAAA,IACP,gBAAgB,MAAM,IACtB,aAAa,QAAQ,MAAM;CACnC;CACA,OAAO;AACT;AAEA,SAAS,gBAAgB,QAAgC;CACvD,IAAI,WAAW,MAAM,OAAO;CAC5B,MAAM,QAAQ,iBAAiB;CAC/B,KAAK,MAAM,OAAO,OAAO,KAAK,MAAM,GAAG;EACrC,sBAAsB,GAAG;EACzB,MAAM,OAAO,gBAAgB,OAAO,IAAK;CAC3C;CACA,OAAO;AACT;AAEA,SAAgB,aACd,MACA,MACoB;CACpB,KAAK,MAAM,OAAO,MAAM;EACtB,IAAI,SAAS,MAAM,OAAO;EAC1B,MAAM,OAAO,OAAO,OAAO,MAAM,GAAG,IAAI,KAAK,OAAO,KAAA;EACpD,IAAI,SAAS,KAAA,GAAW,OAAO;EAC/B,OAAO;CACT;CACA,OAAO;AACT;AAEA,SAAgB,YAAY,OAAgB,MAAkC;CAC5E,IAAI,OAAO;CACX,KAAK,MAAM,OAAO,MAAM;EACtB,sBAAsB,GAAG;EACzB,IAAI,OAAO,SAAS,YAAY,SAAS,MAAM,OAAO,KAAA;EACtD,OAAO,OAAO,OAAO,MAAM,GAAG,IACzB,KAAiC,OAClC,KAAA;CACN;CACA,OAAO;AACT;AAEA,SAAgB,SAAS,SAAkB,SAA4B;CACrE,MAAM,UACJ,OAAO,YAAY,YAAY,YAAY,OAAO,OAAO,KAAK,OAAO,IAAI,CAAC;CAC5E,MAAM,UACJ,OAAO,YAAY,YAAY,YAAY,OAAO,OAAO,KAAK,OAAO,IAAI,CAAC;CAC5E,IAAI,QAAQ,WAAW,GAAG,OAAO;CACjC,MAAM,SAAS,IAAI,IAAI,OAAO;CAC9B,MAAM,cAAc,QAAQ,QAAQ,MAAM,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ;CAClE,OAAO,CAAC,GAAG,SAAS,GAAG,WAAW;AACpC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ReadonlyJSONValue } from "../../utils/json/json-value.js";
|
|
2
|
+
import "../../utils.js";
|
|
3
|
+
import { GorpStreamChunk, GorpStreamOperation } from "./types.js";
|
|
4
|
+
//#region src/core/gorp/createGorpStream.d.ts
|
|
5
|
+
type GorpStreamController = {
|
|
6
|
+
readonly abortSignal: AbortSignal;
|
|
7
|
+
enqueue(operations: readonly GorpStreamOperation[]): void;
|
|
8
|
+
};
|
|
9
|
+
type CreateGorpStreamOptions = {
|
|
10
|
+
execute: (controller: GorpStreamController) => void | PromiseLike<void>;
|
|
11
|
+
defaultValue?: ReadonlyJSONValue;
|
|
12
|
+
};
|
|
13
|
+
declare const createGorpStream: ({ execute, defaultValue }: CreateGorpStreamOptions) => ReadableStream<GorpStreamChunk>;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { createGorpStream };
|
|
16
|
+
//# sourceMappingURL=createGorpStream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createGorpStream.d.ts","names":[],"sources":["../../../src/core/gorp/createGorpStream.ts"],"mappings":";;;;KAKK;WACM,aAAa;EAEtB,QAAQ,qBAAqB;;KA8D1B;EACH,UAAU,YAAY,gCAAgC;EACtD,eAAe;;cAGJ,qBAAoB,SAAA,gBAG9B,4BAAuB,eAAA"}
|
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { GorpStreamAccumulator } from "./GorpStreamAccumulator.js";
|
|
2
2
|
import { withPromiseOrValue } from "../utils/withPromiseOrValue.js";
|
|
3
|
-
//#region src/core/
|
|
4
|
-
var
|
|
3
|
+
//#region src/core/gorp/createGorpStream.ts
|
|
4
|
+
var GorpStreamControllerImpl = class {
|
|
5
5
|
_controller;
|
|
6
6
|
_abortController = new AbortController();
|
|
7
7
|
_accumulator;
|
|
8
|
+
_cancelled = false;
|
|
8
9
|
get abortSignal() {
|
|
9
10
|
return this._abortController.signal;
|
|
10
11
|
}
|
|
11
12
|
constructor(controller, defaultValue) {
|
|
12
13
|
this._controller = controller;
|
|
13
|
-
this._accumulator = new
|
|
14
|
+
this._accumulator = new GorpStreamAccumulator(defaultValue);
|
|
14
15
|
}
|
|
15
16
|
enqueue(operations) {
|
|
17
|
+
if (this._cancelled) return;
|
|
16
18
|
this._accumulator.append(operations);
|
|
17
19
|
this._controller.enqueue({
|
|
18
20
|
snapshot: this._accumulator.state,
|
|
@@ -20,12 +22,15 @@ var ObjectStreamControllerImpl = class {
|
|
|
20
22
|
});
|
|
21
23
|
}
|
|
22
24
|
__internalError(error) {
|
|
25
|
+
if (this._cancelled) return;
|
|
23
26
|
this._controller.error(error);
|
|
24
27
|
}
|
|
25
28
|
__internalClose() {
|
|
29
|
+
if (this._cancelled) return;
|
|
26
30
|
this._controller.close();
|
|
27
31
|
}
|
|
28
32
|
__internalCancel(reason) {
|
|
33
|
+
this._cancelled = true;
|
|
29
34
|
this._abortController.abort(reason);
|
|
30
35
|
}
|
|
31
36
|
};
|
|
@@ -33,14 +38,14 @@ const getStreamControllerPair = (defaultValue) => {
|
|
|
33
38
|
let controller;
|
|
34
39
|
return [new ReadableStream({
|
|
35
40
|
start(c) {
|
|
36
|
-
controller = new
|
|
41
|
+
controller = new GorpStreamControllerImpl(c, defaultValue);
|
|
37
42
|
},
|
|
38
43
|
cancel(reason) {
|
|
39
44
|
controller.__internalCancel(reason);
|
|
40
45
|
}
|
|
41
46
|
}), controller];
|
|
42
47
|
};
|
|
43
|
-
const
|
|
48
|
+
const createGorpStream = ({ execute, defaultValue = {} }) => {
|
|
44
49
|
const [stream, controller] = getStreamControllerPair(defaultValue);
|
|
45
50
|
withPromiseOrValue(() => execute(controller), () => {
|
|
46
51
|
controller.__internalClose();
|
|
@@ -50,6 +55,6 @@ const createObjectStream = ({ execute, defaultValue = {} }) => {
|
|
|
50
55
|
return stream;
|
|
51
56
|
};
|
|
52
57
|
//#endregion
|
|
53
|
-
export {
|
|
58
|
+
export { createGorpStream };
|
|
54
59
|
|
|
55
|
-
//# sourceMappingURL=
|
|
60
|
+
//# sourceMappingURL=createGorpStream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createGorpStream.js","names":[],"sources":["../../../src/core/gorp/createGorpStream.ts"],"sourcesContent":["import type { ReadonlyJSONValue } from \"../../utils\";\nimport { withPromiseOrValue } from \"../utils/withPromiseOrValue\";\nimport { GorpStreamAccumulator } from \"./GorpStreamAccumulator\";\nimport type { GorpStreamOperation, GorpStreamChunk } from \"./types\";\n\ntype GorpStreamController = {\n readonly abortSignal: AbortSignal;\n\n enqueue(operations: readonly GorpStreamOperation[]): void;\n};\n\nclass GorpStreamControllerImpl implements GorpStreamController {\n private _controller: ReadableStreamDefaultController<GorpStreamChunk>;\n private _abortController = new AbortController();\n private _accumulator: GorpStreamAccumulator;\n private _cancelled = false;\n\n get abortSignal() {\n return this._abortController.signal;\n }\n\n constructor(\n controller: ReadableStreamDefaultController<GorpStreamChunk>,\n defaultValue: ReadonlyJSONValue,\n ) {\n this._controller = controller;\n this._accumulator = new GorpStreamAccumulator(defaultValue);\n }\n\n enqueue(operations: readonly GorpStreamOperation[]) {\n if (this._cancelled) return;\n\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 if (this._cancelled) return;\n this._controller.error(error);\n }\n\n __internalClose() {\n if (this._cancelled) return;\n this._controller.close();\n }\n\n __internalCancel(reason?: unknown) {\n this._cancelled = true;\n this._abortController.abort(reason);\n }\n}\n\nconst getStreamControllerPair = (defaultValue: ReadonlyJSONValue) => {\n let controller!: GorpStreamControllerImpl;\n const stream = new ReadableStream<GorpStreamChunk>({\n start(c) {\n controller = new GorpStreamControllerImpl(c, defaultValue);\n },\n cancel(reason: unknown) {\n controller.__internalCancel(reason);\n },\n });\n\n return [stream, controller] as const;\n};\n\ntype CreateGorpStreamOptions = {\n execute: (controller: GorpStreamController) => void | PromiseLike<void>;\n defaultValue?: ReadonlyJSONValue;\n};\n\nexport const createGorpStream = ({\n execute,\n defaultValue = {},\n}: CreateGorpStreamOptions) => {\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,2BAAN,MAA+D;CAC7D;CACA,mBAA2B,IAAI,gBAAgB;CAC/C;CACA,aAAqB;CAErB,IAAI,cAAc;EAChB,OAAO,KAAK,iBAAiB;CAC/B;CAEA,YACE,YACA,cACA;EACA,KAAK,cAAc;EACnB,KAAK,eAAe,IAAI,sBAAsB,YAAY;CAC5D;CAEA,QAAQ,YAA4C;EAClD,IAAI,KAAK,YAAY;EAErB,KAAK,aAAa,OAAO,UAAU;EAEnC,KAAK,YAAY,QAAQ;GACvB,UAAU,KAAK,aAAa;GAC5B;EACF,CAAC;CACH;CAEA,gBAAgB,OAAgB;EAC9B,IAAI,KAAK,YAAY;EACrB,KAAK,YAAY,MAAM,KAAK;CAC9B;CAEA,kBAAkB;EAChB,IAAI,KAAK,YAAY;EACrB,KAAK,YAAY,MAAM;CACzB;CAEA,iBAAiB,QAAkB;EACjC,KAAK,aAAa;EAClB,KAAK,iBAAiB,MAAM,MAAM;CACpC;AACF;AAEA,MAAM,2BAA2B,iBAAoC;CACnE,IAAI;CAUJ,OAAO,CAAC,IATW,eAAgC;EACjD,MAAM,GAAG;GACP,aAAa,IAAI,yBAAyB,GAAG,YAAY;EAC3D;EACA,OAAO,QAAiB;GACtB,WAAW,iBAAiB,MAAM;EACpC;CACF,CAEa,GAAG,UAAU;AAC5B;AAOA,MAAa,oBAAoB,EAC/B,SACA,eAAe,CAAC,QACa;CAC7B,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,7 +1,7 @@
|
|
|
1
1
|
import { ReadonlyJSONValue } from "../../utils/json/json-value.js";
|
|
2
2
|
import "../../utils.js";
|
|
3
|
-
//#region src/core/
|
|
4
|
-
type
|
|
3
|
+
//#region src/core/gorp/types.d.ts
|
|
4
|
+
type AssistantTransportStateOperation = {
|
|
5
5
|
readonly type: "set";
|
|
6
6
|
readonly path: readonly string[];
|
|
7
7
|
readonly value: ReadonlyJSONValue;
|
|
@@ -10,10 +10,11 @@ type ObjectStreamOperation = {
|
|
|
10
10
|
readonly path: readonly string[];
|
|
11
11
|
readonly value: string;
|
|
12
12
|
};
|
|
13
|
-
type
|
|
13
|
+
type GorpStreamOperation = AssistantTransportStateOperation;
|
|
14
|
+
type GorpStreamChunk = {
|
|
14
15
|
readonly snapshot: ReadonlyJSONValue;
|
|
15
|
-
readonly operations: readonly
|
|
16
|
+
readonly operations: readonly GorpStreamOperation[];
|
|
16
17
|
};
|
|
17
18
|
//#endregion
|
|
18
|
-
export {
|
|
19
|
+
export { AssistantTransportStateOperation, GorpStreamChunk, GorpStreamOperation };
|
|
19
20
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/core/gorp/types.ts"],"mappings":";;;KAEY;WAEG;WACA;WACA,OAAO;;WAGP;WACA;WACA;;KAGH,sBAAsB;KAEtB;WACD,UAAU;WACV,qBAAqB"}
|
|
@@ -76,10 +76,11 @@ type AssistantStreamController = {
|
|
|
76
76
|
* Creates an {@link AssistantStream} and writes to it with an
|
|
77
77
|
* {@link AssistantStreamController}.
|
|
78
78
|
*
|
|
79
|
-
* The callback may write synchronously or asynchronously. If it throws
|
|
80
|
-
* `error` chunk is emitted
|
|
81
|
-
*
|
|
82
|
-
*
|
|
79
|
+
* The callback may write synchronously or asynchronously. If it throws while
|
|
80
|
+
* the stream is open, an `error` chunk is emitted. Failures after an explicit
|
|
81
|
+
* close are logged, while failures after consumer cancellation are discarded.
|
|
82
|
+
* When the callback settles, the stream is closed automatically unless the
|
|
83
|
+
* controller was already closed.
|
|
83
84
|
*/
|
|
84
85
|
declare function createAssistantStream(callback: (controller: AssistantStreamController) => PromiseLike<void> | void): AssistantStream;
|
|
85
86
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assistant-stream.d.ts","names":[],"sources":["../../../src/core/modules/assistant-stream.ts"],"mappings":";;;;;;;;KAuBK;EACH;EACA;EACA;EACA,OAAO;EACP,WAAW,iBAAiB;;;;;;;;;KAUlB;;EAEV,WAAW;;EAEX,gBAAgB;;EAEhB,aAAa,SAAS;;EAEtB,WAAW,SAAS;;EAEpB,WAAW,SAAS;;;;;;;;EAQpB,eAAe;;;;;;;EAOf,gBAAgB,kBAAkB;;;;;;;EAOlC,gBAAgB,SAAS,mBAAmB;;EAE5C,QAAQ,OAAO;;;;;;;EAOf,MAAM,QAAQ;;EAEd;;;;;;;EAOA,aAAa,mBAAmB
|
|
1
|
+
{"version":3,"file":"assistant-stream.d.ts","names":[],"sources":["../../../src/core/modules/assistant-stream.ts"],"mappings":";;;;;;;;KAuBK;EACH;EACA;EACA;EACA,OAAO;EACP,WAAW,iBAAiB;;;;;;;;;KAUlB;;EAEV,WAAW;;EAEX,gBAAgB;;EAEhB,aAAa,SAAS;;EAEtB,WAAW,SAAS;;EAEpB,WAAW,SAAS;;;;;;;;EAQpB,eAAe;;;;;;;EAOf,gBAAgB,kBAAkB;;;;;;;EAOlC,gBAAgB,SAAS,mBAAmB;;EAE5C,QAAQ,OAAO;;;;;;;EAOf,MAAM,QAAQ;;EAEd;;;;;;;EAOA,aAAa,mBAAmB;;;;;;;;;;;;iBAoNlB,sBACd,WAAW,YAAY,8BAA8B,2BACpD;;;;;;;;iBAkCa,6CAA+B,iBAAA;;;;;;;;iBAsB/B,8BACd,WAAW,YAAY,8BAA8B,2BAAwB"}
|
|
@@ -18,7 +18,10 @@ var AssistantStreamControllerImpl = class AssistantStreamControllerImpl {
|
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
20
|
get __internal_isClosed() {
|
|
21
|
-
return this._state.merger.isSealed();
|
|
21
|
+
return this._state.merger.isSealed() || this._state.merger.isCancelled() || this._state.merger.isErrored();
|
|
22
|
+
}
|
|
23
|
+
get __internal_isCancelled() {
|
|
24
|
+
return this._state.merger.isCancelled();
|
|
22
25
|
}
|
|
23
26
|
__internal_getReadable() {
|
|
24
27
|
return this._state.merger.readable;
|
|
@@ -133,10 +136,11 @@ var AssistantStreamControllerImpl = class AssistantStreamControllerImpl {
|
|
|
133
136
|
* Creates an {@link AssistantStream} and writes to it with an
|
|
134
137
|
* {@link AssistantStreamController}.
|
|
135
138
|
*
|
|
136
|
-
* The callback may write synchronously or asynchronously. If it throws
|
|
137
|
-
* `error` chunk is emitted
|
|
138
|
-
*
|
|
139
|
-
*
|
|
139
|
+
* The callback may write synchronously or asynchronously. If it throws while
|
|
140
|
+
* the stream is open, an `error` chunk is emitted. Failures after an explicit
|
|
141
|
+
* close are logged, while failures after consumer cancellation are discarded.
|
|
142
|
+
* When the callback settles, the stream is closed automatically unless the
|
|
143
|
+
* controller was already closed.
|
|
140
144
|
*/
|
|
141
145
|
function createAssistantStream(callback) {
|
|
142
146
|
const controller = new AssistantStreamControllerImpl();
|
|
@@ -149,7 +153,7 @@ function createAssistantStream(callback) {
|
|
|
149
153
|
path: [],
|
|
150
154
|
error: String(e)
|
|
151
155
|
});
|
|
152
|
-
|
|
156
|
+
else if (!controller.__internal_isCancelled) console.error(e);
|
|
153
157
|
} finally {
|
|
154
158
|
if (!controller.__internal_isClosed) controller.close();
|
|
155
159
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assistant-stream.js","names":[],"sources":["../../../src/core/modules/assistant-stream.ts"],"sourcesContent":["import { AssistantStream } from \"../AssistantStream\";\nimport type { AssistantStreamChunk, PartInit } from \"../AssistantStreamChunk\";\nimport { createMergeStream } from \"../utils/stream/merge\";\nimport { createTextStreamController, type TextStreamController } from \"./text\";\nimport {\n createToolCallStreamController,\n type ToolCallStreamController,\n} from \"./tool-call\";\nimport { Counter } from \"../utils/Counter\";\nimport {\n PathAppendEncoder,\n PathMergeEncoder,\n} from \"../utils/stream/path-utils\";\nimport { DataStreamEncoder } from \"../serialization/data-stream/DataStream\";\nimport type { DataPart, FilePart, SourcePart } from \"../utils/types\";\nimport { generateId } from \"../utils/generateId\";\nimport type {\n ReadonlyJSONObject,\n ReadonlyJSONValue,\n} from \"../../utils/json/json-value\";\nimport type { ToolResponseLike } from \"../tool/ToolResponse\";\nimport { promiseWithResolvers } from \"../../utils/promiseWithResolvers\";\n\ntype ToolCallPartInit = {\n toolCallId?: string;\n toolName: string;\n argsText?: string;\n args?: ReadonlyJSONObject;\n response?: ToolResponseLike<ReadonlyJSONValue>;\n};\n\n/**\n * Imperative writer for constructing an {@link AssistantStream}.\n *\n * The controller handles part boundaries for common streaming operations. Use\n * `appendText` and `appendReasoning` for simple token streams, or open explicit\n * parts with `addTextPart` and `addToolCallPart` when you need direct control.\n */\nexport type AssistantStreamController = {\n /** Appends text to the current text part, opening one if needed. */\n appendText(textDelta: string): void;\n /** Appends reasoning text to the current reasoning part, opening one if needed. */\n appendReasoning(reasoningDelta: string): void;\n /** Appends a source citation part to the stream. */\n appendSource(options: SourcePart): void;\n /** Appends a file part to the stream. */\n appendFile(options: FilePart): void;\n /** Appends a named data part to the stream. */\n appendData(options: DataPart): void;\n /**\n * Opens a text part and returns its writer.\n *\n * Close the returned controller when the text part is complete. Opening a new\n * part through this controller closes any implicit text or reasoning append\n * part first.\n */\n addTextPart(): TextStreamController;\n /**\n * Opens a tool-call part by tool name and returns its writer.\n *\n * A tool call id is generated automatically. Use the object overload when the\n * caller already has an id, initial args, args text, or response.\n */\n addToolCallPart(options: string): ToolCallStreamController;\n /**\n * Opens a tool-call part and returns its writer.\n *\n * Use this overload to provide a stable `toolCallId`, initial arguments,\n * streamed argument text, or an immediate {@link ToolResponseLike}.\n */\n addToolCallPart(options: ToolCallPartInit): ToolCallStreamController;\n /** Enqueues a raw protocol chunk. Prefer higher-level helpers when possible. */\n enqueue(chunk: AssistantStreamChunk): void;\n /**\n * Merges another assistant stream into this stream.\n *\n * Paths from the merged stream are remapped so its parts appear at the next\n * available position in this controller's output.\n */\n merge(stream: AssistantStream): void;\n /** Closes any active part and finishes the stream. */\n close(): void;\n /**\n * Returns a controller that writes child parts with `parentId` attached.\n *\n * Use this for nested or related parts that should be associated with an\n * existing message or part in downstream renderers.\n */\n withParentId(parentId: string): AssistantStreamController;\n};\n\n// Shared state between controller instances\ntype AssistantStreamControllerState = {\n merger: ReturnType<typeof createMergeStream>;\n append?:\n | {\n controller: TextStreamController;\n kind: \"text\" | \"reasoning\";\n parentId: string | undefined;\n }\n | undefined;\n contentCounter: Counter;\n closeSubscriber?: () => void;\n};\n\nclass AssistantStreamControllerImpl implements AssistantStreamController {\n private readonly _state: AssistantStreamControllerState;\n private _parentId?: string;\n\n constructor(state?: AssistantStreamControllerState) {\n this._state = state || {\n merger: createMergeStream(),\n contentCounter: new Counter(),\n };\n }\n\n get __internal_isClosed() {\n return this._state.merger.isSealed();\n }\n\n __internal_getReadable() {\n return this._state.merger.readable;\n }\n\n __internal_subscribeToClose(callback: () => void) {\n this._state.closeSubscriber = callback;\n }\n\n private _addPart(part: PartInit, stream: AssistantStream) {\n if (this._state.append) {\n this._state.append.controller.close();\n this._state.append = undefined;\n }\n\n this.enqueue({\n type: \"part-start\",\n part,\n path: [],\n });\n this._state.merger.addStream(\n stream.pipeThrough(\n new PathAppendEncoder(this._state.contentCounter.value),\n ),\n );\n }\n\n merge(stream: AssistantStream) {\n this._state.merger.addStream(\n stream.pipeThrough(new PathMergeEncoder(this._state.contentCounter)),\n );\n }\n\n appendText(textDelta: string) {\n if (\n this._state.append?.kind !== \"text\" ||\n this._state.append.parentId !== this._parentId\n ) {\n this._state.append = {\n kind: \"text\",\n parentId: this._parentId,\n controller: this.addTextPart(),\n };\n }\n this._state.append.controller.append(textDelta);\n }\n\n appendReasoning(textDelta: string) {\n if (\n this._state.append?.kind !== \"reasoning\" ||\n this._state.append.parentId !== this._parentId\n ) {\n this._state.append = {\n kind: \"reasoning\",\n parentId: this._parentId,\n controller: this.addReasoningPart(),\n };\n }\n this._state.append.controller.append(textDelta);\n }\n\n addTextPart() {\n const [stream, controller] = createTextStreamController();\n this._addPart(this._withParentIdOption({ type: \"text\" }), stream);\n return controller;\n }\n\n addReasoningPart() {\n const [stream, controller] = createTextStreamController();\n this._addPart(this._withParentIdOption({ type: \"reasoning\" }), stream);\n return controller;\n }\n\n addToolCallPart(\n options: string | ToolCallPartInit,\n ): ToolCallStreamController {\n const opt = typeof options === \"string\" ? { toolName: options } : options;\n const toolName = opt.toolName;\n const toolCallId = opt.toolCallId ?? generateId();\n\n const [stream, controller] = createToolCallStreamController();\n this._addPart(\n {\n type: \"tool-call\",\n toolName,\n toolCallId,\n ...(this._parentId && { parentId: this._parentId }),\n },\n stream,\n );\n\n if (opt.argsText !== undefined) {\n controller.argsText.append(opt.argsText);\n controller.argsText.close();\n }\n if (opt.args !== undefined) {\n controller.argsText.append(JSON.stringify(opt.args));\n controller.argsText.close();\n }\n if (opt.response !== undefined) {\n controller.setResponse(opt.response);\n }\n\n return controller;\n }\n\n private _finishedPartStream(): AssistantStream {\n return new ReadableStream({\n start(controller) {\n controller.enqueue({ type: \"part-finish\", path: [] });\n controller.close();\n },\n });\n }\n\n private _withParentIdOption<T>(options: T): T {\n if (!this._parentId) return options;\n return { ...options, parentId: this._parentId };\n }\n\n appendSource(options: SourcePart) {\n this._addPart(\n this._withParentIdOption(options),\n this._finishedPartStream(),\n );\n }\n\n appendFile(options: FilePart) {\n this._addPart(\n this._withParentIdOption(options),\n this._finishedPartStream(),\n );\n }\n\n appendData(options: DataPart) {\n this._addPart(\n this._withParentIdOption(options),\n this._finishedPartStream(),\n );\n }\n\n enqueue(chunk: AssistantStreamChunk) {\n this._state.merger.enqueue(chunk);\n\n if (chunk.type === \"part-start\" && chunk.path.length === 0) {\n this._state.contentCounter.up();\n }\n }\n\n withParentId(parentId: string): AssistantStreamController {\n const controller = new AssistantStreamControllerImpl(this._state);\n controller._parentId = parentId;\n return controller;\n }\n\n close() {\n this._state.append?.controller?.close();\n this._state.merger.seal();\n\n this._state.closeSubscriber?.();\n }\n}\n\n/**\n * Creates an {@link AssistantStream} and writes to it with an\n * {@link AssistantStreamController}.\n *\n * The callback may write synchronously or asynchronously. If it throws, an\n * `error` chunk is emitted before the error is rethrown; when the callback\n * settles, the stream is closed automatically unless the controller was\n * already closed.\n */\nexport function createAssistantStream(\n callback: (controller: AssistantStreamController) => PromiseLike<void> | void,\n): AssistantStream {\n const controller = new AssistantStreamControllerImpl();\n\n const runTask = async () => {\n try {\n await callback(controller);\n } catch (e) {\n if (!controller.__internal_isClosed) {\n controller.enqueue({\n type: \"error\",\n path: [],\n error: String(e),\n });\n }\n throw e;\n } finally {\n if (!controller.__internal_isClosed) {\n controller.close();\n }\n }\n };\n runTask();\n\n return controller.__internal_getReadable();\n}\n\n/**\n * Creates an {@link AssistantStream} together with the controller used to\n * write into it.\n *\n * Use this when the stream needs to be returned before all writers are known.\n * Closing the returned controller finishes the paired stream.\n */\nexport function createAssistantStreamController() {\n const { resolve, promise } = promiseWithResolvers<void>();\n let controller!: AssistantStreamController;\n const stream = createAssistantStream((c) => {\n controller = c;\n\n (controller as AssistantStreamControllerImpl).__internal_subscribeToClose(\n resolve,\n );\n\n return promise;\n });\n return [stream, controller] as const;\n}\n\n/**\n * Creates a `Response` whose body is an encoded {@link AssistantStream}.\n *\n * This is the HTTP-route convenience form of {@link createAssistantStream}; it\n * uses {@link DataStreamEncoder} so the response can be consumed by matching\n * assistant-ui data stream decoders.\n */\nexport function createAssistantStreamResponse(\n callback: (controller: AssistantStreamController) => PromiseLike<void> | void,\n) {\n return AssistantStream.toResponse(\n createAssistantStream(callback),\n new DataStreamEncoder(),\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"assistant-stream.js","names":[],"sources":["../../../src/core/modules/assistant-stream.ts"],"sourcesContent":["import { AssistantStream } from \"../AssistantStream\";\nimport type { AssistantStreamChunk, PartInit } from \"../AssistantStreamChunk\";\nimport { createMergeStream } from \"../utils/stream/merge\";\nimport { createTextStreamController, type TextStreamController } from \"./text\";\nimport {\n createToolCallStreamController,\n type ToolCallStreamController,\n} from \"./tool-call\";\nimport { Counter } from \"../utils/Counter\";\nimport {\n PathAppendEncoder,\n PathMergeEncoder,\n} from \"../utils/stream/path-utils\";\nimport { DataStreamEncoder } from \"../serialization/data-stream/DataStream\";\nimport type { DataPart, FilePart, SourcePart } from \"../utils/types\";\nimport { generateId } from \"../utils/generateId\";\nimport type {\n ReadonlyJSONObject,\n ReadonlyJSONValue,\n} from \"../../utils/json/json-value\";\nimport type { ToolResponseLike } from \"../tool/ToolResponse\";\nimport { promiseWithResolvers } from \"../../utils/promiseWithResolvers\";\n\ntype ToolCallPartInit = {\n toolCallId?: string;\n toolName: string;\n argsText?: string;\n args?: ReadonlyJSONObject;\n response?: ToolResponseLike<ReadonlyJSONValue>;\n};\n\n/**\n * Imperative writer for constructing an {@link AssistantStream}.\n *\n * The controller handles part boundaries for common streaming operations. Use\n * `appendText` and `appendReasoning` for simple token streams, or open explicit\n * parts with `addTextPart` and `addToolCallPart` when you need direct control.\n */\nexport type AssistantStreamController = {\n /** Appends text to the current text part, opening one if needed. */\n appendText(textDelta: string): void;\n /** Appends reasoning text to the current reasoning part, opening one if needed. */\n appendReasoning(reasoningDelta: string): void;\n /** Appends a source citation part to the stream. */\n appendSource(options: SourcePart): void;\n /** Appends a file part to the stream. */\n appendFile(options: FilePart): void;\n /** Appends a named data part to the stream. */\n appendData(options: DataPart): void;\n /**\n * Opens a text part and returns its writer.\n *\n * Close the returned controller when the text part is complete. Opening a new\n * part through this controller closes any implicit text or reasoning append\n * part first.\n */\n addTextPart(): TextStreamController;\n /**\n * Opens a tool-call part by tool name and returns its writer.\n *\n * A tool call id is generated automatically. Use the object overload when the\n * caller already has an id, initial args, args text, or response.\n */\n addToolCallPart(options: string): ToolCallStreamController;\n /**\n * Opens a tool-call part and returns its writer.\n *\n * Use this overload to provide a stable `toolCallId`, initial arguments,\n * streamed argument text, or an immediate {@link ToolResponseLike}.\n */\n addToolCallPart(options: ToolCallPartInit): ToolCallStreamController;\n /** Enqueues a raw protocol chunk. Prefer higher-level helpers when possible. */\n enqueue(chunk: AssistantStreamChunk): void;\n /**\n * Merges another assistant stream into this stream.\n *\n * Paths from the merged stream are remapped so its parts appear at the next\n * available position in this controller's output.\n */\n merge(stream: AssistantStream): void;\n /** Closes any active part and finishes the stream. */\n close(): void;\n /**\n * Returns a controller that writes child parts with `parentId` attached.\n *\n * Use this for nested or related parts that should be associated with an\n * existing message or part in downstream renderers.\n */\n withParentId(parentId: string): AssistantStreamController;\n};\n\n// Shared state between controller instances\ntype AssistantStreamControllerState = {\n merger: ReturnType<typeof createMergeStream>;\n append?:\n | {\n controller: TextStreamController;\n kind: \"text\" | \"reasoning\";\n parentId: string | undefined;\n }\n | undefined;\n contentCounter: Counter;\n closeSubscriber?: () => void;\n};\n\nclass AssistantStreamControllerImpl implements AssistantStreamController {\n private readonly _state: AssistantStreamControllerState;\n private _parentId?: string;\n\n constructor(state?: AssistantStreamControllerState) {\n this._state = state || {\n merger: createMergeStream(),\n contentCounter: new Counter(),\n };\n }\n\n get __internal_isClosed() {\n return (\n this._state.merger.isSealed() ||\n this._state.merger.isCancelled() ||\n this._state.merger.isErrored()\n );\n }\n\n get __internal_isCancelled() {\n return this._state.merger.isCancelled();\n }\n\n __internal_getReadable() {\n return this._state.merger.readable;\n }\n\n __internal_subscribeToClose(callback: () => void) {\n this._state.closeSubscriber = callback;\n }\n\n private _addPart(part: PartInit, stream: AssistantStream) {\n if (this._state.append) {\n this._state.append.controller.close();\n this._state.append = undefined;\n }\n\n this.enqueue({\n type: \"part-start\",\n part,\n path: [],\n });\n this._state.merger.addStream(\n stream.pipeThrough(\n new PathAppendEncoder(this._state.contentCounter.value),\n ),\n );\n }\n\n merge(stream: AssistantStream) {\n this._state.merger.addStream(\n stream.pipeThrough(new PathMergeEncoder(this._state.contentCounter)),\n );\n }\n\n appendText(textDelta: string) {\n if (\n this._state.append?.kind !== \"text\" ||\n this._state.append.parentId !== this._parentId\n ) {\n this._state.append = {\n kind: \"text\",\n parentId: this._parentId,\n controller: this.addTextPart(),\n };\n }\n this._state.append.controller.append(textDelta);\n }\n\n appendReasoning(textDelta: string) {\n if (\n this._state.append?.kind !== \"reasoning\" ||\n this._state.append.parentId !== this._parentId\n ) {\n this._state.append = {\n kind: \"reasoning\",\n parentId: this._parentId,\n controller: this.addReasoningPart(),\n };\n }\n this._state.append.controller.append(textDelta);\n }\n\n addTextPart() {\n const [stream, controller] = createTextStreamController();\n this._addPart(this._withParentIdOption({ type: \"text\" }), stream);\n return controller;\n }\n\n addReasoningPart() {\n const [stream, controller] = createTextStreamController();\n this._addPart(this._withParentIdOption({ type: \"reasoning\" }), stream);\n return controller;\n }\n\n addToolCallPart(\n options: string | ToolCallPartInit,\n ): ToolCallStreamController {\n const opt = typeof options === \"string\" ? { toolName: options } : options;\n const toolName = opt.toolName;\n const toolCallId = opt.toolCallId ?? generateId();\n\n const [stream, controller] = createToolCallStreamController();\n this._addPart(\n {\n type: \"tool-call\",\n toolName,\n toolCallId,\n ...(this._parentId && { parentId: this._parentId }),\n },\n stream,\n );\n\n if (opt.argsText !== undefined) {\n controller.argsText.append(opt.argsText);\n controller.argsText.close();\n }\n if (opt.args !== undefined) {\n controller.argsText.append(JSON.stringify(opt.args));\n controller.argsText.close();\n }\n if (opt.response !== undefined) {\n controller.setResponse(opt.response);\n }\n\n return controller;\n }\n\n private _finishedPartStream(): AssistantStream {\n return new ReadableStream({\n start(controller) {\n controller.enqueue({ type: \"part-finish\", path: [] });\n controller.close();\n },\n });\n }\n\n private _withParentIdOption<T>(options: T): T {\n if (!this._parentId) return options;\n return { ...options, parentId: this._parentId };\n }\n\n appendSource(options: SourcePart) {\n this._addPart(\n this._withParentIdOption(options),\n this._finishedPartStream(),\n );\n }\n\n appendFile(options: FilePart) {\n this._addPart(\n this._withParentIdOption(options),\n this._finishedPartStream(),\n );\n }\n\n appendData(options: DataPart) {\n this._addPart(\n this._withParentIdOption(options),\n this._finishedPartStream(),\n );\n }\n\n enqueue(chunk: AssistantStreamChunk) {\n this._state.merger.enqueue(chunk);\n\n if (chunk.type === \"part-start\" && chunk.path.length === 0) {\n this._state.contentCounter.up();\n }\n }\n\n withParentId(parentId: string): AssistantStreamController {\n const controller = new AssistantStreamControllerImpl(this._state);\n controller._parentId = parentId;\n return controller;\n }\n\n close() {\n this._state.append?.controller?.close();\n this._state.merger.seal();\n\n this._state.closeSubscriber?.();\n }\n}\n\n/**\n * Creates an {@link AssistantStream} and writes to it with an\n * {@link AssistantStreamController}.\n *\n * The callback may write synchronously or asynchronously. If it throws while\n * the stream is open, an `error` chunk is emitted. Failures after an explicit\n * close are logged, while failures after consumer cancellation are discarded.\n * When the callback settles, the stream is closed automatically unless the\n * controller was already closed.\n */\nexport function createAssistantStream(\n callback: (controller: AssistantStreamController) => PromiseLike<void> | void,\n): AssistantStream {\n const controller = new AssistantStreamControllerImpl();\n\n const runTask = async () => {\n try {\n await callback(controller);\n } catch (e) {\n if (!controller.__internal_isClosed) {\n controller.enqueue({\n type: \"error\",\n path: [],\n error: String(e),\n });\n } else if (!controller.__internal_isCancelled) {\n console.error(e);\n }\n } finally {\n if (!controller.__internal_isClosed) {\n controller.close();\n }\n }\n };\n runTask();\n\n return controller.__internal_getReadable();\n}\n\n/**\n * Creates an {@link AssistantStream} together with the controller used to\n * write into it.\n *\n * Use this when the stream needs to be returned before all writers are known.\n * Closing the returned controller finishes the paired stream.\n */\nexport function createAssistantStreamController() {\n const { resolve, promise } = promiseWithResolvers<void>();\n let controller!: AssistantStreamController;\n const stream = createAssistantStream((c) => {\n controller = c;\n\n (controller as AssistantStreamControllerImpl).__internal_subscribeToClose(\n resolve,\n );\n\n return promise;\n });\n return [stream, controller] as const;\n}\n\n/**\n * Creates a `Response` whose body is an encoded {@link AssistantStream}.\n *\n * This is the HTTP-route convenience form of {@link createAssistantStream}; it\n * uses {@link DataStreamEncoder} so the response can be consumed by matching\n * assistant-ui data stream decoders.\n */\nexport function createAssistantStreamResponse(\n callback: (controller: AssistantStreamController) => PromiseLike<void> | void,\n) {\n return AssistantStream.toResponse(\n createAssistantStream(callback),\n new DataStreamEncoder(),\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-call.d.ts","names":[],"sources":["../../../src/core/modules/tool-call.ts"],"mappings":";;;;;;KAOY;EACV,UAAU;EAEV,YAAY,UAAU,iBAAiB;EACvC;;
|
|
1
|
+
{"version":3,"file":"tool-call.d.ts","names":[],"sources":["../../../src/core/modules/tool-call.ts"],"mappings":";;;;;;KAOY;EACV,UAAU;EAEV,YAAY,UAAU,iBAAiB;EACvC;;cA8FW,uBACX,UAAU,mBAAmB,8BAC5B;cAcU,gDAA8B,iBAAA"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createTextStream } from "./text.js";
|
|
2
2
|
//#region src/core/modules/tool-call.ts
|
|
3
3
|
var ToolCallStreamControllerImpl = class {
|
|
4
|
-
_controller;
|
|
5
4
|
_isClosed = false;
|
|
6
5
|
_mergeTask;
|
|
6
|
+
_controller;
|
|
7
7
|
constructor(_controller) {
|
|
8
8
|
this._controller = _controller;
|
|
9
9
|
const stream = createTextStream({ start: (c) => {
|
|
@@ -36,8 +36,6 @@ var ToolCallStreamControllerImpl = class {
|
|
|
36
36
|
}
|
|
37
37
|
_argsTextController;
|
|
38
38
|
async setResponse(response) {
|
|
39
|
-
this._argsTextController.close();
|
|
40
|
-
await Promise.resolve();
|
|
41
39
|
this._controller.enqueue({
|
|
42
40
|
type: "result",
|
|
43
41
|
path: [],
|
|
@@ -47,6 +45,8 @@ var ToolCallStreamControllerImpl = class {
|
|
|
47
45
|
...response.modelContent !== void 0 ? { modelContent: response.modelContent } : {},
|
|
48
46
|
...response.messages !== void 0 ? { messages: response.messages } : {}
|
|
49
47
|
});
|
|
48
|
+
this._argsTextController.close();
|
|
49
|
+
await Promise.resolve();
|
|
50
50
|
}
|
|
51
51
|
async close() {
|
|
52
52
|
if (this._isClosed) return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-call.js","names":[],"sources":["../../../src/core/modules/tool-call.ts"],"sourcesContent":["import type { AssistantStream } from \"../AssistantStream\";\nimport type { AssistantStreamChunk } from \"../AssistantStreamChunk\";\nimport type { ToolResponseLike } from \"../tool/ToolResponse\";\nimport type { ReadonlyJSONValue } from \"../../utils/json/json-value\";\nimport type { UnderlyingReadable } from \"../utils/stream/UnderlyingReadable\";\nimport { createTextStream, type TextStreamController } from \"./text\";\n\nexport type ToolCallStreamController = {\n argsText: TextStreamController;\n\n setResponse(response: ToolResponseLike<ReadonlyJSONValue>): void;\n close(): void;\n};\n\nclass ToolCallStreamControllerImpl implements ToolCallStreamController {\n private _isClosed = false;\n\n private _mergeTask: Promise<void>;\n constructor(\n
|
|
1
|
+
{"version":3,"file":"tool-call.js","names":[],"sources":["../../../src/core/modules/tool-call.ts"],"sourcesContent":["import type { AssistantStream } from \"../AssistantStream\";\nimport type { AssistantStreamChunk } from \"../AssistantStreamChunk\";\nimport type { ToolResponseLike } from \"../tool/ToolResponse\";\nimport type { ReadonlyJSONValue } from \"../../utils/json/json-value\";\nimport type { UnderlyingReadable } from \"../utils/stream/UnderlyingReadable\";\nimport { createTextStream, type TextStreamController } from \"./text\";\n\nexport type ToolCallStreamController = {\n argsText: TextStreamController;\n\n setResponse(response: ToolResponseLike<ReadonlyJSONValue>): void;\n close(): void;\n};\n\nclass ToolCallStreamControllerImpl implements ToolCallStreamController {\n private _isClosed = false;\n\n private _mergeTask: Promise<void>;\n private _controller: ReadableStreamDefaultController<AssistantStreamChunk>;\n\n constructor(\n _controller: ReadableStreamDefaultController<AssistantStreamChunk>,\n ) {\n this._controller = _controller;\n const stream = createTextStream({\n start: (c) => {\n this._argsTextController = c;\n },\n });\n\n let hasArgsText = false;\n this._mergeTask = stream.pipeTo(\n new WritableStream({\n write: (chunk) => {\n switch (chunk.type) {\n case \"text-delta\":\n hasArgsText = true;\n this._controller.enqueue(chunk);\n break;\n\n case \"part-finish\":\n if (!hasArgsText) {\n // if no argsText was provided, assume empty object\n this._controller.enqueue({\n type: \"text-delta\",\n textDelta: \"{}\",\n path: [],\n });\n }\n this._controller.enqueue({\n type: \"tool-call-args-text-finish\",\n path: [],\n });\n break;\n\n default:\n throw new Error(`Unexpected chunk type: ${chunk.type}`);\n }\n },\n }),\n );\n }\n\n get argsText() {\n return this._argsTextController;\n }\n\n private _argsTextController!: TextStreamController;\n\n async setResponse(response: ToolResponseLike<ReadonlyJSONValue>) {\n this._controller.enqueue({\n type: \"result\",\n path: [],\n ...(response.artifact !== undefined\n ? { artifact: response.artifact }\n : {}),\n result: response.result,\n isError: response.isError ?? false,\n ...(response.modelContent !== undefined\n ? { modelContent: response.modelContent }\n : {}),\n ...(response.messages !== undefined\n ? { messages: response.messages }\n : {}),\n });\n this._argsTextController.close();\n await Promise.resolve(); // flush microtask queue\n // TODO switch argsTextController to be something that doesn'#t require this\n }\n\n async close() {\n if (this._isClosed) return;\n\n this._isClosed = true;\n this._argsTextController.close();\n await this._mergeTask;\n\n this._controller.enqueue({\n type: \"part-finish\",\n path: [],\n });\n this._controller.close();\n }\n}\n\nexport const createToolCallStream = (\n readable: UnderlyingReadable<ToolCallStreamController>,\n): AssistantStream => {\n return new ReadableStream({\n start(c) {\n return readable.start?.(new ToolCallStreamControllerImpl(c));\n },\n pull(c) {\n return readable.pull?.(new ToolCallStreamControllerImpl(c));\n },\n cancel(c) {\n return readable.cancel?.(c);\n },\n });\n};\n\nexport const createToolCallStreamController = () => {\n let controller!: ToolCallStreamController;\n const stream = createToolCallStream({\n start(c) {\n controller = c;\n },\n });\n return [stream, controller] as const;\n};\n"],"mappings":";;AAcA,IAAM,+BAAN,MAAuE;CACrE,YAAoB;CAEpB;CACA;CAEA,YACE,aACA;EACA,KAAK,cAAc;EACnB,MAAM,SAAS,iBAAiB,EAC9B,QAAQ,MAAM;GACZ,KAAK,sBAAsB;EAC7B,EACF,CAAC;EAED,IAAI,cAAc;EAClB,KAAK,aAAa,OAAO,OACvB,IAAI,eAAe,EACjB,QAAQ,UAAU;GAChB,QAAQ,MAAM,MAAd;IACE,KAAK;KACH,cAAc;KACd,KAAK,YAAY,QAAQ,KAAK;KAC9B;IAEF,KAAK;KACH,IAAI,CAAC,aAEH,KAAK,YAAY,QAAQ;MACvB,MAAM;MACN,WAAW;MACX,MAAM,CAAC;KACT,CAAC;KAEH,KAAK,YAAY,QAAQ;MACvB,MAAM;MACN,MAAM,CAAC;KACT,CAAC;KACD;IAEF,SACE,MAAM,IAAI,MAAM,0BAA0B,MAAM,MAAM;GAC1D;EACF,EACF,CAAC,CACH;CACF;CAEA,IAAI,WAAW;EACb,OAAO,KAAK;CACd;CAEA;CAEA,MAAM,YAAY,UAA+C;EAC/D,KAAK,YAAY,QAAQ;GACvB,MAAM;GACN,MAAM,CAAC;GACP,GAAI,SAAS,aAAa,KAAA,IACtB,EAAE,UAAU,SAAS,SAAS,IAC9B,CAAC;GACL,QAAQ,SAAS;GACjB,SAAS,SAAS,WAAW;GAC7B,GAAI,SAAS,iBAAiB,KAAA,IAC1B,EAAE,cAAc,SAAS,aAAa,IACtC,CAAC;GACL,GAAI,SAAS,aAAa,KAAA,IACtB,EAAE,UAAU,SAAS,SAAS,IAC9B,CAAC;EACP,CAAC;EACD,KAAK,oBAAoB,MAAM;EAC/B,MAAM,QAAQ,QAAQ;CAExB;CAEA,MAAM,QAAQ;EACZ,IAAI,KAAK,WAAW;EAEpB,KAAK,YAAY;EACjB,KAAK,oBAAoB,MAAM;EAC/B,MAAM,KAAK;EAEX,KAAK,YAAY,QAAQ;GACvB,MAAM;GACN,MAAM,CAAC;EACT,CAAC;EACD,KAAK,YAAY,MAAM;CACzB;AACF;AAEA,MAAa,wBACX,aACoB;CACpB,OAAO,IAAI,eAAe;EACxB,MAAM,GAAG;GACP,OAAO,SAAS,QAAQ,IAAI,6BAA6B,CAAC,CAAC;EAC7D;EACA,KAAK,GAAG;GACN,OAAO,SAAS,OAAO,IAAI,6BAA6B,CAAC,CAAC;EAC5D;EACA,OAAO,GAAG;GACR,OAAO,SAAS,SAAS,CAAC;EAC5B;CACF,CAAC;AACH;AAEA,MAAa,uCAAuC;CAClD,IAAI;CAMJ,OAAO,CALQ,qBAAqB,EAClC,MAAM,GAAG;EACP,aAAa;CACf,EACF,CACa,GAAG,UAAU;AAC5B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssistantTransport.d.ts","names":[],"sources":["../../../../src/core/serialization/assistant-transport/AssistantTransport.ts"],"mappings":";;;;;;;;
|
|
1
|
+
{"version":3,"file":"AssistantTransport.d.ts","names":[],"sources":["../../../../src/core/serialization/assistant-transport/AssistantTransport.ts"],"mappings":";;;;;;;;cA6Ea,kCACH,wBAAwB,sBAAsB,WAAW,yBACtD;EAEX,SAAO;;;;;;;cA4BI,kCAAkC,wBAC7C,WAAW,cACX"}
|
|
@@ -1,6 +1,87 @@
|
|
|
1
1
|
import { PipeableTransformStream } from "../../utils/stream/PipeableTransformStream.js";
|
|
2
2
|
import { SSEEventDecoderStream } from "../../utils/stream/SSEEventDecoderStream.js";
|
|
3
|
+
import sjson from "secure-json-parse";
|
|
3
4
|
//#region src/core/serialization/assistant-transport/AssistantTransport.ts
|
|
5
|
+
const noFields = () => true;
|
|
6
|
+
const requiredObject = (key) => (c) => {
|
|
7
|
+
const value = c[key];
|
|
8
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9
|
+
};
|
|
10
|
+
const requiredString = (key) => (c) => typeof c[key] === "string";
|
|
11
|
+
const requiredArray = (key) => (c) => Array.isArray(c[key]);
|
|
12
|
+
const optionalBoolean = (key) => (c) => c[key] === void 0 || typeof c[key] === "boolean";
|
|
13
|
+
const KNOWN_CHUNK_TYPES = {
|
|
14
|
+
"part-start": {
|
|
15
|
+
kind: "message",
|
|
16
|
+
valid: requiredObject("part")
|
|
17
|
+
},
|
|
18
|
+
"part-finish": {
|
|
19
|
+
kind: "part-addressed",
|
|
20
|
+
valid: noFields
|
|
21
|
+
},
|
|
22
|
+
"tool-call-args-text-finish": {
|
|
23
|
+
kind: "part-addressed",
|
|
24
|
+
valid: noFields
|
|
25
|
+
},
|
|
26
|
+
"text-delta": {
|
|
27
|
+
kind: "part-addressed",
|
|
28
|
+
valid: requiredString("textDelta")
|
|
29
|
+
},
|
|
30
|
+
annotations: {
|
|
31
|
+
kind: "message",
|
|
32
|
+
valid: requiredArray("annotations")
|
|
33
|
+
},
|
|
34
|
+
data: {
|
|
35
|
+
kind: "message",
|
|
36
|
+
valid: requiredArray("data")
|
|
37
|
+
},
|
|
38
|
+
"step-start": {
|
|
39
|
+
kind: "message",
|
|
40
|
+
valid: noFields
|
|
41
|
+
},
|
|
42
|
+
"step-finish": {
|
|
43
|
+
kind: "message",
|
|
44
|
+
valid: requiredString("finishReason")
|
|
45
|
+
},
|
|
46
|
+
"message-finish": {
|
|
47
|
+
kind: "message",
|
|
48
|
+
valid: requiredString("finishReason")
|
|
49
|
+
},
|
|
50
|
+
result: {
|
|
51
|
+
kind: "part-addressed",
|
|
52
|
+
valid: optionalBoolean("isError")
|
|
53
|
+
},
|
|
54
|
+
error: {
|
|
55
|
+
kind: "message",
|
|
56
|
+
valid: noFields
|
|
57
|
+
},
|
|
58
|
+
"update-state": {
|
|
59
|
+
kind: "message",
|
|
60
|
+
valid: requiredArray("operations")
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
const parseChunk = (data) => {
|
|
64
|
+
let value;
|
|
65
|
+
try {
|
|
66
|
+
value = sjson.parse(data);
|
|
67
|
+
} catch {
|
|
68
|
+
return "unparseable";
|
|
69
|
+
}
|
|
70
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return "not-an-object";
|
|
71
|
+
const { type, path } = value;
|
|
72
|
+
if (typeof type !== "string" || !Object.prototype.hasOwnProperty.call(KNOWN_CHUNK_TYPES, type)) return "unknown-type";
|
|
73
|
+
const rule = KNOWN_CHUNK_TYPES[type];
|
|
74
|
+
if (!rule.valid(value)) return `invalid-fields:${type}`;
|
|
75
|
+
if (path === void 0) {
|
|
76
|
+
if (rule.kind !== "message") return `missing-path:${type}`;
|
|
77
|
+
return {
|
|
78
|
+
...value,
|
|
79
|
+
path: []
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
if (!Array.isArray(path) || !path.every((entry) => Number.isInteger(entry) && entry >= 0)) return "invalid-path";
|
|
83
|
+
return value;
|
|
84
|
+
};
|
|
4
85
|
/**
|
|
5
86
|
* AssistantTransportEncoder encodes AssistantStreamChunks into SSE format
|
|
6
87
|
* and emits [DONE] when the stream completes.
|
|
@@ -32,6 +113,7 @@ var AssistantTransportDecoder = class extends PipeableTransformStream {
|
|
|
32
113
|
constructor() {
|
|
33
114
|
super((readable) => {
|
|
34
115
|
let receivedDone = false;
|
|
116
|
+
const warnedReasons = /* @__PURE__ */ new Set();
|
|
35
117
|
return readable.pipeThrough(new TextDecoderStream()).pipeThrough(new SSEEventDecoderStream()).pipeThrough(new TransformStream({
|
|
36
118
|
transform(event, controller) {
|
|
37
119
|
switch (event.event) {
|
|
@@ -39,7 +121,15 @@ var AssistantTransportDecoder = class extends PipeableTransformStream {
|
|
|
39
121
|
if (event.data === "[DONE]") {
|
|
40
122
|
receivedDone = true;
|
|
41
123
|
controller.terminate();
|
|
42
|
-
} else
|
|
124
|
+
} else {
|
|
125
|
+
const chunk = parseChunk(event.data);
|
|
126
|
+
if (typeof chunk === "string") {
|
|
127
|
+
if (!warnedReasons.has(chunk)) {
|
|
128
|
+
warnedReasons.add(chunk);
|
|
129
|
+
console.warn(`Dropped invalid assistant-transport chunk (${chunk}): ${event.data.slice(0, 200)}`);
|
|
130
|
+
}
|
|
131
|
+
} else controller.enqueue(chunk);
|
|
132
|
+
}
|
|
43
133
|
break;
|
|
44
134
|
default: throw new Error(`Unknown SSE event type: ${event.event}`);
|
|
45
135
|
}
|