@tashiscool/stream 0.1.0
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 +145 -0
- package/dist/aggregator.d.ts +95 -0
- package/dist/aggregator.d.ts.map +1 -0
- package/dist/aggregator.js +127 -0
- package/dist/aggregator.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/interceptor.d.ts +40 -0
- package/dist/interceptor.d.ts.map +1 -0
- package/dist/interceptor.js +94 -0
- package/dist/interceptor.js.map +1 -0
- package/dist/jsonl.d.ts +59 -0
- package/dist/jsonl.d.ts.map +1 -0
- package/dist/jsonl.js +104 -0
- package/dist/jsonl.js.map +1 -0
- package/dist/mux.d.ts +114 -0
- package/dist/mux.d.ts.map +1 -0
- package/dist/mux.js +310 -0
- package/dist/mux.js.map +1 -0
- package/dist/response.d.ts +101 -0
- package/dist/response.d.ts.map +1 -0
- package/dist/response.js +309 -0
- package/dist/response.js.map +1 -0
- package/package.json +51 -0
package/README.md
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# @llm-utils/stream
|
|
2
|
+
|
|
3
|
+
Streaming utilities for real-time LLM responses. Handle HTTP streaming, chunk aggregation, and JSONL parsing with ease.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @llm-utils/stream
|
|
9
|
+
# or
|
|
10
|
+
npm install @llm-utils/stream
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- **HTTP Response Interception** - Transform streaming responses without breaking semantics
|
|
16
|
+
- **Chunk Aggregation** - Aggregate chunks from multiple sources with lifecycle handlers
|
|
17
|
+
- **JSONL Streaming** - Parse and serialize JSONL streams (server and client)
|
|
18
|
+
- **Type-Safe** - Full TypeScript support with generics
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
### Intercept and Transform Streaming Responses
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { interceptResponseWrites } from '@llm-utils/stream';
|
|
26
|
+
|
|
27
|
+
app.post('/chat', async (req, res) => {
|
|
28
|
+
const llmStream = await callLLM(req.body);
|
|
29
|
+
|
|
30
|
+
interceptResponseWrites(res, {
|
|
31
|
+
transform: async (chunk) => {
|
|
32
|
+
// Transform each chunk before sending to client
|
|
33
|
+
const data = JSON.parse(chunk);
|
|
34
|
+
const enriched = { ...data, timestamp: Date.now() };
|
|
35
|
+
return JSON.stringify(enriched) + '\n';
|
|
36
|
+
},
|
|
37
|
+
onFinish: async () => {
|
|
38
|
+
// Cleanup after stream ends
|
|
39
|
+
await saveConversation();
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
llmStream.pipe(res);
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Aggregate Chunks from Multiple Sources
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { createChunkAggregator } from '@llm-utils/stream';
|
|
51
|
+
|
|
52
|
+
const aggregator = createChunkAggregator({
|
|
53
|
+
onBegin: async (message) => {
|
|
54
|
+
console.log(`Starting message from node: ${message.nodeId}`);
|
|
55
|
+
},
|
|
56
|
+
onItem: async (message, delta) => {
|
|
57
|
+
// Stream delta to client
|
|
58
|
+
sendToClient(delta);
|
|
59
|
+
},
|
|
60
|
+
onEnd: async (message) => {
|
|
61
|
+
// Save complete message
|
|
62
|
+
await saveMessage(message);
|
|
63
|
+
},
|
|
64
|
+
onError: async (message, error) => {
|
|
65
|
+
console.error(`Error in ${message.nodeId}:`, error);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// Ingest structured chunks
|
|
70
|
+
for await (const chunk of structuredStream) {
|
|
71
|
+
await aggregator.ingest(chunk);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Finalize any incomplete messages
|
|
75
|
+
await aggregator.finalizeAll();
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### JSONL Streaming
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import { streamJsonLines, parseJsonLines } from '@llm-utils/stream';
|
|
82
|
+
|
|
83
|
+
// Server: Stream objects as JSONL
|
|
84
|
+
app.get('/events', (req, res) => {
|
|
85
|
+
streamJsonLines(res, async function* () {
|
|
86
|
+
for (const event of events) {
|
|
87
|
+
yield event;
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Client: Parse JSONL stream
|
|
93
|
+
const response = await fetch('/events');
|
|
94
|
+
for await (const event of parseJsonLines(response)) {
|
|
95
|
+
handleEvent(event);
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## API Reference
|
|
100
|
+
|
|
101
|
+
### `interceptResponseWrites(res, options)`
|
|
102
|
+
|
|
103
|
+
Intercepts Express response writes for transformation.
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
interface InterceptOptions {
|
|
107
|
+
transform: (text: string) => Promise<string | undefined>;
|
|
108
|
+
onFinish?: () => Promise<void>;
|
|
109
|
+
onError?: (error: Error) => void;
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### `createChunkAggregator(handlers)`
|
|
114
|
+
|
|
115
|
+
Creates a stateful chunk aggregator for multi-source streaming.
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
interface StructuredChunk {
|
|
119
|
+
nodeId: string;
|
|
120
|
+
runIndex: number;
|
|
121
|
+
itemIndex: number;
|
|
122
|
+
type: 'begin' | 'item' | 'end' | 'error';
|
|
123
|
+
data?: string;
|
|
124
|
+
error?: string;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
interface ChunkHandlers {
|
|
128
|
+
onBegin?: (message: AggregatedMessage) => Promise<void>;
|
|
129
|
+
onItem?: (message: AggregatedMessage, delta: string) => Promise<void>;
|
|
130
|
+
onEnd?: (message: AggregatedMessage) => Promise<void>;
|
|
131
|
+
onError?: (message: AggregatedMessage, error?: string) => Promise<void>;
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### `streamJsonLines(res, generator)`
|
|
136
|
+
|
|
137
|
+
Streams objects as JSONL to an HTTP response.
|
|
138
|
+
|
|
139
|
+
### `parseJsonLines(response)`
|
|
140
|
+
|
|
141
|
+
Parses a JSONL stream from a fetch Response.
|
|
142
|
+
|
|
143
|
+
## License
|
|
144
|
+
|
|
145
|
+
MIT
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chunk Aggregation
|
|
3
|
+
* Aggregate streaming chunks from multiple sources with lifecycle handlers
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* A structured chunk from a streaming source
|
|
7
|
+
*/
|
|
8
|
+
export interface StructuredChunk {
|
|
9
|
+
/** Identifier for the source node */
|
|
10
|
+
nodeId: string;
|
|
11
|
+
/** Run index for parallel executions */
|
|
12
|
+
runIndex: number;
|
|
13
|
+
/** Item index within the run */
|
|
14
|
+
itemIndex: number;
|
|
15
|
+
/** Chunk lifecycle type */
|
|
16
|
+
type: 'begin' | 'item' | 'end' | 'error';
|
|
17
|
+
/** Content data (for 'item' type) */
|
|
18
|
+
data?: string;
|
|
19
|
+
/** Error message (for 'error' type) */
|
|
20
|
+
error?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* An aggregated message from multiple chunks
|
|
24
|
+
*/
|
|
25
|
+
export interface AggregatedMessage {
|
|
26
|
+
/** Source node identifier */
|
|
27
|
+
nodeId: string;
|
|
28
|
+
/** Run index */
|
|
29
|
+
runIndex: number;
|
|
30
|
+
/** Item index */
|
|
31
|
+
itemIndex: number;
|
|
32
|
+
/** Accumulated content */
|
|
33
|
+
content: string;
|
|
34
|
+
/** Current status */
|
|
35
|
+
status: 'streaming' | 'complete' | 'error';
|
|
36
|
+
/** Error message if status is 'error' */
|
|
37
|
+
error?: string;
|
|
38
|
+
/** Timestamp when message started */
|
|
39
|
+
startedAt: Date;
|
|
40
|
+
/** Timestamp when message completed */
|
|
41
|
+
completedAt?: Date;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Handlers for chunk lifecycle events
|
|
45
|
+
*/
|
|
46
|
+
export interface ChunkHandlers {
|
|
47
|
+
/** Called when a new message stream begins */
|
|
48
|
+
onBegin?: (message: AggregatedMessage) => Promise<void>;
|
|
49
|
+
/** Called for each content chunk */
|
|
50
|
+
onItem?: (message: AggregatedMessage, delta: string) => Promise<void>;
|
|
51
|
+
/** Called when a message stream ends successfully */
|
|
52
|
+
onEnd?: (message: AggregatedMessage) => Promise<void>;
|
|
53
|
+
/** Called when a message stream errors */
|
|
54
|
+
onError?: (message: AggregatedMessage, error?: string) => Promise<void>;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Chunk aggregator interface
|
|
58
|
+
*/
|
|
59
|
+
export interface ChunkAggregator {
|
|
60
|
+
/** Ingest a structured chunk */
|
|
61
|
+
ingest: (chunk: StructuredChunk) => Promise<AggregatedMessage>;
|
|
62
|
+
/** Finalize all incomplete messages */
|
|
63
|
+
finalizeAll: () => Promise<void>;
|
|
64
|
+
/** Get all current messages */
|
|
65
|
+
getMessages: () => Map<string, AggregatedMessage>;
|
|
66
|
+
/** Get a specific message by key */
|
|
67
|
+
getMessage: (nodeId: string, runIndex: number, itemIndex: number) => AggregatedMessage | undefined;
|
|
68
|
+
/** Clear all messages */
|
|
69
|
+
clear: () => void;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Creates a stateful chunk aggregator for multi-source streaming.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```typescript
|
|
76
|
+
* const aggregator = createChunkAggregator({
|
|
77
|
+
* onBegin: async (message) => {
|
|
78
|
+
* console.log(`Starting: ${message.nodeId}`);
|
|
79
|
+
* },
|
|
80
|
+
* onItem: async (message, delta) => {
|
|
81
|
+
* sendToClient(delta);
|
|
82
|
+
* },
|
|
83
|
+
* onEnd: async (message) => {
|
|
84
|
+
* await saveMessage(message);
|
|
85
|
+
* }
|
|
86
|
+
* });
|
|
87
|
+
*
|
|
88
|
+
* for await (const chunk of stream) {
|
|
89
|
+
* await aggregator.ingest(chunk);
|
|
90
|
+
* }
|
|
91
|
+
* await aggregator.finalizeAll();
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
export declare function createChunkAggregator(handlers?: ChunkHandlers): ChunkAggregator;
|
|
95
|
+
//# sourceMappingURL=aggregator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aggregator.d.ts","sourceRoot":"","sources":["../src/aggregator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,gCAAgC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,2BAA2B;IAC3B,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,CAAC;IACzC,qCAAqC;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uCAAuC;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,6BAA6B;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,0BAA0B;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,qBAAqB;IACrB,MAAM,EAAE,WAAW,GAAG,UAAU,GAAG,OAAO,CAAC;IAC3C,yCAAyC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qCAAqC;IACrC,SAAS,EAAE,IAAI,CAAC;IAChB,uCAAuC;IACvC,WAAW,CAAC,EAAE,IAAI,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,8CAA8C;IAC9C,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,oCAAoC;IACpC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,iBAAiB,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtE,qDAAqD;IACrD,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,0CAA0C;IAC1C,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,iBAAiB,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACzE;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,gCAAgC;IAChC,MAAM,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC/D,uCAAuC;IACvC,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,+BAA+B;IAC/B,WAAW,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IAClD,oCAAoC;IACpC,UAAU,EAAE,CACV,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,KACd,iBAAiB,GAAG,SAAS,CAAC;IACnC,yBAAyB;IACzB,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,GAAE,aAAkB,GAC3B,eAAe,CAqHjB"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chunk Aggregation
|
|
3
|
+
* Aggregate streaming chunks from multiple sources with lifecycle handlers
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Creates a stateful chunk aggregator for multi-source streaming.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const aggregator = createChunkAggregator({
|
|
11
|
+
* onBegin: async (message) => {
|
|
12
|
+
* console.log(`Starting: ${message.nodeId}`);
|
|
13
|
+
* },
|
|
14
|
+
* onItem: async (message, delta) => {
|
|
15
|
+
* sendToClient(delta);
|
|
16
|
+
* },
|
|
17
|
+
* onEnd: async (message) => {
|
|
18
|
+
* await saveMessage(message);
|
|
19
|
+
* }
|
|
20
|
+
* });
|
|
21
|
+
*
|
|
22
|
+
* for await (const chunk of stream) {
|
|
23
|
+
* await aggregator.ingest(chunk);
|
|
24
|
+
* }
|
|
25
|
+
* await aggregator.finalizeAll();
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export function createChunkAggregator(handlers = {}) {
|
|
29
|
+
const messages = new Map();
|
|
30
|
+
function getKey(chunk) {
|
|
31
|
+
return `${chunk.nodeId}:${chunk.runIndex}:${chunk.itemIndex}`;
|
|
32
|
+
}
|
|
33
|
+
async function ingest(chunk) {
|
|
34
|
+
const key = getKey(chunk);
|
|
35
|
+
switch (chunk.type) {
|
|
36
|
+
case 'begin': {
|
|
37
|
+
const message = {
|
|
38
|
+
nodeId: chunk.nodeId,
|
|
39
|
+
runIndex: chunk.runIndex,
|
|
40
|
+
itemIndex: chunk.itemIndex,
|
|
41
|
+
content: '',
|
|
42
|
+
status: 'streaming',
|
|
43
|
+
startedAt: new Date(),
|
|
44
|
+
};
|
|
45
|
+
messages.set(key, message);
|
|
46
|
+
await handlers.onBegin?.(message);
|
|
47
|
+
return message;
|
|
48
|
+
}
|
|
49
|
+
case 'item': {
|
|
50
|
+
let message = messages.get(key);
|
|
51
|
+
// Auto-create message if 'begin' was missed
|
|
52
|
+
if (!message) {
|
|
53
|
+
message = {
|
|
54
|
+
nodeId: chunk.nodeId,
|
|
55
|
+
runIndex: chunk.runIndex,
|
|
56
|
+
itemIndex: chunk.itemIndex,
|
|
57
|
+
content: '',
|
|
58
|
+
status: 'streaming',
|
|
59
|
+
startedAt: new Date(),
|
|
60
|
+
};
|
|
61
|
+
messages.set(key, message);
|
|
62
|
+
await handlers.onBegin?.(message);
|
|
63
|
+
}
|
|
64
|
+
if (chunk.data) {
|
|
65
|
+
message.content += chunk.data;
|
|
66
|
+
await handlers.onItem?.(message, chunk.data);
|
|
67
|
+
}
|
|
68
|
+
return message;
|
|
69
|
+
}
|
|
70
|
+
case 'end': {
|
|
71
|
+
const message = messages.get(key);
|
|
72
|
+
if (message) {
|
|
73
|
+
message.status = 'complete';
|
|
74
|
+
message.completedAt = new Date();
|
|
75
|
+
await handlers.onEnd?.(message);
|
|
76
|
+
}
|
|
77
|
+
return message;
|
|
78
|
+
}
|
|
79
|
+
case 'error': {
|
|
80
|
+
let message = messages.get(key);
|
|
81
|
+
// Auto-create message if 'begin' was missed
|
|
82
|
+
if (!message) {
|
|
83
|
+
message = {
|
|
84
|
+
nodeId: chunk.nodeId,
|
|
85
|
+
runIndex: chunk.runIndex,
|
|
86
|
+
itemIndex: chunk.itemIndex,
|
|
87
|
+
content: '',
|
|
88
|
+
status: 'error',
|
|
89
|
+
startedAt: new Date(),
|
|
90
|
+
};
|
|
91
|
+
messages.set(key, message);
|
|
92
|
+
}
|
|
93
|
+
message.status = 'error';
|
|
94
|
+
message.error = chunk.error;
|
|
95
|
+
message.completedAt = new Date();
|
|
96
|
+
await handlers.onError?.(message, chunk.error);
|
|
97
|
+
return message;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async function finalizeAll() {
|
|
102
|
+
for (const message of messages.values()) {
|
|
103
|
+
if (message.status === 'streaming') {
|
|
104
|
+
message.status = 'complete';
|
|
105
|
+
message.completedAt = new Date();
|
|
106
|
+
await handlers.onEnd?.(message);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function getMessages() {
|
|
111
|
+
return new Map(messages);
|
|
112
|
+
}
|
|
113
|
+
function getMessage(nodeId, runIndex, itemIndex) {
|
|
114
|
+
return messages.get(`${nodeId}:${runIndex}:${itemIndex}`);
|
|
115
|
+
}
|
|
116
|
+
function clear() {
|
|
117
|
+
messages.clear();
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
ingest,
|
|
121
|
+
finalizeAll,
|
|
122
|
+
getMessages,
|
|
123
|
+
getMessage,
|
|
124
|
+
clear,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=aggregator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aggregator.js","sourceRoot":"","sources":["../src/aggregator.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @llm-utils/stream
|
|
3
|
+
* Streaming utilities for real-time LLM responses
|
|
4
|
+
*/
|
|
5
|
+
export { interceptResponseWrites } from './interceptor.js';
|
|
6
|
+
export type { InterceptOptions, ChunkTransformer } from './interceptor.js';
|
|
7
|
+
export { createChunkAggregator } from './aggregator.js';
|
|
8
|
+
export type { StructuredChunk, AggregatedMessage, ChunkHandlers, ChunkAggregator, } from './aggregator.js';
|
|
9
|
+
export { streamJsonLines, parseJsonLines, toJsonLine, fromJsonLine } from './jsonl.js';
|
|
10
|
+
export type { JsonLineGenerator } from './jsonl.js';
|
|
11
|
+
export { mux, muxSimple, parseCursor, serializeCursor, createCursor, createReplayableStream, mapEvents, filterEvents, accumulateDeltas, } from './mux.js';
|
|
12
|
+
export type { StreamEvent, StreamCursor, MuxOptions, NamedStream, } from './mux.js';
|
|
13
|
+
export { sse, jsonl, raw, streamToResponse, textStreamResponse, parseSSEResponse, parseJSONLResponse, streamToNodeResponse, createAIEvent, toEventStream, } from './response.js';
|
|
14
|
+
export type { StreamProtocol, StreamResponseOptions, ProtocolEncoder, NodeServerResponse, } from './response.js';
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAC3D,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAG3E,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,YAAY,EACV,eAAe,EACf,iBAAiB,EACjB,aAAa,EACb,eAAe,GAChB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACvF,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAGpD,OAAO,EACL,GAAG,EACH,SAAS,EACT,WAAW,EACX,eAAe,EACf,YAAY,EACZ,sBAAsB,EACtB,SAAS,EACT,YAAY,EACZ,gBAAgB,GACjB,MAAM,UAAU,CAAC;AAClB,YAAY,EACV,WAAW,EACX,YAAY,EACZ,UAAU,EACV,WAAW,GACZ,MAAM,UAAU,CAAC;AAGlB,OAAO,EACL,GAAG,EACH,KAAK,EACL,GAAG,EACH,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,EACpB,aAAa,EACb,aAAa,GACd,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,cAAc,EACd,qBAAqB,EACrB,eAAe,EACf,kBAAkB,GACnB,MAAM,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @llm-utils/stream
|
|
3
|
+
* Streaming utilities for real-time LLM responses
|
|
4
|
+
*/
|
|
5
|
+
// Response Interception
|
|
6
|
+
export { interceptResponseWrites } from './interceptor.js';
|
|
7
|
+
// Chunk Aggregation
|
|
8
|
+
export { createChunkAggregator } from './aggregator.js';
|
|
9
|
+
// JSONL Streaming
|
|
10
|
+
export { streamJsonLines, parseJsonLines, toJsonLine, fromJsonLine } from './jsonl.js';
|
|
11
|
+
// Stream Multiplexing
|
|
12
|
+
export { mux, muxSimple, parseCursor, serializeCursor, createCursor, createReplayableStream, mapEvents, filterEvents, accumulateDeltas, } from './mux.js';
|
|
13
|
+
// HTTP Response Utilities
|
|
14
|
+
export { sse, jsonl, raw, streamToResponse, textStreamResponse, parseSSEResponse, parseJSONLResponse, streamToNodeResponse, createAIEvent, toEventStream, } from './response.js';
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,wBAAwB;AACxB,OAAO,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAG3D,oBAAoB;AACpB,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAQxD,kBAAkB;AAClB,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAGvF,sBAAsB;AACtB,OAAO,EACL,GAAG,EACH,SAAS,EACT,WAAW,EACX,eAAe,EACf,YAAY,EACZ,sBAAsB,EACtB,SAAS,EACT,YAAY,EACZ,gBAAgB,GACjB,MAAM,UAAU,CAAC;AAQlB,0BAA0B;AAC1B,OAAO,EACL,GAAG,EACH,KAAK,EACL,GAAG,EACH,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,EACpB,aAAa,EACb,aAAa,GACd,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP Response Interception
|
|
3
|
+
* Transform streaming responses without breaking HTTP semantics
|
|
4
|
+
*/
|
|
5
|
+
import type { ServerResponse } from 'node:http';
|
|
6
|
+
/**
|
|
7
|
+
* Transform function for stream chunks
|
|
8
|
+
* Return undefined to skip the chunk
|
|
9
|
+
*/
|
|
10
|
+
export type ChunkTransformer = (text: string) => Promise<string | undefined>;
|
|
11
|
+
/**
|
|
12
|
+
* Options for response interception
|
|
13
|
+
*/
|
|
14
|
+
export interface InterceptOptions {
|
|
15
|
+
/** Transform each chunk before sending */
|
|
16
|
+
transform: ChunkTransformer;
|
|
17
|
+
/** Called when stream finishes */
|
|
18
|
+
onFinish?: () => Promise<void>;
|
|
19
|
+
/** Called on error */
|
|
20
|
+
onError?: (error: Error) => void;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Intercepts HTTP response writes for transformation.
|
|
24
|
+
* Preserves Express/Node response API compatibility.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* interceptResponseWrites(res, {
|
|
29
|
+
* transform: async (chunk) => {
|
|
30
|
+
* const data = JSON.parse(chunk);
|
|
31
|
+
* return JSON.stringify({ ...data, timestamp: Date.now() }) + '\n';
|
|
32
|
+
* },
|
|
33
|
+
* onFinish: async () => {
|
|
34
|
+
* await saveConversation();
|
|
35
|
+
* }
|
|
36
|
+
* });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export declare function interceptResponseWrites<T extends ServerResponse>(res: T, options: InterceptOptions): T;
|
|
40
|
+
//# sourceMappingURL=interceptor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interceptor.d.ts","sourceRoot":"","sources":["../src/interceptor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEhD;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;AAE7E;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,0CAA0C;IAC1C,SAAS,EAAE,gBAAgB,CAAC;IAC5B,kCAAkC;IAClC,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,sBAAsB;IACtB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,SAAS,cAAc,EAC9D,GAAG,EAAE,CAAC,EACN,OAAO,EAAE,gBAAgB,GACxB,CAAC,CAgGH"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP Response Interception
|
|
3
|
+
* Transform streaming responses without breaking HTTP semantics
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Intercepts HTTP response writes for transformation.
|
|
7
|
+
* Preserves Express/Node response API compatibility.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* interceptResponseWrites(res, {
|
|
12
|
+
* transform: async (chunk) => {
|
|
13
|
+
* const data = JSON.parse(chunk);
|
|
14
|
+
* return JSON.stringify({ ...data, timestamp: Date.now() }) + '\n';
|
|
15
|
+
* },
|
|
16
|
+
* onFinish: async () => {
|
|
17
|
+
* await saveConversation();
|
|
18
|
+
* }
|
|
19
|
+
* });
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function interceptResponseWrites(res, options) {
|
|
23
|
+
const { transform, onFinish, onError } = options;
|
|
24
|
+
// Store original methods
|
|
25
|
+
const originalWrite = res.write.bind(res);
|
|
26
|
+
const originalEnd = res.end.bind(res);
|
|
27
|
+
// Buffer for incomplete chunks
|
|
28
|
+
let buffer = '';
|
|
29
|
+
// Override write
|
|
30
|
+
res.write = function (chunk, encodingOrCallback, callback) {
|
|
31
|
+
const encoding = typeof encodingOrCallback === 'string' ? encodingOrCallback : 'utf8';
|
|
32
|
+
const cb = typeof encodingOrCallback === 'function' ? encodingOrCallback : callback;
|
|
33
|
+
const text = typeof chunk === 'string' ? chunk : chunk.toString(encoding);
|
|
34
|
+
buffer += text;
|
|
35
|
+
// Process complete lines
|
|
36
|
+
const lines = buffer.split('\n');
|
|
37
|
+
buffer = lines.pop() || ''; // Keep incomplete line in buffer
|
|
38
|
+
// Process each complete line
|
|
39
|
+
(async () => {
|
|
40
|
+
try {
|
|
41
|
+
for (const line of lines) {
|
|
42
|
+
if (line.trim()) {
|
|
43
|
+
const transformed = await transform(line);
|
|
44
|
+
if (transformed !== undefined) {
|
|
45
|
+
originalWrite(transformed + '\n', encoding);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
cb?.(null);
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
onError?.(error);
|
|
53
|
+
cb?.(error);
|
|
54
|
+
}
|
|
55
|
+
})();
|
|
56
|
+
return true;
|
|
57
|
+
};
|
|
58
|
+
// Override end
|
|
59
|
+
res.end = function (chunkOrCallback, encodingOrCallback, callback) {
|
|
60
|
+
const chunk = typeof chunkOrCallback === 'function' ? undefined : chunkOrCallback;
|
|
61
|
+
const cb = typeof chunkOrCallback === 'function'
|
|
62
|
+
? chunkOrCallback
|
|
63
|
+
: typeof encodingOrCallback === 'function'
|
|
64
|
+
? encodingOrCallback
|
|
65
|
+
: callback;
|
|
66
|
+
(async () => {
|
|
67
|
+
try {
|
|
68
|
+
// Process any remaining chunk
|
|
69
|
+
if (chunk) {
|
|
70
|
+
const text = typeof chunk === 'string' ? chunk : chunk.toString('utf8');
|
|
71
|
+
buffer += text;
|
|
72
|
+
}
|
|
73
|
+
// Process remaining buffer
|
|
74
|
+
if (buffer.trim()) {
|
|
75
|
+
const transformed = await transform(buffer);
|
|
76
|
+
if (transformed !== undefined) {
|
|
77
|
+
originalWrite(transformed);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Call finish handler
|
|
81
|
+
await onFinish?.();
|
|
82
|
+
// End the response
|
|
83
|
+
originalEnd(cb);
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
onError?.(error);
|
|
87
|
+
originalEnd(cb);
|
|
88
|
+
}
|
|
89
|
+
})();
|
|
90
|
+
return res;
|
|
91
|
+
};
|
|
92
|
+
return res;
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=interceptor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interceptor.js","sourceRoot":"","sources":["../src/interceptor.ts"],"names":[],"mappings":"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"}
|
package/dist/jsonl.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSONL Streaming
|
|
3
|
+
* Parse and serialize JSONL (JSON Lines) streams
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Generator function that yields objects to stream as JSONL
|
|
7
|
+
*/
|
|
8
|
+
export type JsonLineGenerator<T> = () => AsyncIterable<T>;
|
|
9
|
+
/**
|
|
10
|
+
* A minimal Response-like interface for parsing JSONL
|
|
11
|
+
*/
|
|
12
|
+
interface ReadableResponse {
|
|
13
|
+
body: ReadableStream<Uint8Array> | null;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* A minimal ServerResponse-like interface for streaming JSONL
|
|
17
|
+
*/
|
|
18
|
+
interface WritableResponse {
|
|
19
|
+
setHeader(name: string, value: string): void;
|
|
20
|
+
write(chunk: string): boolean;
|
|
21
|
+
end(): void;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Streams objects as JSONL (JSON Lines) to an HTTP response.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* app.get('/events', (req, res) => {
|
|
29
|
+
* streamJsonLines(res, async function* () {
|
|
30
|
+
* for (const event of events) {
|
|
31
|
+
* yield event;
|
|
32
|
+
* }
|
|
33
|
+
* });
|
|
34
|
+
* });
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export declare function streamJsonLines<T>(res: WritableResponse, generator: JsonLineGenerator<T>): void;
|
|
38
|
+
/**
|
|
39
|
+
* Parses a JSONL stream from a fetch Response.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* const response = await fetch('/events');
|
|
44
|
+
* for await (const event of parseJsonLines(response)) {
|
|
45
|
+
* handleEvent(event);
|
|
46
|
+
* }
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export declare function parseJsonLines<T = unknown>(response: ReadableResponse): AsyncGenerator<T, void, unknown>;
|
|
50
|
+
/**
|
|
51
|
+
* Serializes an object to a JSONL line
|
|
52
|
+
*/
|
|
53
|
+
export declare function toJsonLine<T>(obj: T): string;
|
|
54
|
+
/**
|
|
55
|
+
* Parses a single JSONL line
|
|
56
|
+
*/
|
|
57
|
+
export declare function fromJsonLine<T>(line: string): T;
|
|
58
|
+
export {};
|
|
59
|
+
//# sourceMappingURL=jsonl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonl.d.ts","sourceRoot":"","sources":["../src/jsonl.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAAI,MAAM,aAAa,CAAC,CAAC,CAAC,CAAC;AAE1D;;GAEG;AACH,UAAU,gBAAgB;IACxB,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC;CACzC;AAED;;GAEG;AACH,UAAU,gBAAgB;IACxB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7C,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9B,GAAG,IAAI,IAAI,CAAC;CACb;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAC/B,GAAG,EAAE,gBAAgB,EACrB,SAAS,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAC9B,IAAI,CAgBN;AAED;;;;;;;;;;GAUG;AACH,wBAAuB,cAAc,CAAC,CAAC,GAAG,OAAO,EAC/C,QAAQ,EAAE,gBAAgB,GACzB,cAAc,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAgDlC;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,MAAM,CAE5C;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,CAE/C"}
|