assistant-stream 0.3.13 → 0.3.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/README.md +39 -0
  2. package/dist/core/AssistantStreamChunk.d.ts +2 -0
  3. package/dist/core/AssistantStreamChunk.d.ts.map +1 -1
  4. package/dist/core/accumulators/assistant-message-accumulator.d.ts.map +1 -1
  5. package/dist/core/accumulators/assistant-message-accumulator.js +3 -0
  6. package/dist/core/accumulators/assistant-message-accumulator.js.map +1 -1
  7. package/dist/core/modules/tool-call.d.ts.map +1 -1
  8. package/dist/core/modules/tool-call.js +3 -0
  9. package/dist/core/modules/tool-call.js.map +1 -1
  10. package/dist/core/tool/ToolExecutionStream.d.ts.map +1 -1
  11. package/dist/core/tool/ToolExecutionStream.js +3 -0
  12. package/dist/core/tool/ToolExecutionStream.js.map +1 -1
  13. package/dist/core/tool/ToolResponse.d.ts +3 -0
  14. package/dist/core/tool/ToolResponse.d.ts.map +1 -1
  15. package/dist/core/tool/ToolResponse.js +4 -0
  16. package/dist/core/tool/ToolResponse.js.map +1 -1
  17. package/dist/core/tool/tool-types.d.ts +17 -0
  18. package/dist/core/tool/tool-types.d.ts.map +1 -1
  19. package/dist/core/tool/toolResultStream.d.ts.map +1 -1
  20. package/dist/core/tool/toolResultStream.js +26 -1
  21. package/dist/core/tool/toolResultStream.js.map +1 -1
  22. package/dist/core/utils/types.d.ts +4 -0
  23. package/dist/core/utils/types.d.ts.map +1 -1
  24. package/dist/index.d.ts +1 -1
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js.map +1 -1
  27. package/dist/resumable/ResumableStreamContext.d.ts +27 -0
  28. package/dist/resumable/ResumableStreamContext.d.ts.map +1 -0
  29. package/dist/resumable/ResumableStreamContext.js +121 -0
  30. package/dist/resumable/ResumableStreamContext.js.map +1 -0
  31. package/dist/resumable/constants.d.ts +2 -0
  32. package/dist/resumable/constants.d.ts.map +1 -0
  33. package/dist/resumable/constants.js +2 -0
  34. package/dist/resumable/constants.js.map +1 -0
  35. package/dist/resumable/createResumableAssistantStreamResponse.d.ts +24 -0
  36. package/dist/resumable/createResumableAssistantStreamResponse.d.ts.map +1 -0
  37. package/dist/resumable/createResumableAssistantStreamResponse.js +40 -0
  38. package/dist/resumable/createResumableAssistantStreamResponse.js.map +1 -0
  39. package/dist/resumable/errors.d.ts +7 -0
  40. package/dist/resumable/errors.d.ts.map +1 -0
  41. package/dist/resumable/errors.js +15 -0
  42. package/dist/resumable/errors.js.map +1 -0
  43. package/dist/resumable/index.d.ts +7 -0
  44. package/dist/resumable/index.d.ts.map +1 -0
  45. package/dist/resumable/index.js +5 -0
  46. package/dist/resumable/index.js.map +1 -0
  47. package/dist/resumable/stores/InMemoryResumableStreamStore.d.ts +13 -0
  48. package/dist/resumable/stores/InMemoryResumableStreamStore.d.ts.map +1 -0
  49. package/dist/resumable/stores/InMemoryResumableStreamStore.js +199 -0
  50. package/dist/resumable/stores/InMemoryResumableStreamStore.js.map +1 -0
  51. package/dist/resumable/stores/ioredis.d.ts +10 -0
  52. package/dist/resumable/stores/ioredis.d.ts.map +1 -0
  53. package/dist/resumable/stores/ioredis.js +95 -0
  54. package/dist/resumable/stores/ioredis.js.map +1 -0
  55. package/dist/resumable/stores/redis-impl.d.ts +60 -0
  56. package/dist/resumable/stores/redis-impl.d.ts.map +1 -0
  57. package/dist/resumable/stores/redis-impl.js +198 -0
  58. package/dist/resumable/stores/redis-impl.js.map +1 -0
  59. package/dist/resumable/stores/redis.d.ts +39 -0
  60. package/dist/resumable/stores/redis.d.ts.map +1 -0
  61. package/dist/resumable/stores/redis.js +113 -0
  62. package/dist/resumable/stores/redis.js.map +1 -0
  63. package/dist/resumable/types.d.ts +30 -0
  64. package/dist/resumable/types.d.ts.map +1 -0
  65. package/dist/resumable/types.js +2 -0
  66. package/dist/resumable/types.js.map +1 -0
  67. package/package.json +28 -3
  68. package/src/core/AssistantStreamChunk.ts +2 -0
  69. package/src/core/accumulators/assistant-message-accumulator.ts +3 -0
  70. package/src/core/modules/tool-call.ts +3 -0
  71. package/src/core/tool/ToolExecutionStream.ts +3 -0
  72. package/src/core/tool/ToolResponse.ts +6 -0
  73. package/src/core/tool/tool-types.ts +23 -0
  74. package/src/core/tool/toolResultStream.test.ts +360 -2
  75. package/src/core/tool/toolResultStream.ts +30 -1
  76. package/src/core/utils/types.ts +4 -0
  77. package/src/index.ts +5 -1
  78. package/src/resumable/ResumableStreamContext.test.ts +274 -0
  79. package/src/resumable/ResumableStreamContext.ts +187 -0
  80. package/src/resumable/__tests__/integration.test.ts +159 -0
  81. package/src/resumable/constants.ts +1 -0
  82. package/src/resumable/createResumableAssistantStreamResponse.test.ts +243 -0
  83. package/src/resumable/createResumableAssistantStreamResponse.ts +80 -0
  84. package/src/resumable/errors.ts +26 -0
  85. package/src/resumable/index.ts +36 -0
  86. package/src/resumable/stores/InMemoryResumableStreamStore.test.ts +285 -0
  87. package/src/resumable/stores/InMemoryResumableStreamStore.ts +237 -0
  88. package/src/resumable/stores/ioredis.ts +123 -0
  89. package/src/resumable/stores/redis-impl.ts +304 -0
  90. package/src/resumable/stores/redis.test.ts +265 -0
  91. package/src/resumable/stores/redis.ts +171 -0
  92. package/src/resumable/types.ts +49 -0
@@ -0,0 +1,60 @@
1
+ import type { ResumableStreamAcquireOptions, ResumableStreamEntry, ResumableStreamRole, ResumableStreamStatus, ResumableStreamStore } from "../types.js";
2
+ export type PipelineCommand = {
3
+ readonly type: "xAdd";
4
+ readonly key: string;
5
+ readonly fields: Record<string, string | Uint8Array>;
6
+ } | {
7
+ readonly type: "expire";
8
+ readonly key: string;
9
+ readonly ttlSec: number;
10
+ } | {
11
+ readonly type: "set";
12
+ readonly key: string;
13
+ readonly value: string;
14
+ readonly ttlSec: number;
15
+ };
16
+ /**
17
+ * Structural Redis-client interface. The bundled `redis` and `ioredis`
18
+ * adapters wrap their respective clients to satisfy it; custom or proxied
19
+ * clients can implement it directly.
20
+ */
21
+ export interface RedisLikeClient {
22
+ setNX(key: string, value: string, ttlSec: number): Promise<boolean>;
23
+ set(key: string, value: string, ttlSec: number): Promise<void>;
24
+ get(key: string): Promise<string | null>;
25
+ expire(key: string, ttlSec: number): Promise<void>;
26
+ exists(key: string): Promise<boolean>;
27
+ del(keys: string[]): Promise<void>;
28
+ xAdd(key: string, fields: Record<string, string | Uint8Array>): Promise<string>;
29
+ xRange(key: string, start: string, end: string): Promise<Array<{
30
+ id: string;
31
+ fields: Record<string, string | Uint8Array>;
32
+ }>>;
33
+ /** Executes the commands as a single pipeline batch (one round trip). */
34
+ pipeline(commands: readonly PipelineCommand[]): Promise<void>;
35
+ }
36
+ export type RedisResumableStreamStoreOptions = {
37
+ readonly keyPrefix?: string;
38
+ readonly defaultTtlMs?: number;
39
+ /** Defaults to 100ms. Lower values reduce read latency, raise traffic. */
40
+ readonly pollIntervalMs?: number;
41
+ readonly maxChunkBytes?: number;
42
+ };
43
+ export declare class RedisResumableStreamStore implements ResumableStreamStore {
44
+ private readonly client;
45
+ private readonly keyPrefix;
46
+ private readonly defaultTtlMs;
47
+ private readonly pollIntervalMs;
48
+ private readonly maxChunkBytes;
49
+ constructor(client: RedisLikeClient, options?: RedisResumableStreamStoreOptions);
50
+ acquire(streamId: string, options?: ResumableStreamAcquireOptions): Promise<ResumableStreamRole>;
51
+ append(streamId: string, chunk: Uint8Array): Promise<void>;
52
+ finalize(streamId: string, status: "done" | "error", error?: string): Promise<void>;
53
+ read(streamId: string, cursor: string, signal: AbortSignal): AsyncIterable<ResumableStreamEntry>;
54
+ status(streamId: string): Promise<ResumableStreamStatus>;
55
+ delete(streamId: string): Promise<void>;
56
+ private readMeta;
57
+ private metaKey;
58
+ private dataKey;
59
+ }
60
+ //# sourceMappingURL=redis-impl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis-impl.d.ts","sourceRoot":"","sources":["../../../src/resumable/stores/redis-impl.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,198 @@
1
+ import { DEFAULT_TTL_MS } from "../constants.js";
2
+ import { ResumableStreamError, validateStreamId } from "../errors.js";
3
+ const DEFAULT_POLL_INTERVAL_MS = 100;
4
+ const DEFAULT_KEY_PREFIX = "aui:resumable";
5
+ const FIELD_CHUNK = "c";
6
+ const FIELD_FIN = "fin";
7
+ const FIELD_ERROR = "error";
8
+ const FIN_DONE = "done";
9
+ const FIN_ERROR = "error";
10
+ const STREAM_START_ID = "0-0";
11
+ export class RedisResumableStreamStore {
12
+ client;
13
+ keyPrefix;
14
+ defaultTtlMs;
15
+ pollIntervalMs;
16
+ maxChunkBytes;
17
+ constructor(client, options = {}) {
18
+ this.client = client;
19
+ this.keyPrefix = options.keyPrefix ?? DEFAULT_KEY_PREFIX;
20
+ this.defaultTtlMs = options.defaultTtlMs ?? DEFAULT_TTL_MS;
21
+ this.pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
22
+ this.maxChunkBytes = options.maxChunkBytes;
23
+ }
24
+ async acquire(streamId, options) {
25
+ validateStreamId(streamId);
26
+ const ttlSec = msToSec(options?.ttlMs ?? this.defaultTtlMs);
27
+ const meta = JSON.stringify({ status: "streaming", ttlSec });
28
+ const acquired = await this.client.setNX(this.metaKey(streamId), meta, ttlSec);
29
+ if (acquired) {
30
+ // a prior producer's data key may outlive its expired meta key.
31
+ await this.client.del([this.dataKey(streamId)]);
32
+ return "producer";
33
+ }
34
+ return "consumer";
35
+ }
36
+ async append(streamId, chunk) {
37
+ validateStreamId(streamId);
38
+ if (this.maxChunkBytes !== undefined &&
39
+ chunk.byteLength > this.maxChunkBytes) {
40
+ throw new Error(`Chunk exceeds maxChunkBytes (${chunk.byteLength} > ${this.maxChunkBytes})`);
41
+ }
42
+ const dataKey = this.dataKey(streamId);
43
+ const metaKey = this.metaKey(streamId);
44
+ const meta = await this.readMeta(streamId);
45
+ if (!meta) {
46
+ throw new Error(`Stream not found: ${streamId}`);
47
+ }
48
+ if (meta.status !== "streaming") {
49
+ throw new ResumableStreamError("finalized", `Stream already finalized: ${streamId}`);
50
+ }
51
+ const ttlSec = meta.ttlSec ?? msToSec(this.defaultTtlMs);
52
+ await this.client.pipeline([
53
+ { type: "xAdd", key: dataKey, fields: { [FIELD_CHUNK]: chunk } },
54
+ { type: "expire", key: dataKey, ttlSec },
55
+ { type: "expire", key: metaKey, ttlSec },
56
+ ]);
57
+ }
58
+ async finalize(streamId, status, error) {
59
+ validateStreamId(streamId);
60
+ const dataKey = this.dataKey(streamId);
61
+ const metaKey = this.metaKey(streamId);
62
+ const existing = await this.readMeta(streamId);
63
+ if (!existing) {
64
+ throw new Error(`Stream not found: ${streamId}`);
65
+ }
66
+ // a second finalize must not append a duplicate FIN entry.
67
+ if (existing.status !== "streaming")
68
+ return;
69
+ const ttlSec = existing.ttlSec ?? msToSec(this.defaultTtlMs);
70
+ const meta = JSON.stringify(status === "error"
71
+ ? { status: "error", error: error ?? "Stream errored", ttlSec }
72
+ : { status: "done", ttlSec });
73
+ const fields = {
74
+ [FIELD_FIN]: status === "error" ? FIN_ERROR : FIN_DONE,
75
+ };
76
+ if (status === "error") {
77
+ fields[FIELD_ERROR] = error ?? "Stream errored";
78
+ }
79
+ await this.client.pipeline([
80
+ { type: "set", key: metaKey, value: meta, ttlSec },
81
+ { type: "xAdd", key: dataKey, fields },
82
+ { type: "expire", key: dataKey, ttlSec },
83
+ ]);
84
+ }
85
+ async *read(streamId, cursor, signal) {
86
+ validateStreamId(streamId);
87
+ const dataKey = this.dataKey(streamId);
88
+ const metaKey = this.metaKey(streamId);
89
+ const initialMeta = await this.client.get(metaKey);
90
+ if (initialMeta === null) {
91
+ throw new Error(`Stream not found: ${streamId}`);
92
+ }
93
+ let lastId = cursor === "" ? STREAM_START_ID : cursor;
94
+ while (true) {
95
+ if (signal.aborted)
96
+ return;
97
+ const start = lastId === STREAM_START_ID ? "-" : `(${lastId}`;
98
+ const entries = await this.client.xRange(dataKey, start, "+");
99
+ for (const entry of entries) {
100
+ if (signal.aborted)
101
+ return;
102
+ lastId = entry.id;
103
+ const fin = readString(entry.fields[FIELD_FIN]);
104
+ if (fin === FIN_DONE)
105
+ return;
106
+ if (fin === FIN_ERROR) {
107
+ throw new Error(readString(entry.fields[FIELD_ERROR]) ?? "Stream errored");
108
+ }
109
+ const raw = entry.fields[FIELD_CHUNK];
110
+ if (raw === undefined)
111
+ continue;
112
+ yield { cursor: entry.id, chunk: toBytes(raw) };
113
+ }
114
+ if (entries.length > 0)
115
+ continue;
116
+ const stillExists = await this.client.exists(metaKey);
117
+ if (!stillExists)
118
+ return;
119
+ await sleep(this.pollIntervalMs, signal);
120
+ }
121
+ }
122
+ async status(streamId) {
123
+ validateStreamId(streamId);
124
+ const meta = await this.client.get(this.metaKey(streamId));
125
+ if (meta === null)
126
+ return "missing";
127
+ const parsed = parseMeta(meta);
128
+ if (parsed?.status === "streaming")
129
+ return "streaming";
130
+ if (parsed?.status === "done")
131
+ return "done";
132
+ if (parsed?.status === "error")
133
+ return "error";
134
+ return "missing";
135
+ }
136
+ async delete(streamId) {
137
+ validateStreamId(streamId);
138
+ await this.client.del([this.metaKey(streamId), this.dataKey(streamId)]);
139
+ }
140
+ async readMeta(streamId) {
141
+ const raw = await this.client.get(this.metaKey(streamId));
142
+ if (raw === null)
143
+ return undefined;
144
+ return parseMeta(raw);
145
+ }
146
+ // {streamId} is a Redis Cluster hash tag so both keys live on the same
147
+ // shard; multi-key DEL and same-stream pipelines stay single-slot.
148
+ metaKey(streamId) {
149
+ return `${this.keyPrefix}:{${streamId}}:meta`;
150
+ }
151
+ dataKey(streamId) {
152
+ return `${this.keyPrefix}:{${streamId}}:data`;
153
+ }
154
+ }
155
+ function parseMeta(value) {
156
+ try {
157
+ const parsed = JSON.parse(value);
158
+ return parsed && typeof parsed === "object" ? parsed : undefined;
159
+ }
160
+ catch {
161
+ return undefined;
162
+ }
163
+ }
164
+ function msToSec(ms) {
165
+ return Math.max(1, Math.ceil(ms / 1000));
166
+ }
167
+ function sleep(ms, signal) {
168
+ return new Promise((resolve) => {
169
+ if (signal.aborted) {
170
+ resolve();
171
+ return;
172
+ }
173
+ const timer = setTimeout(() => {
174
+ signal.removeEventListener("abort", onAbort);
175
+ resolve();
176
+ }, ms);
177
+ const onAbort = () => {
178
+ clearTimeout(timer);
179
+ resolve();
180
+ };
181
+ signal.addEventListener("abort", onAbort, { once: true });
182
+ });
183
+ }
184
+ const SHARED_DECODER = new TextDecoder();
185
+ const SHARED_ENCODER = new TextEncoder();
186
+ function readString(value) {
187
+ if (value === undefined)
188
+ return undefined;
189
+ if (typeof value === "string")
190
+ return value;
191
+ return SHARED_DECODER.decode(value);
192
+ }
193
+ function toBytes(value) {
194
+ if (value instanceof Uint8Array)
195
+ return value;
196
+ return SHARED_ENCODER.encode(value);
197
+ }
198
+ //# sourceMappingURL=redis-impl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis-impl.js","sourceRoot":"","sources":["../../../src/resumable/stores/redis-impl.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import { type RedisResumableStreamStoreOptions } from "./redis-impl.js";
2
+ import type { ResumableStreamStore } from "../types.js";
3
+ type NodeRedisFields = Record<string, string | Buffer>;
4
+ interface NodeRedisMultiCommand {
5
+ xAdd(key: string, id: string, fields: NodeRedisFields): NodeRedisMultiCommand;
6
+ expire(key: string, seconds: number): NodeRedisMultiCommand;
7
+ set(key: string, value: string, options: {
8
+ EX: number;
9
+ }): NodeRedisMultiCommand;
10
+ execAsPipeline(): Promise<unknown>;
11
+ exec(): Promise<unknown>;
12
+ }
13
+ /** Structural subset of node-redis v5 used by the adapter. */
14
+ export interface NodeRedisLike {
15
+ set(key: string, value: string, options: {
16
+ NX: true;
17
+ EX: number;
18
+ }): Promise<string | null>;
19
+ set(key: string, value: string, options: {
20
+ EX: number;
21
+ }): Promise<string | null>;
22
+ get(key: string): Promise<string | null>;
23
+ expire(key: string, seconds: number): Promise<unknown>;
24
+ exists(key: string): Promise<number>;
25
+ del(keys: string | string[]): Promise<unknown>;
26
+ xAdd(key: string, id: string, fields: NodeRedisFields): Promise<string>;
27
+ sendCommand<T = unknown>(args: ReadonlyArray<string | Buffer>, options?: {
28
+ typeMapping?: Record<number, unknown>;
29
+ }): Promise<T>;
30
+ multi(): NodeRedisMultiCommand;
31
+ }
32
+ /**
33
+ * Resumable stream store backed by [`redis`](https://www.npmjs.com/package/redis)
34
+ * v5. Expects a connected client; cluster routing relies on the shared
35
+ * `{streamId}` hash tag baked into the key scheme.
36
+ */
37
+ export declare function createRedisResumableStreamStore(client: NodeRedisLike, options?: RedisResumableStreamStoreOptions): ResumableStreamStore;
38
+ export {};
39
+ //# sourceMappingURL=redis.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis.d.ts","sourceRoot":"","sources":["../../../src/resumable/stores/redis.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,gCAAgC,EACtC,wBAAqB;AACtB,OAAO,KAAK,EAAE,oBAAoB,EAAE,oBAAiB;AAIrD,KAAK,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;AAEvD,UAAU,qBAAqB;IAC7B,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,qBAAqB,CAAC;IAC9E,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,qBAAqB,CAAC;IAC5D,GAAG,CACD,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,GACtB,qBAAqB,CAAC;IACzB,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CAC1B;AAED,8DAA8D;AAC9D,MAAM,WAAW,aAAa;IAC5B,GAAG,CACD,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,OAAO,EAAE;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,GAChC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC1B,GAAG,CACD,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,GACtB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC1B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACzC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACvD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACrC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/C,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACxE,WAAW,CAAC,CAAC,GAAG,OAAO,EACrB,IAAI,EAAE,aAAa,CAAC,MAAM,GAAG,MAAM,CAAC,EACpC,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,GAClD,OAAO,CAAC,CAAC,CAAC,CAAC;IACd,KAAK,IAAI,qBAAqB,CAAC;CAChC;AAED;;;;GAIG;AACH,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,aAAa,EACrB,OAAO,CAAC,EAAE,gCAAgC,GACzC,oBAAoB,CAEtB"}
@@ -0,0 +1,113 @@
1
+ import { RedisResumableStreamStore, } from "./redis-impl.js";
2
+ const RESP_BLOB_STRING = 36;
3
+ /**
4
+ * Resumable stream store backed by [`redis`](https://www.npmjs.com/package/redis)
5
+ * v5. Expects a connected client; cluster routing relies on the shared
6
+ * `{streamId}` hash tag baked into the key scheme.
7
+ */
8
+ export function createRedisResumableStreamStore(client, options) {
9
+ return new RedisResumableStreamStore(adapt(client), options);
10
+ }
11
+ function adapt(client) {
12
+ return {
13
+ async setNX(key, value, ttlSec) {
14
+ const result = await client.set(key, value, { NX: true, EX: ttlSec });
15
+ return result === "OK";
16
+ },
17
+ async set(key, value, ttlSec) {
18
+ await client.set(key, value, { EX: ttlSec });
19
+ },
20
+ async get(key) {
21
+ return client.get(key);
22
+ },
23
+ async expire(key, ttlSec) {
24
+ await client.expire(key, ttlSec);
25
+ },
26
+ async exists(key) {
27
+ const result = await client.exists(key);
28
+ return result > 0;
29
+ },
30
+ async del(keys) {
31
+ if (keys.length === 0)
32
+ return;
33
+ await client.del(keys.length === 1 ? keys[0] : keys);
34
+ },
35
+ async xAdd(key, fields) {
36
+ return client.xAdd(key, "*", toNodeFields(fields));
37
+ },
38
+ async xRange(key, start, end) {
39
+ const reply = await client.sendCommand(["XRANGE", key, start, end], { typeMapping: { [RESP_BLOB_STRING]: Buffer } });
40
+ return parseXRangeReply(reply);
41
+ },
42
+ async pipeline(commands) {
43
+ if (commands.length === 0)
44
+ return;
45
+ let chain = client.multi();
46
+ for (const cmd of commands) {
47
+ chain = applyPipelineCommand(chain, cmd);
48
+ }
49
+ await chain.execAsPipeline();
50
+ },
51
+ };
52
+ }
53
+ function applyPipelineCommand(chain, cmd) {
54
+ switch (cmd.type) {
55
+ case "xAdd":
56
+ return chain.xAdd(cmd.key, "*", toNodeFields(cmd.fields));
57
+ case "expire":
58
+ return chain.expire(cmd.key, cmd.ttlSec);
59
+ case "set":
60
+ return chain.set(cmd.key, cmd.value, { EX: cmd.ttlSec });
61
+ }
62
+ }
63
+ function toNodeFields(fields) {
64
+ const out = {};
65
+ for (const [k, v] of Object.entries(fields)) {
66
+ out[k] = typeof v === "string" ? v : toBuffer(v);
67
+ }
68
+ return out;
69
+ }
70
+ function toBuffer(bytes) {
71
+ if (Buffer.isBuffer(bytes))
72
+ return bytes;
73
+ return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength);
74
+ }
75
+ function parseXRangeReply(reply) {
76
+ if (!Array.isArray(reply))
77
+ return [];
78
+ const out = [];
79
+ for (const entry of reply) {
80
+ if (!Array.isArray(entry) || entry.length < 2)
81
+ continue;
82
+ const [rawId, rawFields] = entry;
83
+ const id = bufferOrStringToString(rawId);
84
+ if (id === undefined || !Array.isArray(rawFields))
85
+ continue;
86
+ const fields = {};
87
+ for (let i = 0; i + 1 < rawFields.length; i += 2) {
88
+ const fieldKey = bufferOrStringToString(rawFields[i]);
89
+ const fieldValue = rawFields[i + 1];
90
+ if (fieldKey === undefined || fieldValue === undefined)
91
+ continue;
92
+ fields[fieldKey] = bufferOrStringToBytes(fieldValue);
93
+ }
94
+ out.push({ id, fields });
95
+ }
96
+ return out;
97
+ }
98
+ function bufferOrStringToString(value) {
99
+ if (typeof value === "string")
100
+ return value;
101
+ if (Buffer.isBuffer(value))
102
+ return value.toString("utf8");
103
+ return undefined;
104
+ }
105
+ function bufferOrStringToBytes(value) {
106
+ if (typeof value === "string")
107
+ return value;
108
+ if (Buffer.isBuffer(value)) {
109
+ return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
110
+ }
111
+ return "";
112
+ }
113
+ //# sourceMappingURL=redis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis.js","sourceRoot":"","sources":["../../../src/resumable/stores/redis.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ export type ResumableStreamRole = "producer" | "consumer";
2
+ export type ResumableStreamStatus = "streaming" | "done" | "error" | "missing";
3
+ export type ResumableStreamEntry = {
4
+ readonly cursor: string;
5
+ readonly chunk: Uint8Array;
6
+ };
7
+ export type ResumableStreamAcquireOptions = {
8
+ readonly ttlMs?: number;
9
+ };
10
+ export interface ResumableStreamStore {
11
+ /**
12
+ * Atomic election. The first caller for a given `streamId` observes
13
+ * `"producer"`; every later caller observes `"consumer"`, including those
14
+ * arriving after `finalize`.
15
+ */
16
+ acquire(streamId: string, options?: ResumableStreamAcquireOptions): Promise<ResumableStreamRole>;
17
+ /** Implementations should refresh the TTL on each call. */
18
+ append(streamId: string, chunk: Uint8Array): Promise<void>;
19
+ finalize(streamId: string, status: "done" | "error", error?: string): Promise<void>;
20
+ /**
21
+ * Yields persisted entries strictly after `cursor` (`""` starts from the
22
+ * beginning), then waits for new ones until the stream is finalized.
23
+ * Aborting `signal` resolves the iterable without throwing.
24
+ */
25
+ read(streamId: string, cursor: string, signal: AbortSignal): AsyncIterable<ResumableStreamEntry>;
26
+ status(streamId: string): Promise<ResumableStreamStatus>;
27
+ /** Active readers terminate. No-op when the stream does not exist. */
28
+ delete(streamId: string): Promise<void>;
29
+ }
30
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/resumable/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,mBAAmB,GAAG,UAAU,GAAG,UAAU,CAAC;AAE1D,MAAM,MAAM,qBAAqB,GAAG,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;AAE/E,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG;IAC1C,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,OAAO,CACL,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,6BAA6B,GACtC,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAEhC,2DAA2D;IAC3D,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE3D,QAAQ,CACN,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GAAG,OAAO,EACxB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB;;;;OAIG;IACH,IAAI,CACF,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,WAAW,GAClB,aAAa,CAAC,oBAAoB,CAAC,CAAC;IAEvC,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAEzD,sEAAsE;IACtE,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/resumable/types.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "assistant-stream",
3
- "version": "0.3.13",
3
+ "version": "0.3.14",
4
4
  "description": "Streaming utilities for AI assistants",
5
5
  "keywords": [
6
6
  "ai",
@@ -22,6 +22,18 @@
22
22
  "./utils": {
23
23
  "types": "./dist/utils.d.ts",
24
24
  "default": "./dist/utils.js"
25
+ },
26
+ "./resumable": {
27
+ "types": "./dist/resumable/index.d.ts",
28
+ "default": "./dist/resumable/index.js"
29
+ },
30
+ "./resumable/redis": {
31
+ "types": "./dist/resumable/stores/redis.d.ts",
32
+ "default": "./dist/resumable/stores/redis.js"
33
+ },
34
+ "./resumable/ioredis": {
35
+ "types": "./dist/resumable/stores/ioredis.d.ts",
36
+ "default": "./dist/resumable/stores/ioredis.js"
25
37
  }
26
38
  },
27
39
  "main": "./dist/index.js",
@@ -38,14 +50,27 @@
38
50
  "nanoid": "^5.1.11",
39
51
  "secure-json-parse": "^4.1.0"
40
52
  },
53
+ "peerDependencies": {
54
+ "ioredis": "^5.10.1",
55
+ "redis": "^5.12.1"
56
+ },
57
+ "peerDependenciesMeta": {
58
+ "ioredis": {
59
+ "optional": true
60
+ },
61
+ "redis": {
62
+ "optional": true
63
+ }
64
+ },
41
65
  "devDependencies": {
42
66
  "@types/json-schema": "^7.0.15",
67
+ "ioredis": "^5.10.1",
68
+ "redis": "^5.12.1",
43
69
  "vitest": "^4.1.5",
44
70
  "@assistant-ui/x-buildutils": "0.0.7"
45
71
  },
46
72
  "publishConfig": {
47
- "access": "public",
48
- "provenance": true
73
+ "access": "public"
49
74
  },
50
75
  "homepage": "https://www.assistant-ui.com/",
51
76
  "repository": {
@@ -1,5 +1,6 @@
1
1
  import type { ReadonlyJSONValue } from "../utils/json/json-value";
2
2
  import type { ObjectStreamOperation } from "./object/types";
3
+ import type { ToolModelContentPart } from "./tool/tool-types";
3
4
 
4
5
  export type PartInit =
5
6
  | {
@@ -96,6 +97,7 @@ export type AssistantStreamChunk = { readonly path: readonly number[] } & (
96
97
  readonly artifact?: ReadonlyJSONValue;
97
98
  readonly result: ReadonlyJSONValue;
98
99
  readonly isError: boolean;
100
+ readonly modelContent?: readonly ToolModelContentPart[];
99
101
  readonly messages?: ReadonlyJSONValue;
100
102
  }
101
103
  | {
@@ -216,6 +216,9 @@ const handleResult = (
216
216
  ...(chunk.artifact !== undefined ? { artifact: chunk.artifact } : {}),
217
217
  result: chunk.result,
218
218
  isError: chunk.isError ?? false,
219
+ ...(chunk.modelContent !== undefined
220
+ ? { modelContent: chunk.modelContent }
221
+ : {}),
219
222
  ...(chunk.messages !== undefined ? { messages: chunk.messages } : {}),
220
223
  status: { type: "complete", reason: "stop" },
221
224
  };
@@ -76,6 +76,9 @@ class ToolCallStreamControllerImpl implements ToolCallStreamController {
76
76
  : {}),
77
77
  result: response.result,
78
78
  isError: response.isError ?? false,
79
+ ...(response.modelContent !== undefined
80
+ ? { modelContent: response.modelContent }
81
+ : {}),
79
82
  ...(response.messages !== undefined
80
83
  ? { messages: response.messages }
81
84
  : {}),
@@ -104,6 +104,7 @@ export class ToolExecutionStream extends PipeableTransformStream<
104
104
  result: chunk.result,
105
105
  artifact: chunk.artifact,
106
106
  isError: chunk.isError,
107
+ modelContent: chunk.modelContent,
107
108
  }),
108
109
  );
109
110
  break;
@@ -156,6 +157,8 @@ export class ToolExecutionStream extends PipeableTransformStream<
156
157
  artifact: c.artifact,
157
158
  result: c.result,
158
159
  isError: c.isError,
160
+ messages: c.messages,
161
+ modelContent: c.modelContent,
159
162
  });
160
163
  streamController.setResponse(result);
161
164
  controller.enqueue({
@@ -1,4 +1,5 @@
1
1
  import type { ReadonlyJSONValue } from "../../utils/json/json-value";
2
+ import type { ToolModelContentPart } from "./tool-types";
2
3
 
3
4
  const TOOL_RESPONSE_SYMBOL = Symbol.for("aui.tool-response");
4
5
 
@@ -6,6 +7,7 @@ export type ToolResponseLike<TResult> = {
6
7
  result: TResult;
7
8
  artifact?: ReadonlyJSONValue | undefined;
8
9
  isError?: boolean | undefined;
10
+ modelContent?: readonly ToolModelContentPart[] | undefined;
9
11
  messages?: ReadonlyJSONValue | undefined;
10
12
  };
11
13
 
@@ -17,6 +19,7 @@ export class ToolResponse<TResult> {
17
19
  readonly artifact?: ReadonlyJSONValue;
18
20
  readonly result: TResult;
19
21
  readonly isError: boolean;
22
+ readonly modelContent?: readonly ToolModelContentPart[];
20
23
  readonly messages?: ReadonlyJSONValue;
21
24
 
22
25
  constructor(options: ToolResponseLike<TResult>) {
@@ -25,6 +28,9 @@ export class ToolResponse<TResult> {
25
28
  }
26
29
  this.result = options.result;
27
30
  this.isError = options.isError ?? false;
31
+ if (options.modelContent !== undefined) {
32
+ this.modelContent = options.modelContent;
33
+ }
28
34
  if (options.messages !== undefined) {
29
35
  this.messages = options.messages;
30
36
  }