assistant-stream 0.3.25 → 0.3.26
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/README.md +4 -1
- package/dist/core/AssistantStream.d.ts +0 -1
- package/dist/core/AssistantStream.d.ts.map +1 -1
- package/dist/core/AssistantStreamChunk.d.ts +24 -12
- package/dist/core/AssistantStreamChunk.d.ts.map +1 -1
- package/dist/core/accumulators/AssistantMessageStream.d.ts +0 -1
- package/dist/core/accumulators/AssistantMessageStream.d.ts.map +1 -1
- package/dist/core/accumulators/TimingTracker.d.ts +0 -1
- package/dist/core/accumulators/TimingTracker.d.ts.map +1 -1
- package/dist/core/accumulators/assistant-message-accumulator.d.ts +3 -9
- package/dist/core/accumulators/assistant-message-accumulator.d.ts.map +1 -1
- package/dist/core/accumulators/assistant-message-accumulator.js +6 -1
- package/dist/core/accumulators/assistant-message-accumulator.js.map +1 -1
- package/dist/core/converters/toGenericMessages.d.ts.map +1 -1
- package/dist/core/converters/toGenericMessages.js +2 -1
- package/dist/core/converters/toGenericMessages.js.map +1 -1
- package/dist/core/modules/assistant-stream.d.ts +13 -7
- package/dist/core/modules/assistant-stream.d.ts.map +1 -1
- package/dist/core/modules/text.d.ts +0 -1
- package/dist/core/modules/text.d.ts.map +1 -1
- package/dist/core/modules/tool-call.d.ts +0 -1
- package/dist/core/modules/tool-call.d.ts.map +1 -1
- package/dist/core/object/ObjectStreamAccumulator.d.ts +1 -1
- package/dist/core/object/ObjectStreamAccumulator.d.ts.map +1 -1
- package/dist/core/object/ObjectStreamResponse.d.ts +0 -1
- package/dist/core/object/ObjectStreamResponse.d.ts.map +1 -1
- package/dist/core/object/ObjectStreamResponse.js +1 -1
- package/dist/core/object/ObjectStreamResponse.js.map +1 -1
- package/dist/core/object/createObjectStream.d.ts +2 -5
- package/dist/core/object/createObjectStream.d.ts.map +1 -1
- package/dist/core/object/types.d.ts +1 -0
- package/dist/core/object/types.d.ts.map +1 -1
- package/dist/core/serialization/PlainText.d.ts +0 -1
- package/dist/core/serialization/PlainText.d.ts.map +1 -1
- package/dist/core/serialization/assistant-transport/AssistantTransport.d.ts +0 -1
- package/dist/core/serialization/assistant-transport/AssistantTransport.d.ts.map +1 -1
- package/dist/core/serialization/assistant-transport/AssistantTransport.js +2 -52
- package/dist/core/serialization/assistant-transport/AssistantTransport.js.map +1 -1
- package/dist/core/serialization/data-stream/DataStream.d.ts +0 -1
- package/dist/core/serialization/data-stream/DataStream.d.ts.map +1 -1
- package/dist/core/serialization/data-stream/chunk-types.d.ts +1 -2
- package/dist/core/serialization/data-stream/chunk-types.d.ts.map +1 -1
- package/dist/core/serialization/data-stream/serialization.d.ts +0 -1
- package/dist/core/serialization/data-stream/serialization.d.ts.map +1 -1
- package/dist/core/serialization/ui-message-stream/UIMessageStream.d.ts +0 -1
- package/dist/core/serialization/ui-message-stream/UIMessageStream.d.ts.map +1 -1
- package/dist/core/serialization/ui-message-stream/UIMessageStream.js +66 -53
- package/dist/core/serialization/ui-message-stream/UIMessageStream.js.map +1 -1
- package/dist/core/serialization/ui-message-stream/chunk-types.d.ts +0 -1
- package/dist/core/serialization/ui-message-stream/chunk-types.d.ts.map +1 -1
- package/dist/core/tool/ToolCallReader.d.ts +1 -0
- package/dist/core/tool/ToolCallReader.d.ts.map +1 -1
- package/dist/core/tool/ToolExecutionStream.d.ts +0 -1
- package/dist/core/tool/ToolExecutionStream.d.ts.map +1 -1
- package/dist/core/tool/ToolResponse.d.ts +6 -4
- package/dist/core/tool/ToolResponse.d.ts.map +1 -1
- package/dist/core/tool/schema-utils.d.ts +0 -1
- package/dist/core/tool/schema-utils.d.ts.map +1 -1
- package/dist/core/tool/tool-types.d.ts +71 -33
- package/dist/core/tool/tool-types.d.ts.map +1 -1
- package/dist/core/tool/toolResultStream.d.ts +3 -2
- package/dist/core/tool/toolResultStream.d.ts.map +1 -1
- package/dist/core/tool/type-path-utils.d.ts +2 -2
- package/dist/core/tool/type-path-utils.d.ts.map +1 -1
- package/dist/core/utils/Counter.d.ts.map +1 -1
- package/dist/core/utils/generateId.d.ts.map +1 -1
- package/dist/core/utils/stream/AssistantMetaTransformStream.d.ts +0 -1
- package/dist/core/utils/stream/AssistantMetaTransformStream.d.ts.map +1 -1
- package/dist/core/utils/stream/AssistantTransformStream.d.ts +0 -1
- package/dist/core/utils/stream/AssistantTransformStream.d.ts.map +1 -1
- package/dist/core/utils/stream/LineDecoderStream.d.ts +1 -0
- package/dist/core/utils/stream/LineDecoderStream.d.ts.map +1 -1
- package/dist/core/utils/stream/LineDecoderStream.js +19 -6
- package/dist/core/utils/stream/LineDecoderStream.js.map +1 -1
- package/dist/core/utils/stream/PipeableTransformStream.d.ts.map +1 -1
- package/dist/core/utils/stream/SSE.d.ts +0 -1
- package/dist/core/utils/stream/SSE.d.ts.map +1 -1
- package/dist/core/utils/stream/SSE.js +2 -52
- package/dist/core/utils/stream/SSE.js.map +1 -1
- package/dist/core/utils/stream/SSEEventDecoder.d.ts +27 -0
- package/dist/core/utils/stream/SSEEventDecoder.d.ts.map +1 -0
- package/dist/core/utils/stream/SSEEventDecoder.js +84 -0
- package/dist/core/utils/stream/SSEEventDecoder.js.map +1 -0
- package/dist/core/utils/stream/SSEEventDecoderStream.d.ts +13 -0
- package/dist/core/utils/stream/SSEEventDecoderStream.d.ts.map +1 -0
- package/dist/core/utils/stream/SSEEventDecoderStream.js +24 -0
- package/dist/core/utils/stream/SSEEventDecoderStream.js.map +1 -0
- package/dist/core/utils/stream/UnderlyingReadable.d.ts.map +1 -1
- package/dist/core/utils/stream/merge.d.ts +0 -1
- package/dist/core/utils/stream/merge.d.ts.map +1 -1
- package/dist/core/utils/stream/path-utils.d.ts +0 -1
- package/dist/core/utils/stream/path-utils.d.ts.map +1 -1
- package/dist/core/utils/types.d.ts +16 -8
- package/dist/core/utils/types.d.ts.map +1 -1
- package/dist/core/utils/withPromiseOrValue.d.ts.map +1 -1
- package/dist/node_modules/.pnpm/@types_json-schema@7.0.15/node_modules/@types/json-schema/index.d.ts +3 -3
- package/dist/node_modules/.pnpm/@types_json-schema@7.0.15/node_modules/@types/json-schema/index.d.ts.map +1 -1
- package/dist/resumable/ResumableStreamContext.d.ts +0 -1
- package/dist/resumable/ResumableStreamContext.d.ts.map +1 -1
- package/dist/resumable/constants.d.ts.map +1 -1
- package/dist/resumable/createResumableAssistantStreamResponse.d.ts +6 -4
- package/dist/resumable/createResumableAssistantStreamResponse.d.ts.map +1 -1
- package/dist/resumable/errors.d.ts.map +1 -1
- package/dist/resumable/stores/InMemoryResumableStreamStore.d.ts +0 -1
- package/dist/resumable/stores/InMemoryResumableStreamStore.d.ts.map +1 -1
- package/dist/resumable/stores/InMemoryResumableStreamStore.js +2 -1
- package/dist/resumable/stores/InMemoryResumableStreamStore.js.map +1 -1
- package/dist/resumable/stores/ioredis.d.ts +0 -1
- package/dist/resumable/stores/ioredis.d.ts.map +1 -1
- package/dist/resumable/stores/redis-impl.d.ts +2 -2
- package/dist/resumable/stores/redis-impl.d.ts.map +1 -1
- package/dist/resumable/stores/redis.d.ts +0 -1
- package/dist/resumable/stores/redis.d.ts.map +1 -1
- package/dist/resumable/types.d.ts.map +1 -1
- package/dist/utils/AsyncIterableStream.d.ts.map +1 -1
- package/dist/utils/json/fix-json.d.ts.map +1 -1
- package/dist/utils/json/json-value.d.ts.map +1 -1
- package/dist/utils/json/parse-partial-json-object.d.ts +0 -1
- package/dist/utils/json/parse-partial-json-object.d.ts.map +1 -1
- package/dist/utils/promiseWithResolvers.d.ts.map +1 -1
- package/dist/utils.d.ts +2 -1
- package/dist/utils.js +2 -1
- package/package.json +5 -5
- package/src/core/AssistantStreamChunk.ts +2 -0
- package/src/core/accumulators/assistant-message-accumulator.test.ts +63 -0
- package/src/core/accumulators/assistant-message-accumulator.ts +15 -1
- package/src/core/object/ObjectStream.test.ts +20 -0
- package/src/core/object/ObjectStreamResponse.test.ts +32 -0
- package/src/core/object/ObjectStreamResponse.ts +6 -1
- package/src/core/serialization/assistant-transport/AssistantTransport.test.ts +41 -0
- package/src/core/serialization/assistant-transport/AssistantTransport.ts +6 -70
- package/src/core/serialization/ui-message-stream/UIMessageStream.test.ts +212 -4
- package/src/core/serialization/ui-message-stream/UIMessageStream.ts +62 -71
- package/src/core/utils/stream/LineDecoderStream.test.ts +26 -2
- package/src/core/utils/stream/LineDecoderStream.ts +21 -11
- package/src/core/utils/stream/SSE.ts +6 -70
- package/src/core/utils/stream/SSEEventDecoder.test.ts +146 -0
- package/src/core/utils/stream/SSEEventDecoder.ts +109 -0
- package/src/core/utils/stream/SSEEventDecoderStream.test.ts +42 -0
- package/src/core/utils/stream/SSEEventDecoderStream.ts +34 -0
- package/src/utils.ts +4 -0
- package/dist/utils/json/is-json.d.ts +0 -9
- package/dist/utils/json/is-json.d.ts.map +0 -1
- package/dist/utils/json/is-json.js +0 -17
- package/dist/utils/json/is-json.js.map +0 -1
- package/src/utils/json/is-json.ts +0 -43
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { SSEEventDecoder } from "./SSEEventDecoder.js";
|
|
2
|
+
//#region src/core/utils/stream/SSEEventDecoderStream.ts
|
|
3
|
+
var SSEEventDecoderStream = class extends TransformStream {
|
|
4
|
+
constructor() {
|
|
5
|
+
const decoder = new SSEEventDecoder({ trailing: "drop" });
|
|
6
|
+
const normalize = (event) => ({
|
|
7
|
+
...event,
|
|
8
|
+
event: event.event || "message"
|
|
9
|
+
});
|
|
10
|
+
super({
|
|
11
|
+
transform(chunk, controller) {
|
|
12
|
+
for (const event of decoder.push(chunk)) controller.enqueue(normalize(event));
|
|
13
|
+
},
|
|
14
|
+
flush(controller) {
|
|
15
|
+
const event = decoder.flush();
|
|
16
|
+
if (event) controller.enqueue(normalize(event));
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
//#endregion
|
|
22
|
+
export { SSEEventDecoderStream };
|
|
23
|
+
|
|
24
|
+
//# sourceMappingURL=SSEEventDecoderStream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SSEEventDecoderStream.js","names":[],"sources":["../../../../src/core/utils/stream/SSEEventDecoderStream.ts"],"sourcesContent":["import { SSEEventDecoder } from \"./SSEEventDecoder\";\nimport type { SSEEvent } from \"./SSEEventDecoder\";\n\nexport type PipelineSSEEvent = {\n event: string;\n data: string;\n id?: string;\n retry?: number;\n};\n\nexport class SSEEventDecoderStream extends TransformStream<\n string,\n PipelineSSEEvent\n> {\n constructor() {\n const decoder = new SSEEventDecoder({ trailing: \"drop\" });\n const normalize = (event: SSEEvent): PipelineSSEEvent => ({\n ...event,\n event: event.event || \"message\",\n });\n\n super({\n transform(chunk, controller) {\n for (const event of decoder.push(chunk)) {\n controller.enqueue(normalize(event));\n }\n },\n flush(controller) {\n const event = decoder.flush();\n if (event) controller.enqueue(normalize(event));\n },\n });\n }\n}\n"],"mappings":";;AAUA,IAAa,wBAAb,cAA2C,gBAGzC;CACA,cAAc;EACZ,MAAM,UAAU,IAAI,gBAAgB,EAAE,UAAU,OAAO,CAAC;EACxD,MAAM,aAAa,WAAuC;GACxD,GAAG;GACH,OAAO,MAAM,SAAS;EACxB;EAEA,MAAM;GACJ,UAAU,OAAO,YAAY;IAC3B,KAAK,MAAM,SAAS,QAAQ,KAAK,KAAK,GACpC,WAAW,QAAQ,UAAU,KAAK,CAAC;GAEvC;GACA,MAAM,YAAY;IAChB,MAAM,QAAQ,QAAQ,MAAM;IAC5B,IAAI,OAAO,WAAW,QAAQ,UAAU,KAAK,CAAC;GAChD;EACF,CAAC;CACH;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UnderlyingReadable.d.ts","names":[],"sources":["../../../../src/core/utils/stream/UnderlyingReadable.ts"],"mappings":";KAAY,
|
|
1
|
+
{"version":3,"file":"UnderlyingReadable.d.ts","names":[],"sources":["../../../../src/core/utils/stream/UnderlyingReadable.ts"],"mappings":";KAAY,mBAAmB;EAC7B,SAAS,YAAY;EACrB,QAAQ,YAAY,uBAAuB;EAC3C,SAAS"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"merge.d.ts","names":[],"sources":["../../../../src/core/utils/stream/merge.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"merge.d.ts","names":[],"sources":["../../../../src/core/utils/stream/merge.ts"],"mappings":";;cAQa;;;;oBA0ES,eAAe;iBAUlB"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { AssistantStreamChunk } from "../../AssistantStreamChunk.js";
|
|
2
2
|
import { Counter } from "../Counter.js";
|
|
3
|
-
|
|
4
3
|
//#region src/core/utils/stream/path-utils.d.ts
|
|
5
4
|
declare class PathAppendEncoder extends TransformStream<AssistantStreamChunk, AssistantStreamChunk> {
|
|
6
5
|
constructor(idx: number);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path-utils.d.ts","names":[],"sources":["../../../../src/core/utils/stream/path-utils.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"path-utils.d.ts","names":[],"sources":["../../../../src/core/utils/stream/path-utils.ts"],"mappings":";;;cAGa,0BAA0B,gBACrC,sBACA;cAEY;;cAYD,0BAA0B,gBACrC,sBACA;cAEY;;cAmBD,yBAAyB,gBACpC,sBACA;cAEY,SAAS"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { ReadonlyJSONObject, ReadonlyJSONValue } from "../../utils/json/json-value.js";
|
|
2
2
|
import { ToolModelContentPart } from "../tool/tool-types.js";
|
|
3
|
-
|
|
4
3
|
//#region src/core/utils/types.d.ts
|
|
5
4
|
type TextStatus = {
|
|
6
5
|
type: "running";
|
|
@@ -43,7 +42,9 @@ type ToolCallStatus = {
|
|
|
43
42
|
* themselves.
|
|
44
43
|
*/
|
|
45
44
|
type ToolCallTiming = {
|
|
46
|
-
/** Epoch milliseconds when the tool call started streaming or executing. */
|
|
45
|
+
/** Epoch milliseconds when the tool call started streaming or executing. */
|
|
46
|
+
readonly startedAt: number;
|
|
47
|
+
/** Epoch milliseconds when the result landed. Absent while the call runs. */
|
|
47
48
|
readonly completedAt?: number;
|
|
48
49
|
};
|
|
49
50
|
type ToolCallPartBase = {
|
|
@@ -122,12 +123,19 @@ type AssistantMessageStatus = {
|
|
|
122
123
|
error?: ReadonlyJSONValue;
|
|
123
124
|
};
|
|
124
125
|
type AssistantMessageTiming = {
|
|
125
|
-
/** Timestamp when the stream started (ms since epoch) */
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
126
|
+
/** Timestamp when the stream started (ms since epoch) */
|
|
127
|
+
streamStartTime: number;
|
|
128
|
+
/** Time to first text token (ms), undefined if no text was generated */
|
|
129
|
+
firstTokenTime?: number;
|
|
130
|
+
/** Total stream duration (ms) */
|
|
131
|
+
totalStreamTime?: number;
|
|
132
|
+
/** Estimated or actual completion token count */
|
|
133
|
+
tokenCount?: number;
|
|
134
|
+
/** Tokens per second throughput */
|
|
135
|
+
tokensPerSecond?: number;
|
|
136
|
+
/** Total number of chunks received */
|
|
137
|
+
totalChunks: number;
|
|
138
|
+
/** Number of tool calls in the message */
|
|
131
139
|
toolCallCount: number;
|
|
132
140
|
};
|
|
133
141
|
type AssistantMessage = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/core/utils/types.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/core/utils/types.ts"],"mappings":";;;KAMK;EAEC;;EAGA;EACA;;EAGA;EACA;;KAOM;EACV;EACA;EACA,QAAQ;EACR;;KAGU;EACV;EACA;EACA,QAAQ;EACR;;KAGG;EAEC;EACA;;EAGA;EACA;;EAGA;EACA;;EAGA;EACA;;;;;;;;KASM;;WAED;;WAEA;;KAGN;EACH;EACA,QAAQ;EACR;EACA;EACA;EACA,MAAM;EACN,SAAS;EACT,WAAW;EACX,SAAS;EACT,wBAAwB;EACxB;EACA;;KAGG,4BAA4B;EAC/B;EACA;EACA;;KAGG,yBAAyB;EAC5B;EACA,QAAQ;EACR,WAAW;EACX,wBAAwB;EACxB;;KAGU,eAAe,4BAA4B;KAE3C;EACV;EACA;EACA;EACA;EACA;EACA;;KAGU;EACV;EACA;EACA;EACA;;KAGU;EACV;EACA;EACA,MAAM;EACN;;KAGU,uBACR,WACA,gBACA,eACA,aACA,WACA;KAEC;EACH;EACA;;KAGG;EAEC;EACA;;EAGA;EACA;EACA;EAQA,QAAQ;EACR;;KAGM;EAEN;;EAGA;EACA;;EAGA;EACA;;EAGA;EACA;EAOA,QAAQ;;KAGF;;EAEV;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;KAGU;EACV;EACA,QAAQ;EACR,OAAO;;;;EAIP,SAAS;EAET;IACE,gBAAgB;IAChB,eAAe;IACf,sBAAsB;IACtB,OAAO;IACP,QAAQ;IACR,SAAS"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"withPromiseOrValue.d.ts","names":[],"sources":["../../../src/core/utils/withPromiseOrValue.ts"],"mappings":";iBAAgB,
|
|
1
|
+
{"version":3,"file":"withPromiseOrValue.d.ts","names":[],"sources":["../../../src/core/utils/withPromiseOrValue.ts"],"mappings":";iBAAgB,mBAAmB,GACjC,gBAAgB,IAAI,YAAY,IAChC,cAAc,OAAO,MAAM,0BAC3B,eAAe,mBAAmB,2BACjC"}
|
package/dist/node_modules/.pnpm/@types_json-schema@7.0.15/node_modules/@types/json-schema/index.d.ts
CHANGED
|
@@ -9,13 +9,13 @@
|
|
|
9
9
|
* @see https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-6.1.1
|
|
10
10
|
*/
|
|
11
11
|
type JSONSchema7TypeName = "string" //
|
|
12
|
-
| "number" | "integer" | "boolean" | "object" | "array" | "null";
|
|
12
|
+
| "number" | "integer" | "boolean" | "object" | "array" | "null";
|
|
13
13
|
/**
|
|
14
14
|
* Primitive type
|
|
15
15
|
* @see https://tools.ietf.org/html/draft-handrews-json-schema-validation-01#section-6.1.1
|
|
16
16
|
*/
|
|
17
17
|
type JSONSchema7Type = string //
|
|
18
|
-
| number | boolean | JSONSchema7Object | JSONSchema7Array | null;
|
|
18
|
+
| number | boolean | JSONSchema7Object | JSONSchema7Array | null;
|
|
19
19
|
// Workaround for infinite type recursion
|
|
20
20
|
interface JSONSchema7Object {
|
|
21
21
|
[key: string]: JSONSchema7Type;
|
|
@@ -137,5 +137,5 @@ interface JSONSchema7 {
|
|
|
137
137
|
examples?: JSONSchema7Type | undefined;
|
|
138
138
|
}
|
|
139
139
|
//#endregion
|
|
140
|
-
export { JSONSchema7 };
|
|
140
|
+
export { JSONSchema7, JSONSchema7Array, JSONSchema7Definition, JSONSchema7Object, JSONSchema7Type, JSONSchema7TypeName, JSONSchema7Version };
|
|
141
141
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../../../../../../../node_modules/.pnpm/@types+json-schema@7.0.15/node_modules/@types/json-schema/index.d.ts"],"x_google_ignoreList":[0],"mappings":";;;;;;;;;;KA+iBY;;;;;;KAaA;sBAIN,oBACA;;UAIW;GACZ,cAAc;;;;UAKF,yBAAyB,MAAM;;;;;;;;;;;;KAapC;;;;;KAMA,wBAAwB;UACnB;EACb;EACA;EACA,UAAU;EACV;;;;;EAMA;KACK,cAAc;;;;;EAMnB,OAAO,sBAAsB;EAC7B,OAAO;EACP,QAAQ;;;;EAKR;EACA;EACA;EACA;EACA;;;;EAKA;EACA;EACA;;;;EAKA,QAAQ,wBAAwB;EAChC,kBAAkB;EAClB;EACA;EACA;EACA,WAAW;;;;EAKX;EACA;EACA;EACA;KACK,cAAc;;EAEnB;KACK,cAAc;;EAEnB,uBAAuB;EACvB;KACK,cAAc;;EAEnB,gBAAgB;;;;EAKhB,KAAK;EACL,OAAO;EACP,OAAO;;;;EAKP,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,MAAM;;;;EAKN;;;;EAKA;EACA;;;;EAKA;KACK,cAAc;;;;;EAMnB;EACA;EACA,UAAU;EACV;EACA;EACA,WAAW"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ResumableStreamContext.d.ts","names":[],"sources":["../../src/resumable/ResumableStreamContext.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"ResumableStreamContext.d.ts","names":[],"sources":["../../src/resumable/ResumableStreamContext.ts"],"mappings":";;KAOY;WACD,OAAO;WACP;;;;;;WAMA,aAAa,SAAS;WACtB,aAAa,kBAAkB,MAAM;WACrC,YAAY,kBAAkB;WAC9B,cACP,kBACA,0BACA;WAEO,WAAW,kBAAkB;;UAGvB;;EAEf,IACE,kBACA,kBAAkB,eAAe,cAChC,QAAQ,eAAe;;EAG1B,OAAO,mBAAmB,QAAQ,eAAe;;EAGjD,cAAc,mBAAmB,QAAQ,eAAe;EAExD,OAAO,mBAAmB,QAAQ;EAElC,OAAO,mBAAmB;;iBAGZ,6BACd,SAAS,gCACR"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","names":[],"sources":["../../src/resumable/constants.ts"],"mappings":";cAAa
|
|
1
|
+
{"version":3,"file":"constants.d.ts","names":[],"sources":["../../src/resumable/constants.ts"],"mappings":";cAAa"}
|
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
import { AssistantStreamController } from "../core/modules/assistant-stream.js";
|
|
2
2
|
import { AssistantStreamEncoder } from "../core/AssistantStream.js";
|
|
3
3
|
import { ResumableStreamContext } from "./ResumableStreamContext.js";
|
|
4
|
-
|
|
5
4
|
//#region src/resumable/createResumableAssistantStreamResponse.d.ts
|
|
6
5
|
declare const RESUMABLE_STREAM_ID_HEADER = "x-resumable-stream-id";
|
|
7
6
|
type CreateResumableAssistantStreamResponseOptions = {
|
|
8
7
|
readonly context: ResumableStreamContext;
|
|
9
8
|
readonly streamId: string;
|
|
10
|
-
readonly callback: (controller: AssistantStreamController) => PromiseLike<void> | void;
|
|
9
|
+
readonly callback: (controller: AssistantStreamController) => PromiseLike<void> | void;
|
|
10
|
+
/** Defaults to `DataStreamEncoder`. Also consulted for response headers. */
|
|
11
11
|
readonly encoder?: () => AssistantStreamEncoder;
|
|
12
12
|
readonly headers?: HeadersInit;
|
|
13
13
|
};
|
|
14
14
|
declare function createResumableAssistantStreamResponse(options: CreateResumableAssistantStreamResponseOptions): Promise<Response>;
|
|
15
15
|
type CreateResumeAssistantStreamResponseOptions = {
|
|
16
16
|
readonly context: ResumableStreamContext;
|
|
17
|
-
readonly streamId: string;
|
|
17
|
+
readonly streamId: string;
|
|
18
|
+
/** Read for `headers` only; the encoder transform is not invoked on resume. */
|
|
18
19
|
readonly encoder?: () => AssistantStreamEncoder;
|
|
19
|
-
readonly headers?: HeadersInit;
|
|
20
|
+
readonly headers?: HeadersInit;
|
|
21
|
+
/** Defaults to a 404 JSON response. */
|
|
20
22
|
readonly missingResponse?: () => Response;
|
|
21
23
|
};
|
|
22
24
|
declare function createResumeAssistantStreamResponse(options: CreateResumeAssistantStreamResponseOptions): Promise<Response>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createResumableAssistantStreamResponse.d.ts","names":[],"sources":["../../src/resumable/createResumableAssistantStreamResponse.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"createResumableAssistantStreamResponse.d.ts","names":[],"sources":["../../src/resumable/createResumableAssistantStreamResponse.ts"],"mappings":";;;;cAQa;KAED;WACD,SAAS;WACT;WACA,WACP,YAAY,8BACT;;WAEI,gBAAgB;WAChB,UAAU;;iBAGC,uCACpB,SAAS,gDACR,QAAQ;KAaC;WACD,SAAS;WACT;;WAEA,gBAAgB;WAChB,UAAU;;WAEV,wBAAwB;;iBAGb,oCACpB,SAAS,6CACR,QAAQ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","names":[],"sources":["../../src/resumable/errors.ts"],"mappings":";KAAY
|
|
1
|
+
{"version":3,"file":"errors.d.ts","names":[],"sources":["../../src/resumable/errors.ts"],"mappings":";KAAY;cAMC,6BAA6B;WAC/B,MAAM;cAEH,MAAM,0BAA0B;;iBAS9B,iBAAiB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InMemoryResumableStreamStore.d.ts","names":[],"sources":["../../../src/resumable/stores/InMemoryResumableStreamStore.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"InMemoryResumableStreamStore.d.ts","names":[],"sources":["../../../src/resumable/stores/InMemoryResumableStreamStore.ts"],"mappings":";;KA0BY;WACD;WACA;WACA;WACA;WACA;WACA;;iBAGK,mCACd,UAAS,sCACR;EAAyB"}
|
|
@@ -134,7 +134,8 @@ function createInMemoryResumableStreamStore(options = {}) {
|
|
|
134
134
|
if (state.final.kind === "error") throw new Error(state.final.error);
|
|
135
135
|
return;
|
|
136
136
|
}
|
|
137
|
-
|
|
137
|
+
const wakeBy = state.expiresAt - now();
|
|
138
|
+
await waitForUpdate(state, signal, wakeBy);
|
|
138
139
|
evictExpired();
|
|
139
140
|
}
|
|
140
141
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InMemoryResumableStreamStore.js","names":[],"sources":["../../../src/resumable/stores/InMemoryResumableStreamStore.ts"],"sourcesContent":["import { DEFAULT_TTL_MS } from \"../constants\";\nimport { ResumableStreamError, validateStreamId } from \"../errors\";\nimport type {\n ResumableStreamEntry,\n ResumableStreamStatus,\n ResumableStreamStore,\n} from \"../types\";\n\ntype FinalizeMarker = { kind: \"done\" } | { kind: \"error\"; error: string };\n\ntype StreamState = {\n entries: ResumableStreamEntry[];\n nextSeq: number;\n expiresAt: number;\n ttlMs: number;\n final: FinalizeMarker | undefined;\n waiters: Array<() => void>;\n};\n\nconst cursorOf = (seq: number): string => seq.toString(36);\nconst seqFromCursor = (cursor: string): number => {\n if (cursor === \"\") return 0;\n const parsed = Number.parseInt(cursor, 36);\n return Number.isNaN(parsed) ? 0 : parsed;\n};\n\nexport type InMemoryResumableStreamStoreOptions = {\n readonly defaultTtlMs?: number;\n readonly now?: () => number;\n readonly maxChunkBytes?: number;\n readonly maxEntriesPerStream?: number;\n readonly maxStreams?: number;\n readonly gcIntervalMs?: number;\n};\n\nexport function createInMemoryResumableStreamStore(\n options: InMemoryResumableStreamStoreOptions = {},\n): ResumableStreamStore & { dispose: () => void } {\n const streams = new Map<string, StreamState>();\n const defaultTtlMs = options.defaultTtlMs ?? DEFAULT_TTL_MS;\n const now = options.now ?? Date.now;\n const maxChunkBytes = options.maxChunkBytes;\n const maxEntriesPerStream = options.maxEntriesPerStream;\n const maxStreams = options.maxStreams;\n\n const evictExpired = (): void => {\n const t = now();\n for (const [id, state] of streams) {\n if (state.expiresAt > t) continue;\n streams.delete(id);\n state.final ??= { kind: \"error\", error: \"Stream expired\" };\n notify(state);\n }\n };\n\n const notify = (state: StreamState): void => {\n const waiters = state.waiters;\n state.waiters = [];\n for (const wake of waiters) wake();\n };\n\n const findStartIndex = (state: StreamState, cursor: string): number => {\n if (cursor === \"\") return 0;\n const after = seqFromCursor(cursor);\n let lo = 0;\n let hi = state.entries.length;\n while (lo < hi) {\n const mid = (lo + hi) >>> 1;\n const seq = seqFromCursor(state.entries[mid]!.cursor);\n if (seq <= after) lo = mid + 1;\n else hi = mid;\n }\n return lo;\n };\n\n const waitForUpdate = (\n state: StreamState,\n signal: AbortSignal,\n wakeBy?: number,\n ): Promise<void> =>\n new Promise<void>((resolve) => {\n let settled = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n const wake = () => {\n if (settled) return;\n settled = true;\n if (timer !== undefined) clearTimeout(timer);\n signal.removeEventListener(\"abort\", wake);\n const idx = state.waiters.indexOf(wake);\n if (idx !== -1) state.waiters.splice(idx, 1);\n resolve();\n };\n if (signal.aborted) {\n wake();\n return;\n }\n state.waiters.push(wake);\n signal.addEventListener(\"abort\", wake, { once: true });\n if (wakeBy !== undefined) {\n if (wakeBy > 0) {\n timer = setTimeout(wake, wakeBy);\n } else {\n // already past the deadline; resolve so the caller can re-check\n // expiration without waiting for an external notify.\n wake();\n }\n }\n });\n\n const requireActive = (streamId: string): StreamState => {\n evictExpired();\n const state = streams.get(streamId);\n if (!state) throw new Error(`Stream not found: ${streamId}`);\n if (state.final) {\n throw new ResumableStreamError(\n \"finalized\",\n `Stream already finalized: ${streamId}`,\n );\n }\n return state;\n };\n\n const gcTimer =\n options.gcIntervalMs !== undefined\n ? setInterval(evictExpired, options.gcIntervalMs)\n : undefined;\n gcTimer?.unref?.();\n\n return {\n async acquire(streamId, acquireOptions) {\n validateStreamId(streamId);\n evictExpired();\n const existing = streams.get(streamId);\n if (existing) return \"consumer\";\n\n if (maxStreams !== undefined && streams.size >= maxStreams) {\n throw new Error(\"maxStreams exceeded\");\n }\n\n const ttlMs = acquireOptions?.ttlMs ?? defaultTtlMs;\n streams.set(streamId, {\n entries: [],\n nextSeq: 1,\n expiresAt: now() + ttlMs,\n ttlMs,\n final: undefined,\n waiters: [],\n });\n return \"producer\";\n },\n\n async append(streamId, chunk) {\n validateStreamId(streamId);\n if (maxChunkBytes !== undefined && chunk.byteLength > maxChunkBytes) {\n throw new Error(`Chunk exceeds maxChunkBytes: ${chunk.byteLength}`);\n }\n const state = requireActive(streamId);\n if (\n maxEntriesPerStream !== undefined &&\n state.entries.length >= maxEntriesPerStream\n ) {\n throw new Error(`Stream exceeded maxEntriesPerStream: ${streamId}`);\n }\n const seq = state.nextSeq;\n state.nextSeq += 1;\n state.entries.push({ cursor: cursorOf(seq), chunk });\n state.expiresAt = now() + state.ttlMs;\n notify(state);\n },\n\n async finalize(streamId, status, error) {\n validateStreamId(streamId);\n evictExpired();\n const state = streams.get(streamId);\n if (!state) throw new Error(`Stream not found: ${streamId}`);\n if (state.final) return;\n state.final =\n status === \"done\"\n ? { kind: \"done\" }\n : { kind: \"error\", error: error ?? \"Stream errored\" };\n state.expiresAt = now() + state.ttlMs;\n notify(state);\n },\n\n async *read(streamId, cursor, signal) {\n validateStreamId(streamId);\n evictExpired();\n const state = streams.get(streamId);\n if (!state) throw new Error(`Stream not found: ${streamId}`);\n\n let idx = findStartIndex(state, cursor);\n\n while (true) {\n if (signal.aborted) return;\n\n while (idx < state.entries.length) {\n if (signal.aborted) return;\n yield state.entries[idx]!;\n idx += 1;\n }\n\n if (state.final) {\n if (state.final.kind === \"error\") {\n throw new Error(state.final.error);\n }\n return;\n }\n\n const wakeBy = state.expiresAt - now();\n await waitForUpdate(state, signal, wakeBy);\n evictExpired();\n }\n },\n\n async status(streamId): Promise<ResumableStreamStatus> {\n validateStreamId(streamId);\n evictExpired();\n const state = streams.get(streamId);\n if (!state) return \"missing\";\n if (!state.final) return \"streaming\";\n return state.final.kind === \"error\" ? \"error\" : \"done\";\n },\n\n async delete(streamId) {\n validateStreamId(streamId);\n const state = streams.get(streamId);\n if (!state) return;\n streams.delete(streamId);\n state.final ??= { kind: \"done\" };\n notify(state);\n },\n\n dispose() {\n if (gcTimer !== undefined) clearInterval(gcTimer);\n },\n };\n}\n"],"mappings":";;;AAmBA,MAAM,YAAY,QAAwB,IAAI,SAAS,EAAE;AACzD,MAAM,iBAAiB,WAA2B;CAChD,IAAI,WAAW,IAAI,OAAO;CAC1B,MAAM,SAAS,OAAO,SAAS,QAAQ,EAAE;CACzC,OAAO,OAAO,MAAM,MAAM,IAAI,IAAI;AACpC;AAWA,SAAgB,mCACd,UAA+C,CAAC,GACA;CAChD,MAAM,0BAAU,IAAI,IAAyB;CAC7C,MAAM,eAAe,QAAQ,gBAAA;CAC7B,MAAM,MAAM,QAAQ,OAAO,KAAK;CAChC,MAAM,gBAAgB,QAAQ;CAC9B,MAAM,sBAAsB,QAAQ;CACpC,MAAM,aAAa,QAAQ;CAE3B,MAAM,qBAA2B;EAC/B,MAAM,IAAI,IAAI;EACd,KAAK,MAAM,CAAC,IAAI,UAAU,SAAS;GACjC,IAAI,MAAM,YAAY,GAAG;GACzB,QAAQ,OAAO,EAAE;GACjB,MAAM,UAAU;IAAE,MAAM;IAAS,OAAO;GAAiB;GACzD,OAAO,KAAK;EACd;CACF;CAEA,MAAM,UAAU,UAA6B;EAC3C,MAAM,UAAU,MAAM;EACtB,MAAM,UAAU,CAAC;EACjB,KAAK,MAAM,QAAQ,SAAS,KAAK;CACnC;CAEA,MAAM,kBAAkB,OAAoB,WAA2B;EACrE,IAAI,WAAW,IAAI,OAAO;EAC1B,MAAM,QAAQ,cAAc,MAAM;EAClC,IAAI,KAAK;EACT,IAAI,KAAK,MAAM,QAAQ;EACvB,OAAO,KAAK,IAAI;GACd,MAAM,MAAO,KAAK,OAAQ;GAE1B,IADY,cAAc,MAAM,QAAQ,IAAI,CAAE,MACxC,KAAK,OAAO,KAAK,MAAM;QACxB,KAAK;EACZ;EACA,OAAO;CACT;CAEA,MAAM,iBACJ,OACA,QACA,WAEA,IAAI,SAAe,YAAY;EAC7B,IAAI,UAAU;EACd,IAAI;EACJ,MAAM,aAAa;GACjB,IAAI,SAAS;GACb,UAAU;GACV,IAAI,UAAU,KAAA,GAAW,aAAa,KAAK;GAC3C,OAAO,oBAAoB,SAAS,IAAI;GACxC,MAAM,MAAM,MAAM,QAAQ,QAAQ,IAAI;GACtC,IAAI,QAAQ,IAAI,MAAM,QAAQ,OAAO,KAAK,CAAC;GAC3C,QAAQ;EACV;EACA,IAAI,OAAO,SAAS;GAClB,KAAK;GACL;EACF;EACA,MAAM,QAAQ,KAAK,IAAI;EACvB,OAAO,iBAAiB,SAAS,MAAM,EAAE,MAAM,KAAK,CAAC;EACrD,IAAI,WAAW,KAAA,GACb,IAAI,SAAS,GACX,QAAQ,WAAW,MAAM,MAAM;OAI/B,KAAK;CAGX,CAAC;CAEH,MAAM,iBAAiB,aAAkC;EACvD,aAAa;EACb,MAAM,QAAQ,QAAQ,IAAI,QAAQ;EAClC,IAAI,CAAC,OAAO,MAAM,IAAI,MAAM,qBAAqB,UAAU;EAC3D,IAAI,MAAM,OACR,MAAM,IAAI,qBACR,aACA,6BAA6B,UAC/B;EAEF,OAAO;CACT;CAEA,MAAM,UACJ,QAAQ,iBAAiB,KAAA,IACrB,YAAY,cAAc,QAAQ,YAAY,IAC9C,KAAA;CACN,SAAS,QAAQ;CAEjB,OAAO;EACL,MAAM,QAAQ,UAAU,gBAAgB;GACtC,iBAAiB,QAAQ;GACzB,aAAa;GAEb,IADiB,QAAQ,IAAI,QAClB,GAAG,OAAO;GAErB,IAAI,eAAe,KAAA,KAAa,QAAQ,QAAQ,YAC9C,MAAM,IAAI,MAAM,qBAAqB;GAGvC,MAAM,QAAQ,gBAAgB,SAAS;GACvC,QAAQ,IAAI,UAAU;IACpB,SAAS,CAAC;IACV,SAAS;IACT,WAAW,IAAI,IAAI;IACnB;IACA,OAAO,KAAA;IACP,SAAS,CAAC;GACZ,CAAC;GACD,OAAO;EACT;EAEA,MAAM,OAAO,UAAU,OAAO;GAC5B,iBAAiB,QAAQ;GACzB,IAAI,kBAAkB,KAAA,KAAa,MAAM,aAAa,eACpD,MAAM,IAAI,MAAM,gCAAgC,MAAM,YAAY;GAEpE,MAAM,QAAQ,cAAc,QAAQ;GACpC,IACE,wBAAwB,KAAA,KACxB,MAAM,QAAQ,UAAU,qBAExB,MAAM,IAAI,MAAM,wCAAwC,UAAU;GAEpE,MAAM,MAAM,MAAM;GAClB,MAAM,WAAW;GACjB,MAAM,QAAQ,KAAK;IAAE,QAAQ,SAAS,GAAG;IAAG;GAAM,CAAC;GACnD,MAAM,YAAY,IAAI,IAAI,MAAM;GAChC,OAAO,KAAK;EACd;EAEA,MAAM,SAAS,UAAU,QAAQ,OAAO;GACtC,iBAAiB,QAAQ;GACzB,aAAa;GACb,MAAM,QAAQ,QAAQ,IAAI,QAAQ;GAClC,IAAI,CAAC,OAAO,MAAM,IAAI,MAAM,qBAAqB,UAAU;GAC3D,IAAI,MAAM,OAAO;GACjB,MAAM,QACJ,WAAW,SACP,EAAE,MAAM,OAAO,IACf;IAAE,MAAM;IAAS,OAAO,SAAS;GAAiB;GACxD,MAAM,YAAY,IAAI,IAAI,MAAM;GAChC,OAAO,KAAK;EACd;EAEA,OAAO,KAAK,UAAU,QAAQ,QAAQ;GACpC,iBAAiB,QAAQ;GACzB,aAAa;GACb,MAAM,QAAQ,QAAQ,IAAI,QAAQ;GAClC,IAAI,CAAC,OAAO,MAAM,IAAI,MAAM,qBAAqB,UAAU;GAE3D,IAAI,MAAM,eAAe,OAAO,MAAM;GAEtC,OAAO,MAAM;IACX,IAAI,OAAO,SAAS;IAEpB,OAAO,MAAM,MAAM,QAAQ,QAAQ;KACjC,IAAI,OAAO,SAAS;KACpB,MAAM,MAAM,QAAQ;KACpB,OAAO;IACT;IAEA,IAAI,MAAM,OAAO;KACf,IAAI,MAAM,MAAM,SAAS,SACvB,MAAM,IAAI,MAAM,MAAM,MAAM,KAAK;KAEnC;IACF;IAGA,MAAM,cAAc,OAAO,QADZ,MAAM,YAAY,IAAI,CACI;IACzC,aAAa;GACf;EACF;EAEA,MAAM,OAAO,UAA0C;GACrD,iBAAiB,QAAQ;GACzB,aAAa;GACb,MAAM,QAAQ,QAAQ,IAAI,QAAQ;GAClC,IAAI,CAAC,OAAO,OAAO;GACnB,IAAI,CAAC,MAAM,OAAO,OAAO;GACzB,OAAO,MAAM,MAAM,SAAS,UAAU,UAAU;EAClD;EAEA,MAAM,OAAO,UAAU;GACrB,iBAAiB,QAAQ;GACzB,MAAM,QAAQ,QAAQ,IAAI,QAAQ;GAClC,IAAI,CAAC,OAAO;GACZ,QAAQ,OAAO,QAAQ;GACvB,MAAM,UAAU,EAAE,MAAM,OAAO;GAC/B,OAAO,KAAK;EACd;EAEA,UAAU;GACR,IAAI,YAAY,KAAA,GAAW,cAAc,OAAO;EAClD;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"InMemoryResumableStreamStore.js","names":[],"sources":["../../../src/resumable/stores/InMemoryResumableStreamStore.ts"],"sourcesContent":["import { DEFAULT_TTL_MS } from \"../constants\";\nimport { ResumableStreamError, validateStreamId } from \"../errors\";\nimport type {\n ResumableStreamEntry,\n ResumableStreamStatus,\n ResumableStreamStore,\n} from \"../types\";\n\ntype FinalizeMarker = { kind: \"done\" } | { kind: \"error\"; error: string };\n\ntype StreamState = {\n entries: ResumableStreamEntry[];\n nextSeq: number;\n expiresAt: number;\n ttlMs: number;\n final: FinalizeMarker | undefined;\n waiters: Array<() => void>;\n};\n\nconst cursorOf = (seq: number): string => seq.toString(36);\nconst seqFromCursor = (cursor: string): number => {\n if (cursor === \"\") return 0;\n const parsed = Number.parseInt(cursor, 36);\n return Number.isNaN(parsed) ? 0 : parsed;\n};\n\nexport type InMemoryResumableStreamStoreOptions = {\n readonly defaultTtlMs?: number;\n readonly now?: () => number;\n readonly maxChunkBytes?: number;\n readonly maxEntriesPerStream?: number;\n readonly maxStreams?: number;\n readonly gcIntervalMs?: number;\n};\n\nexport function createInMemoryResumableStreamStore(\n options: InMemoryResumableStreamStoreOptions = {},\n): ResumableStreamStore & { dispose: () => void } {\n const streams = new Map<string, StreamState>();\n const defaultTtlMs = options.defaultTtlMs ?? DEFAULT_TTL_MS;\n const now = options.now ?? Date.now;\n const maxChunkBytes = options.maxChunkBytes;\n const maxEntriesPerStream = options.maxEntriesPerStream;\n const maxStreams = options.maxStreams;\n\n const evictExpired = (): void => {\n const t = now();\n for (const [id, state] of streams) {\n if (state.expiresAt > t) continue;\n streams.delete(id);\n state.final ??= { kind: \"error\", error: \"Stream expired\" };\n notify(state);\n }\n };\n\n const notify = (state: StreamState): void => {\n const waiters = state.waiters;\n state.waiters = [];\n for (const wake of waiters) wake();\n };\n\n const findStartIndex = (state: StreamState, cursor: string): number => {\n if (cursor === \"\") return 0;\n const after = seqFromCursor(cursor);\n let lo = 0;\n let hi = state.entries.length;\n while (lo < hi) {\n const mid = (lo + hi) >>> 1;\n const seq = seqFromCursor(state.entries[mid]!.cursor);\n if (seq <= after) lo = mid + 1;\n else hi = mid;\n }\n return lo;\n };\n\n const waitForUpdate = (\n state: StreamState,\n signal: AbortSignal,\n wakeBy?: number,\n ): Promise<void> =>\n new Promise<void>((resolve) => {\n let settled = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n const wake = () => {\n if (settled) return;\n settled = true;\n if (timer !== undefined) clearTimeout(timer);\n signal.removeEventListener(\"abort\", wake);\n const idx = state.waiters.indexOf(wake);\n if (idx !== -1) state.waiters.splice(idx, 1);\n resolve();\n };\n if (signal.aborted) {\n wake();\n return;\n }\n state.waiters.push(wake);\n signal.addEventListener(\"abort\", wake, { once: true });\n if (wakeBy !== undefined) {\n if (wakeBy > 0) {\n timer = setTimeout(wake, wakeBy);\n } else {\n // already past the deadline; resolve so the caller can re-check\n // expiration without waiting for an external notify.\n wake();\n }\n }\n });\n\n const requireActive = (streamId: string): StreamState => {\n evictExpired();\n const state = streams.get(streamId);\n if (!state) throw new Error(`Stream not found: ${streamId}`);\n if (state.final) {\n throw new ResumableStreamError(\n \"finalized\",\n `Stream already finalized: ${streamId}`,\n );\n }\n return state;\n };\n\n const gcTimer =\n options.gcIntervalMs !== undefined\n ? setInterval(evictExpired, options.gcIntervalMs)\n : undefined;\n gcTimer?.unref?.();\n\n return {\n async acquire(streamId, acquireOptions) {\n validateStreamId(streamId);\n evictExpired();\n const existing = streams.get(streamId);\n if (existing) return \"consumer\";\n\n if (maxStreams !== undefined && streams.size >= maxStreams) {\n throw new Error(\"maxStreams exceeded\");\n }\n\n const ttlMs = acquireOptions?.ttlMs ?? defaultTtlMs;\n streams.set(streamId, {\n entries: [],\n nextSeq: 1,\n expiresAt: now() + ttlMs,\n ttlMs,\n final: undefined,\n waiters: [],\n });\n return \"producer\";\n },\n\n async append(streamId, chunk) {\n validateStreamId(streamId);\n if (maxChunkBytes !== undefined && chunk.byteLength > maxChunkBytes) {\n throw new Error(`Chunk exceeds maxChunkBytes: ${chunk.byteLength}`);\n }\n const state = requireActive(streamId);\n if (\n maxEntriesPerStream !== undefined &&\n state.entries.length >= maxEntriesPerStream\n ) {\n throw new Error(`Stream exceeded maxEntriesPerStream: ${streamId}`);\n }\n const seq = state.nextSeq;\n state.nextSeq += 1;\n state.entries.push({ cursor: cursorOf(seq), chunk });\n state.expiresAt = now() + state.ttlMs;\n notify(state);\n },\n\n async finalize(streamId, status, error) {\n validateStreamId(streamId);\n evictExpired();\n const state = streams.get(streamId);\n if (!state) throw new Error(`Stream not found: ${streamId}`);\n if (state.final) return;\n state.final =\n status === \"done\"\n ? { kind: \"done\" }\n : { kind: \"error\", error: error ?? \"Stream errored\" };\n state.expiresAt = now() + state.ttlMs;\n notify(state);\n },\n\n async *read(streamId, cursor, signal) {\n validateStreamId(streamId);\n evictExpired();\n const state = streams.get(streamId);\n if (!state) throw new Error(`Stream not found: ${streamId}`);\n\n let idx = findStartIndex(state, cursor);\n\n while (true) {\n if (signal.aborted) return;\n\n while (idx < state.entries.length) {\n if (signal.aborted) return;\n yield state.entries[idx]!;\n idx += 1;\n }\n\n if (state.final) {\n if (state.final.kind === \"error\") {\n throw new Error(state.final.error);\n }\n return;\n }\n\n const wakeBy = state.expiresAt - now();\n await waitForUpdate(state, signal, wakeBy);\n evictExpired();\n }\n },\n\n async status(streamId): Promise<ResumableStreamStatus> {\n validateStreamId(streamId);\n evictExpired();\n const state = streams.get(streamId);\n if (!state) return \"missing\";\n if (!state.final) return \"streaming\";\n return state.final.kind === \"error\" ? \"error\" : \"done\";\n },\n\n async delete(streamId) {\n validateStreamId(streamId);\n const state = streams.get(streamId);\n if (!state) return;\n streams.delete(streamId);\n state.final ??= { kind: \"done\" };\n notify(state);\n },\n\n dispose() {\n if (gcTimer !== undefined) clearInterval(gcTimer);\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ioredis.d.ts","names":[],"sources":["../../../src/resumable/stores/ioredis.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"ioredis.d.ts","names":[],"sources":["../../../src/resumable/stores/ioredis.ts"],"mappings":";;;;KAaY,cAAc,QAAU;;;;;iBAMpB,kCACd,QAAQ,aACR,UAAU,mCACT"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { ResumableStreamAcquireOptions, ResumableStreamEntry, ResumableStreamRole, ResumableStreamStatus, ResumableStreamStore } from "../types.js";
|
|
2
|
-
|
|
3
2
|
//#region src/resumable/stores/redis-impl.d.ts
|
|
4
3
|
type PipelineCommand = {
|
|
5
4
|
readonly type: "xAdd";
|
|
@@ -37,7 +36,8 @@ interface RedisLikeClient {
|
|
|
37
36
|
}
|
|
38
37
|
type RedisResumableStreamStoreOptions = {
|
|
39
38
|
readonly keyPrefix?: string;
|
|
40
|
-
readonly defaultTtlMs?: number;
|
|
39
|
+
readonly defaultTtlMs?: number;
|
|
40
|
+
/** Defaults to 100ms. Lower values reduce read latency, raise traffic. */
|
|
41
41
|
readonly pollIntervalMs?: number;
|
|
42
42
|
readonly maxChunkBytes?: number;
|
|
43
43
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redis-impl.d.ts","names":[],"sources":["../../../src/resumable/stores/redis-impl.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"redis-impl.d.ts","names":[],"sources":["../../../src/resumable/stores/redis-impl.ts"],"mappings":";;KAsBY;WAEG;WACA;WACA,QAAQ,wBAAwB;;WAEhC;WAAyB;WAAsB;;WAE/C;WACA;WACA;WACA;;;;;;;UAQE;EACf,MAAM,aAAa,eAAe,iBAAiB;EACnD,IAAI,aAAa,eAAe,iBAAiB;EACjD,IAAI,cAAc;EAClB,OAAO,aAAa,iBAAiB;EACrC,OAAO,cAAc;EACrB,IAAI,iBAAiB;EACrB,KACE,aACA,QAAQ,wBAAwB,cAC/B;EACH,OACE,aACA,eACA,cACC,QACD;IAAQ;IAAY,QAAQ,wBAAwB;;;EAGtD,SAAS,mBAAmB,oBAAoB;;KAGtC;WACD;WACA;;WAEA;WACA;;cAGE,qCAAqC;mBAC/B;mBACA;mBACA;mBACA;mBACA;cAGf,QAAQ,iBACR,UAAS;EASL,QACJ,kBACA,UAAU,gCACT,QAAQ;EAiBL,OAAO,kBAAkB,OAAO,aAAa;EA8B7C,SACJ,kBACA,0BACA,iBACC;EA6BI,KACL,kBACA,gBACA,QAAQ,cACP,cAAc;EA2CX,OAAO,mBAAmB,QAAQ;EAWlC,OAAO,mBAAmB;UAKlB;UAQN;UAIA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redis.d.ts","names":[],"sources":["../../../src/resumable/stores/redis.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"redis.d.ts","names":[],"sources":["../../../src/resumable/stores/redis.ts"],"mappings":";;;KAUK,kBAAkB,wBAAwB;UAErC;EACR,KAAK,aAAa,YAAY,QAAQ,kBAAkB;EACxD,OAAO,aAAa,kBAAkB;EACtC,IACE,aACA,eACA;IAAW;MACV;EACH,kBAAkB;EAClB,QAAQ;;;UAIO;EACf,IACE,aACA,eACA;IAAW;IAAU;MACpB;EACH,IACE,aACA,eACA;IAAW;MACV;EACH,IAAI,cAAc;EAClB,OAAO,aAAa,kBAAkB;EACtC,OAAO,cAAc;EACrB,IAAI,0BAA0B;EAC9B,KAAK,aAAa,YAAY,QAAQ,kBAAkB;EACxD,YAAY,aACV,MAAM,uBAAuB,SAC7B;IAAY,cAAc;MACzB,QAAQ;EACX,SAAS;;;;;;;iBAQK,gCACd,QAAQ,eACR,UAAU,mCACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","names":[],"sources":["../../src/resumable/types.ts"],"mappings":";KAAY
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../../src/resumable/types.ts"],"mappings":";KAAY;KAEA;KAEA;WACD;WACA,OAAO;;KAGN;WACD;;UAGM;;;;;;EAMf,QACE,kBACA,UAAU,gCACT,QAAQ;;EAGX,OAAO,kBAAkB,OAAO,aAAa;EAE7C,SACE,kBACA,0BACA,iBACC;;;;;;EAOH,KACE,kBACA,gBACA,QAAQ,cACP,cAAc;EAEjB,OAAO,mBAAmB,QAAQ;;EAGlC,OAAO,mBAAmB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AsyncIterableStream.d.ts","names":[],"sources":["../../src/utils/AsyncIterableStream.ts"],"mappings":";KAAY,
|
|
1
|
+
{"version":3,"file":"AsyncIterableStream.d.ts","names":[],"sources":["../../src/utils/AsyncIterableStream.ts"],"mappings":";KAAY,oBAAoB,KAAK,cAAc,KAAK,eAAe;iBAiBvD,sBAAsB,GACpC,QAAQ,eAAe,KACtB,oBAAoB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fix-json.d.ts","names":[],"sources":["../../../src/utils/json/fix-json.ts"],"mappings":";iBA+DgB,
|
|
1
|
+
{"version":3,"file":"fix-json.d.ts","names":[],"sources":["../../../src/utils/json/fix-json.ts"],"mappings":";iBA+DgB,QAAQ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json-value.d.ts","names":[],"sources":["../../../src/utils/json/json-value.ts"],"mappings":";KAAY,
|
|
1
|
+
{"version":3,"file":"json-value.d.ts","names":[],"sources":["../../../src/utils/json/json-value.ts"],"mappings":";KAAY,uDAKR,qBACA;KAEQ;YACA,cAAc;;KAGd,6BAA6B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse-partial-json-object.d.ts","names":[],"sources":["../../../src/utils/json/parse-partial-json-object.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"parse-partial-json-object.d.ts","names":[],"sources":["../../../src/utils/json/parse-partial-json-object.ts"],"mappings":";;cAIM;KAID;KAEA;EACH;EACA;;cAGW,2BACX,KAAK,4BACJ;cAIU,yBACX,kBAEG;GACE,kCAAkC;;cAqE5B,iCACX,KAAK,yBACL,mCACC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"promiseWithResolvers.d.ts","names":[],"sources":["../../src/utils/promiseWithResolvers.ts"],"mappings":";cAAa,
|
|
1
|
+
{"version":3,"file":"promiseWithResolvers.d.ts","names":[],"sources":["../../src/utils/promiseWithResolvers.ts"],"mappings":";cAAa,uBAAwB;;mBACb,IAAI,YAAY"}
|
package/dist/utils.d.ts
CHANGED
|
@@ -3,4 +3,5 @@ import { getPartialJsonObjectFieldState, getPartialJsonObjectMeta, parsePartialJ
|
|
|
3
3
|
import { AsyncIterableStream, asAsyncIterableStream } from "./utils/AsyncIterableStream.js";
|
|
4
4
|
import { AssistantTransformStream } from "./core/utils/stream/AssistantTransformStream.js";
|
|
5
5
|
import { AssistantMetaTransformStream } from "./core/utils/stream/AssistantMetaTransformStream.js";
|
|
6
|
-
|
|
6
|
+
import { SSEEvent, SSEEventDecoder } from "./core/utils/stream/SSEEventDecoder.js";
|
|
7
|
+
export { AssistantMetaTransformStream, AssistantTransformStream, type AsyncIterableStream, type ReadonlyJSONArray, type ReadonlyJSONObject, type ReadonlyJSONValue, type SSEEvent, SSEEventDecoder, asAsyncIterableStream, getPartialJsonObjectFieldState, getPartialJsonObjectMeta, parsePartialJsonObject };
|
package/dist/utils.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AssistantTransformStream } from "./core/utils/stream/AssistantTransformStream.js";
|
|
2
2
|
import { AssistantMetaTransformStream } from "./core/utils/stream/AssistantMetaTransformStream.js";
|
|
3
3
|
import { getPartialJsonObjectFieldState, getPartialJsonObjectMeta, parsePartialJsonObject } from "./utils/json/parse-partial-json-object.js";
|
|
4
|
+
import { SSEEventDecoder } from "./core/utils/stream/SSEEventDecoder.js";
|
|
4
5
|
import { asAsyncIterableStream } from "./utils/AsyncIterableStream.js";
|
|
5
|
-
export { AssistantMetaTransformStream, AssistantTransformStream, asAsyncIterableStream, getPartialJsonObjectFieldState, getPartialJsonObjectMeta, parsePartialJsonObject };
|
|
6
|
+
export { AssistantMetaTransformStream, AssistantTransformStream, SSEEventDecoder, asAsyncIterableStream, getPartialJsonObjectFieldState, getPartialJsonObjectMeta, parsePartialJsonObject };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "assistant-stream",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.26",
|
|
4
4
|
"description": "Streaming utilities for AI assistants",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"sideEffects": false,
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@standard-schema/spec": "^1.1.0",
|
|
50
|
-
"nanoid": "^
|
|
50
|
+
"nanoid": "^6.0.0",
|
|
51
51
|
"secure-json-parse": "^4.1.0"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
@@ -65,9 +65,9 @@
|
|
|
65
65
|
"devDependencies": {
|
|
66
66
|
"@types/json-schema": "^7.0.15",
|
|
67
67
|
"ioredis": "^5.11.1",
|
|
68
|
-
"redis": "^6.
|
|
69
|
-
"vitest": "^4.1.
|
|
70
|
-
"@assistant-ui/x-buildutils": "0.0.
|
|
68
|
+
"redis": "^6.1.0",
|
|
69
|
+
"vitest": "^4.1.10",
|
|
70
|
+
"@assistant-ui/x-buildutils": "0.0.18"
|
|
71
71
|
},
|
|
72
72
|
"publishConfig": {
|
|
73
73
|
"access": "public",
|
|
@@ -132,6 +132,8 @@ export type AssistantStreamChunk = { readonly path: readonly number[] } & (
|
|
|
132
132
|
/** Emits a stream-level error message. */
|
|
133
133
|
readonly type: "error";
|
|
134
134
|
readonly error: string;
|
|
135
|
+
readonly code?: string;
|
|
136
|
+
readonly severity?: "critical" | "warning" | "info";
|
|
135
137
|
}
|
|
136
138
|
| {
|
|
137
139
|
/** Applies object-stream operations to state carried by this stream. */
|
|
@@ -225,3 +225,66 @@ describe("AssistantMessageAccumulator timing", () => {
|
|
|
225
225
|
expect(last.metadata.timing!.firstTokenTime).toBeTypeOf("number");
|
|
226
226
|
});
|
|
227
227
|
});
|
|
228
|
+
|
|
229
|
+
describe("AssistantMessageAccumulator error chunks", () => {
|
|
230
|
+
it("defaults missing code to unknown", async () => {
|
|
231
|
+
const messages = await collectStream([
|
|
232
|
+
{ type: "error", path: [], error: "stream failed" },
|
|
233
|
+
]);
|
|
234
|
+
const last = messages.at(-1)!;
|
|
235
|
+
|
|
236
|
+
expect(last.status).toEqual({
|
|
237
|
+
type: "incomplete",
|
|
238
|
+
reason: "error",
|
|
239
|
+
error: { code: "unknown", message: "stream failed" },
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it("carries code and severity through unchanged", async () => {
|
|
244
|
+
const messages = await collectStream([
|
|
245
|
+
{
|
|
246
|
+
type: "error",
|
|
247
|
+
path: [],
|
|
248
|
+
error: "rate limited",
|
|
249
|
+
code: "provider",
|
|
250
|
+
severity: "warning",
|
|
251
|
+
},
|
|
252
|
+
]);
|
|
253
|
+
const last = messages.at(-1)!;
|
|
254
|
+
|
|
255
|
+
expect(last.status).toEqual({
|
|
256
|
+
type: "incomplete",
|
|
257
|
+
reason: "error",
|
|
258
|
+
error: {
|
|
259
|
+
code: "provider",
|
|
260
|
+
message: "rate limited",
|
|
261
|
+
severity: "warning",
|
|
262
|
+
},
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it("drops out-of-taxonomy severity at ingestion", async () => {
|
|
267
|
+
const messages = await collectStream([
|
|
268
|
+
{
|
|
269
|
+
type: "error",
|
|
270
|
+
path: [],
|
|
271
|
+
error: "boom",
|
|
272
|
+
code: "unknown",
|
|
273
|
+
severity: "fatal",
|
|
274
|
+
} as unknown as AssistantStreamChunk,
|
|
275
|
+
]);
|
|
276
|
+
const last = messages.at(-1)!;
|
|
277
|
+
|
|
278
|
+
expect(last.status).toEqual({
|
|
279
|
+
type: "incomplete",
|
|
280
|
+
reason: "error",
|
|
281
|
+
error: {
|
|
282
|
+
code: "unknown",
|
|
283
|
+
message: "boom",
|
|
284
|
+
},
|
|
285
|
+
});
|
|
286
|
+
expect(last.status).toMatchObject({
|
|
287
|
+
error: expect.not.objectContaining({ severity: expect.anything() }),
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
});
|
|
@@ -364,9 +364,23 @@ const handleErrorChunk = (
|
|
|
364
364
|
message: AssistantMessage,
|
|
365
365
|
chunk: AssistantStreamChunk & { type: "error" },
|
|
366
366
|
): AssistantMessage => {
|
|
367
|
+
const severity =
|
|
368
|
+
chunk.severity === "critical" ||
|
|
369
|
+
chunk.severity === "warning" ||
|
|
370
|
+
chunk.severity === "info"
|
|
371
|
+
? chunk.severity
|
|
372
|
+
: undefined;
|
|
367
373
|
return {
|
|
368
374
|
...message,
|
|
369
|
-
status: {
|
|
375
|
+
status: {
|
|
376
|
+
type: "incomplete",
|
|
377
|
+
reason: "error",
|
|
378
|
+
error: {
|
|
379
|
+
code: chunk.code ?? "unknown",
|
|
380
|
+
message: chunk.error,
|
|
381
|
+
...(severity !== undefined && { severity }),
|
|
382
|
+
},
|
|
383
|
+
},
|
|
370
384
|
};
|
|
371
385
|
};
|
|
372
386
|
|
|
@@ -50,6 +50,26 @@ async function encodeAndDecode(
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
describe("ObjectStream serialization and deserialization", () => {
|
|
53
|
+
it("should discard an unterminated SSE event", async () => {
|
|
54
|
+
const operations: ObjectStreamChunk["operations"] = [
|
|
55
|
+
{ type: "set", path: ["status"], value: "complete" },
|
|
56
|
+
];
|
|
57
|
+
const stream = new ReadableStream<Uint8Array>({
|
|
58
|
+
start(controller) {
|
|
59
|
+
controller.enqueue(
|
|
60
|
+
new TextEncoder().encode(`data: ${JSON.stringify(operations)}\n`),
|
|
61
|
+
);
|
|
62
|
+
controller.close();
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const chunks = await collectChunks(
|
|
67
|
+
stream.pipeThrough(new ObjectStreamDecoder()),
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
expect(chunks).toEqual([]);
|
|
71
|
+
});
|
|
72
|
+
|
|
53
73
|
it("should correctly serialize and deserialize simple objects", async () => {
|
|
54
74
|
// Create an object stream with simple operations
|
|
55
75
|
const stream = createObjectStream({
|