assistant-stream 0.3.13 → 0.3.15
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 +39 -0
- package/dist/core/AssistantStream.d.ts +37 -0
- package/dist/core/AssistantStream.d.ts.map +1 -1
- package/dist/core/AssistantStream.js +22 -0
- package/dist/core/AssistantStream.js.map +1 -1
- package/dist/core/AssistantStreamChunk.d.ts +32 -0
- package/dist/core/AssistantStreamChunk.d.ts.map +1 -1
- package/dist/core/accumulators/assistant-message-accumulator.d.ts.map +1 -1
- package/dist/core/accumulators/assistant-message-accumulator.js +3 -0
- package/dist/core/accumulators/assistant-message-accumulator.js.map +1 -1
- package/dist/core/modules/assistant-stream.d.ts +68 -0
- package/dist/core/modules/assistant-stream.d.ts.map +1 -1
- package/dist/core/modules/assistant-stream.js +23 -0
- 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 -0
- package/dist/core/modules/tool-call.js.map +1 -1
- package/dist/core/tool/ToolExecutionStream.d.ts.map +1 -1
- package/dist/core/tool/ToolExecutionStream.js +3 -0
- package/dist/core/tool/ToolExecutionStream.js.map +1 -1
- package/dist/core/tool/ToolResponse.d.ts +44 -0
- package/dist/core/tool/ToolResponse.d.ts.map +1 -1
- package/dist/core/tool/ToolResponse.js +27 -0
- package/dist/core/tool/ToolResponse.js.map +1 -1
- package/dist/core/tool/tool-types.d.ts +119 -2
- package/dist/core/tool/tool-types.d.ts.map +1 -1
- package/dist/core/tool/toolResultStream.d.ts +15 -0
- package/dist/core/tool/toolResultStream.d.ts.map +1 -1
- package/dist/core/tool/toolResultStream.js +39 -1
- package/dist/core/tool/toolResultStream.js.map +1 -1
- package/dist/core/utils/types.d.ts +4 -0
- package/dist/core/utils/types.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/resumable/ResumableStreamContext.d.ts +27 -0
- package/dist/resumable/ResumableStreamContext.d.ts.map +1 -0
- package/dist/resumable/ResumableStreamContext.js +121 -0
- package/dist/resumable/ResumableStreamContext.js.map +1 -0
- package/dist/resumable/constants.d.ts +2 -0
- package/dist/resumable/constants.d.ts.map +1 -0
- package/dist/resumable/constants.js +2 -0
- package/dist/resumable/constants.js.map +1 -0
- package/dist/resumable/createResumableAssistantStreamResponse.d.ts +24 -0
- package/dist/resumable/createResumableAssistantStreamResponse.d.ts.map +1 -0
- package/dist/resumable/createResumableAssistantStreamResponse.js +40 -0
- package/dist/resumable/createResumableAssistantStreamResponse.js.map +1 -0
- package/dist/resumable/errors.d.ts +7 -0
- package/dist/resumable/errors.d.ts.map +1 -0
- package/dist/resumable/errors.js +15 -0
- package/dist/resumable/errors.js.map +1 -0
- package/dist/resumable/index.d.ts +7 -0
- package/dist/resumable/index.d.ts.map +1 -0
- package/dist/resumable/index.js +5 -0
- package/dist/resumable/index.js.map +1 -0
- package/dist/resumable/stores/InMemoryResumableStreamStore.d.ts +13 -0
- package/dist/resumable/stores/InMemoryResumableStreamStore.d.ts.map +1 -0
- package/dist/resumable/stores/InMemoryResumableStreamStore.js +199 -0
- package/dist/resumable/stores/InMemoryResumableStreamStore.js.map +1 -0
- package/dist/resumable/stores/ioredis.d.ts +10 -0
- package/dist/resumable/stores/ioredis.d.ts.map +1 -0
- package/dist/resumable/stores/ioredis.js +95 -0
- package/dist/resumable/stores/ioredis.js.map +1 -0
- package/dist/resumable/stores/redis-impl.d.ts +60 -0
- package/dist/resumable/stores/redis-impl.d.ts.map +1 -0
- package/dist/resumable/stores/redis-impl.js +198 -0
- package/dist/resumable/stores/redis-impl.js.map +1 -0
- package/dist/resumable/stores/redis.d.ts +39 -0
- package/dist/resumable/stores/redis.d.ts.map +1 -0
- package/dist/resumable/stores/redis.js +113 -0
- package/dist/resumable/stores/redis.js.map +1 -0
- package/dist/resumable/types.d.ts +30 -0
- package/dist/resumable/types.d.ts.map +1 -0
- package/dist/resumable/types.js +2 -0
- package/dist/resumable/types.js.map +1 -0
- package/package.json +28 -2
- package/src/core/AssistantStream.ts +37 -0
- package/src/core/AssistantStreamChunk.ts +32 -0
- package/src/core/accumulators/assistant-message-accumulator.ts +3 -0
- package/src/core/modules/assistant-stream.ts +68 -0
- package/src/core/modules/tool-call.ts +3 -0
- package/src/core/tool/ToolExecutionStream.ts +3 -0
- package/src/core/tool/ToolResponse.ts +50 -0
- package/src/core/tool/tool-types.ts +125 -2
- package/src/core/tool/toolResultStream.test.ts +360 -2
- package/src/core/tool/toolResultStream.ts +45 -1
- package/src/core/utils/types.ts +4 -0
- package/src/index.ts +5 -1
- package/src/resumable/ResumableStreamContext.test.ts +274 -0
- package/src/resumable/ResumableStreamContext.ts +187 -0
- package/src/resumable/__tests__/integration.test.ts +159 -0
- package/src/resumable/constants.ts +1 -0
- package/src/resumable/createResumableAssistantStreamResponse.test.ts +243 -0
- package/src/resumable/createResumableAssistantStreamResponse.ts +80 -0
- package/src/resumable/errors.ts +26 -0
- package/src/resumable/index.ts +36 -0
- package/src/resumable/stores/InMemoryResumableStreamStore.test.ts +285 -0
- package/src/resumable/stores/InMemoryResumableStreamStore.ts +237 -0
- package/src/resumable/stores/ioredis.ts +123 -0
- package/src/resumable/stores/redis-impl.ts +304 -0
- package/src/resumable/stores/redis.test.ts +265 -0
- package/src/resumable/stores/redis.ts +171 -0
- 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 @@
|
|
|
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.
|
|
3
|
+
"version": "0.3.15",
|
|
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,10 +50,24 @@
|
|
|
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
|
-
"@assistant-ui/x-buildutils": "0.0.
|
|
70
|
+
"@assistant-ui/x-buildutils": "0.0.8"
|
|
45
71
|
},
|
|
46
72
|
"publishConfig": {
|
|
47
73
|
"access": "public",
|
|
@@ -1,7 +1,22 @@
|
|
|
1
1
|
import type { AssistantStreamChunk } from "./AssistantStreamChunk";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Stream of assistant-ui protocol chunks.
|
|
5
|
+
*
|
|
6
|
+
* `AssistantStream` is the normalized internal stream format used by
|
|
7
|
+
* encoders, decoders, accumulators, and tool execution transforms. Use an
|
|
8
|
+
* encoder such as `DataStreamEncoder` when returning it from an HTTP route,
|
|
9
|
+
* and the matching decoder when reading it back from a response.
|
|
10
|
+
*/
|
|
3
11
|
export type AssistantStream = ReadableStream<AssistantStreamChunk>;
|
|
4
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Encoder that converts an {@link AssistantStream} into bytes suitable for an
|
|
15
|
+
* HTTP response body.
|
|
16
|
+
*
|
|
17
|
+
* Encoders may expose response headers, such as content type or protocol
|
|
18
|
+
* markers, through `headers`.
|
|
19
|
+
*/
|
|
5
20
|
export type AssistantStreamEncoder = ReadableWritablePair<
|
|
6
21
|
Uint8Array<ArrayBuffer>,
|
|
7
22
|
AssistantStreamChunk
|
|
@@ -10,12 +25,26 @@ export type AssistantStreamEncoder = ReadableWritablePair<
|
|
|
10
25
|
};
|
|
11
26
|
|
|
12
27
|
export const AssistantStream = {
|
|
28
|
+
/**
|
|
29
|
+
* Converts an {@link AssistantStream} into a `Response` using the supplied
|
|
30
|
+
* encoder.
|
|
31
|
+
*
|
|
32
|
+
* The encoder's `headers` are copied onto the response. Pair this with the
|
|
33
|
+
* decoder for the same wire format when consuming the response.
|
|
34
|
+
*/
|
|
13
35
|
toResponse(stream: AssistantStream, transformer: AssistantStreamEncoder) {
|
|
14
36
|
return new Response(AssistantStream.toByteStream(stream, transformer), {
|
|
15
37
|
headers: transformer.headers ?? {},
|
|
16
38
|
});
|
|
17
39
|
},
|
|
18
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Reads an assistant stream from a `Response` body using the supplied
|
|
43
|
+
* decoder.
|
|
44
|
+
*
|
|
45
|
+
* The response body must be present and encoded with the matching assistant
|
|
46
|
+
* stream wire format.
|
|
47
|
+
*/
|
|
19
48
|
fromResponse(
|
|
20
49
|
response: Response,
|
|
21
50
|
transformer: ReadableWritablePair<
|
|
@@ -26,6 +55,10 @@ export const AssistantStream = {
|
|
|
26
55
|
return AssistantStream.fromByteStream(response.body!, transformer);
|
|
27
56
|
},
|
|
28
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Pipes an {@link AssistantStream} through an encoder and returns the
|
|
60
|
+
* resulting byte stream.
|
|
61
|
+
*/
|
|
29
62
|
toByteStream(
|
|
30
63
|
stream: AssistantStream,
|
|
31
64
|
transformer: ReadableWritablePair<
|
|
@@ -36,6 +69,10 @@ export const AssistantStream = {
|
|
|
36
69
|
return stream.pipeThrough(transformer);
|
|
37
70
|
},
|
|
38
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Pipes a byte stream through a decoder and returns normalized
|
|
74
|
+
* {@link AssistantStreamChunk} values.
|
|
75
|
+
*/
|
|
39
76
|
fromByteStream(
|
|
40
77
|
readable: ReadableStream<Uint8Array<ArrayBuffer>>,
|
|
41
78
|
transformer: ReadableWritablePair<
|