streamfold 0.1.4 → 0.1.5
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 +25 -0
- package/package.json +1 -1
- package/src/adapter.js +161 -0
- package/src/ag-ui.d.ts +6 -4
- package/src/ag-ui.js +17 -9
- package/src/anthropic.d.ts +6 -4
- package/src/anthropic.js +38 -24
- package/src/assistant-ui.d.ts +6 -4
- package/src/assistant-ui.js +29 -18
- package/src/gemini.d.ts +6 -4
- package/src/gemini.js +8 -5
- package/src/index.d.ts +137 -8
- package/src/index.js +89 -24
- package/src/internal/diagnostics.js +9 -0
- package/src/internal/errors.js +14 -0
- package/src/internal/integration.js +105 -15
- package/src/internal/snapshots.js +58 -0
- package/src/internal/wasm-runtime.js +31 -5
- package/src/langchain.d.ts +6 -4
- package/src/langchain.js +8 -4
- package/src/openai.d.ts +6 -4
- package/src/openai.js +22 -15
- package/src/read-structured.js +48 -0
- package/src/vercel-ai.d.ts +6 -4
- package/src/vercel-ai.js +34 -16
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { wasmBinaryBase64 } from "./wasm-binary.js";
|
|
2
|
+
import { streamError } from "./errors.js";
|
|
2
3
|
|
|
3
4
|
const DEPTH_MASK = 0x00ff_ffff;
|
|
4
5
|
const COMPLETE_FLAG = 1 << 24;
|
|
@@ -36,7 +37,7 @@ const getExports = () => {
|
|
|
36
37
|
return exports;
|
|
37
38
|
};
|
|
38
39
|
|
|
39
|
-
const
|
|
40
|
+
const describeParserError = ({ wasm, handle, maxBytes, maxDepth }, code) => {
|
|
40
41
|
const offset = wasm.streamfold_parser_error_offset(handle) >>> 0;
|
|
41
42
|
const byte = wasm.streamfold_parser_error_byte(handle);
|
|
42
43
|
if (code === 1) {
|
|
@@ -61,6 +62,28 @@ const parserError = ({ wasm, handle, maxBytes, maxDepth }, code) => {
|
|
|
61
62
|
return new SyntaxError(`Rust parser failed with error code ${code}`);
|
|
62
63
|
};
|
|
63
64
|
|
|
65
|
+
const parserErrorCodes = [
|
|
66
|
+
"PARSER_ERROR",
|
|
67
|
+
"UNEXPECTED_TOKEN",
|
|
68
|
+
"MISMATCHED_CLOSING",
|
|
69
|
+
"TRAILING_DATA",
|
|
70
|
+
"EMPTY_INPUT",
|
|
71
|
+
"INCOMPLETE_JSON",
|
|
72
|
+
"INVALID_JSON",
|
|
73
|
+
"MAX_BYTES_EXCEEDED",
|
|
74
|
+
"MAX_DEPTH_EXCEEDED",
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
const parserError = (parser, code) =>
|
|
78
|
+
streamError(
|
|
79
|
+
describeParserError(parser, code),
|
|
80
|
+
parserErrorCodes[code] ?? "PARSER_ERROR",
|
|
81
|
+
{
|
|
82
|
+
byteOffset:
|
|
83
|
+
parser.wasm.streamfold_parser_error_offset(parser.handle) >>> 0,
|
|
84
|
+
},
|
|
85
|
+
);
|
|
86
|
+
|
|
64
87
|
const readState = (parser, encoded) => {
|
|
65
88
|
const { wasm, handle } = parser;
|
|
66
89
|
const error = encoded >>> ERROR_SHIFT;
|
|
@@ -190,6 +213,12 @@ const pushChunk = (parser, chunk) => {
|
|
|
190
213
|
};
|
|
191
214
|
|
|
192
215
|
export const pushWasmParser = (parser, chunk) => {
|
|
216
|
+
if (typeof chunk !== "string") {
|
|
217
|
+
throw streamError(
|
|
218
|
+
new TypeError("A structured stream chunk must be a string"),
|
|
219
|
+
"INVALID_CHUNK",
|
|
220
|
+
);
|
|
221
|
+
}
|
|
193
222
|
let input = parser.pendingHighSurrogate + chunk;
|
|
194
223
|
parser.pendingHighSurrogate = "";
|
|
195
224
|
|
|
@@ -217,10 +246,7 @@ export const finishWasmParser = (parser) => {
|
|
|
217
246
|
};
|
|
218
247
|
|
|
219
248
|
export const readWasmParser = (parser) =>
|
|
220
|
-
readState(
|
|
221
|
-
parser,
|
|
222
|
-
parser.wasm.streamfold_parser_state(parser.handle),
|
|
223
|
-
);
|
|
249
|
+
readState(parser, parser.wasm.streamfold_parser_state(parser.handle));
|
|
224
250
|
|
|
225
251
|
export const freeWasmParser = ({ wasm, handle }) => {
|
|
226
252
|
wasm.streamfold_parser_free(handle);
|
package/src/langchain.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
BatchEventStructuredStream,
|
|
3
|
+
BatchStructuredStreamIntegration,
|
|
4
|
+
StructuredStreamIntegrationOptions,
|
|
4
5
|
StructuredStreamPool,
|
|
5
6
|
} from "./index.js";
|
|
6
7
|
|
|
@@ -16,11 +17,12 @@ export interface LangChainToolCallMessage {
|
|
|
16
17
|
readonly tool_call_chunks?: readonly LangChainToolCallChunk[];
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
export const langchain:
|
|
20
|
+
export const langchain: BatchStructuredStreamIntegration<
|
|
20
21
|
LangChainToolCallMessage,
|
|
21
22
|
string
|
|
22
23
|
>;
|
|
23
24
|
|
|
24
25
|
export function createStructuredStream(
|
|
25
26
|
pool?: StructuredStreamPool<string>,
|
|
26
|
-
|
|
27
|
+
options?: StructuredStreamIntegrationOptions,
|
|
28
|
+
): BatchEventStructuredStream<LangChainToolCallMessage, string>;
|
package/src/langchain.js
CHANGED
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
import { createStructuredStreamPool } from "./index.js";
|
|
2
2
|
import { createIntegration } from "./internal/integration.js";
|
|
3
3
|
|
|
4
|
-
export const langchain = (pool = createStructuredStreamPool()) => {
|
|
4
|
+
export const langchain = (pool = createStructuredStreamPool(), options) => {
|
|
5
5
|
const idsByIndex = new Map();
|
|
6
6
|
|
|
7
7
|
return createIntegration(
|
|
8
8
|
pool,
|
|
9
|
-
(message) => {
|
|
9
|
+
(message, calls) => {
|
|
10
10
|
let update;
|
|
11
11
|
for (const chunk of message.tool_call_chunks ?? []) {
|
|
12
12
|
let id = idsByIndex.get(chunk.index);
|
|
13
13
|
if (id === undefined && chunk.id) {
|
|
14
14
|
id = chunk.id;
|
|
15
15
|
idsByIndex.set(chunk.index, id);
|
|
16
|
-
update =
|
|
16
|
+
update = calls.start(id);
|
|
17
17
|
}
|
|
18
18
|
if (id !== undefined && chunk.args) {
|
|
19
|
-
update =
|
|
19
|
+
update = calls.push(id, chunk.args);
|
|
20
|
+
} else if (id === undefined && chunk.args) {
|
|
21
|
+
calls.unmatched("Arguments delta has no matching tool call index");
|
|
20
22
|
}
|
|
21
23
|
}
|
|
22
24
|
return update;
|
|
@@ -25,6 +27,8 @@ export const langchain = (pool = createStructuredStreamPool()) => {
|
|
|
25
27
|
for (const id of idsByIndex.values()) complete(id);
|
|
26
28
|
idsByIndex.clear();
|
|
27
29
|
},
|
|
30
|
+
"langchain",
|
|
31
|
+
options,
|
|
28
32
|
);
|
|
29
33
|
};
|
|
30
34
|
|
package/src/openai.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
BatchEventStructuredStream,
|
|
3
|
+
BatchStructuredStreamIntegration,
|
|
4
|
+
StructuredStreamIntegrationOptions,
|
|
4
5
|
StructuredStreamPool,
|
|
5
6
|
} from "./index.js";
|
|
6
7
|
|
|
@@ -27,11 +28,12 @@ export type OpenAiResponsesToolEvent =
|
|
|
27
28
|
readonly [key: string]: unknown;
|
|
28
29
|
};
|
|
29
30
|
|
|
30
|
-
export const openAI:
|
|
31
|
+
export const openAI: BatchStructuredStreamIntegration<
|
|
31
32
|
OpenAiResponsesToolEvent,
|
|
32
33
|
string
|
|
33
34
|
>;
|
|
34
35
|
|
|
35
36
|
export function createStructuredStream(
|
|
36
37
|
pool?: StructuredStreamPool<string>,
|
|
37
|
-
|
|
38
|
+
options?: StructuredStreamIntegrationOptions,
|
|
39
|
+
): BatchEventStructuredStream<OpenAiResponsesToolEvent, string>;
|
package/src/openai.js
CHANGED
|
@@ -1,20 +1,27 @@
|
|
|
1
1
|
import { createStructuredStreamPool } from "./index.js";
|
|
2
2
|
import { append, createIntegration } from "./internal/integration.js";
|
|
3
3
|
|
|
4
|
-
export const openAI = (pool = createStructuredStreamPool()) =>
|
|
5
|
-
createIntegration(
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
4
|
+
export const openAI = (pool = createStructuredStreamPool(), options) =>
|
|
5
|
+
createIntegration(
|
|
6
|
+
pool,
|
|
7
|
+
(event, calls) => {
|
|
8
|
+
if (event.type === "response.output_item.added") {
|
|
9
|
+
if (event.item.type !== "function_call") return undefined;
|
|
10
|
+
return calls.start(event.item.id);
|
|
11
|
+
}
|
|
12
|
+
if (event.type === "response.function_call_arguments.delta") {
|
|
13
|
+
return append(calls, event.item_id, event.delta);
|
|
14
|
+
}
|
|
15
|
+
if (event.type === "response.function_call_arguments.done") {
|
|
16
|
+
if (!calls.has(event.item_id))
|
|
17
|
+
calls.start(event.item_id, event.arguments);
|
|
18
|
+
return calls.complete(event.item_id);
|
|
19
|
+
}
|
|
20
|
+
return undefined;
|
|
21
|
+
},
|
|
22
|
+
undefined,
|
|
23
|
+
"openai",
|
|
24
|
+
options,
|
|
25
|
+
);
|
|
19
26
|
|
|
20
27
|
export { openAI as createStructuredStream };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { createStructuredStreamPool } from "./index.js";
|
|
2
|
+
import { streamError } from "./internal/errors.js";
|
|
3
|
+
|
|
4
|
+
export async function* readStructured(
|
|
5
|
+
events,
|
|
6
|
+
{ adapter, integration, limits, onDiagnostic },
|
|
7
|
+
) {
|
|
8
|
+
if (
|
|
9
|
+
(adapter === undefined) === (integration === undefined) ||
|
|
10
|
+
(adapter !== undefined && typeof adapter !== "function") ||
|
|
11
|
+
(integration !== undefined && typeof integration !== "function")
|
|
12
|
+
) {
|
|
13
|
+
throw streamError(
|
|
14
|
+
new TypeError("Choose exactly one adapter or integration factory"),
|
|
15
|
+
"INVALID_OPTIONS",
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
let pool;
|
|
19
|
+
let stream;
|
|
20
|
+
try {
|
|
21
|
+
if (integration !== undefined) {
|
|
22
|
+
pool = createStructuredStreamPool(limits);
|
|
23
|
+
stream = integration(pool, { onDiagnostic });
|
|
24
|
+
} else {
|
|
25
|
+
stream = adapter(
|
|
26
|
+
onDiagnostic === undefined ? limits : { ...limits, onDiagnostic },
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
for await (const event of events) {
|
|
30
|
+
for (const update of stream.pushAll(event)) yield update;
|
|
31
|
+
}
|
|
32
|
+
if (pool === undefined) {
|
|
33
|
+
for (const completed of stream.finish()) yield completed;
|
|
34
|
+
} else {
|
|
35
|
+
// SDK finish() appends pending calls to its legacy completion history.
|
|
36
|
+
const pending = pool.size;
|
|
37
|
+
const completed = stream.finish();
|
|
38
|
+
if (pending > 0) {
|
|
39
|
+
for (const result of completed.slice(-pending)) {
|
|
40
|
+
yield { type: "complete", ...result };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
} finally {
|
|
45
|
+
if (pool === undefined) stream?.dispose();
|
|
46
|
+
else for (const id of pool.activeIds) pool.abort(id);
|
|
47
|
+
}
|
|
48
|
+
}
|
package/src/vercel-ai.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
BatchEventStructuredStream,
|
|
3
|
+
BatchStructuredStreamIntegration,
|
|
4
|
+
StructuredStreamIntegrationOptions,
|
|
4
5
|
StructuredStreamPool,
|
|
5
6
|
} from "./index.js";
|
|
6
7
|
|
|
@@ -42,11 +43,12 @@ export type VercelAiToolInputEvent =
|
|
|
42
43
|
readonly [key: string]: unknown;
|
|
43
44
|
};
|
|
44
45
|
|
|
45
|
-
export const vercelAI:
|
|
46
|
+
export const vercelAI: BatchStructuredStreamIntegration<
|
|
46
47
|
VercelAiToolInputEvent,
|
|
47
48
|
string
|
|
48
49
|
>;
|
|
49
50
|
|
|
50
51
|
export function createStructuredStream(
|
|
51
52
|
pool?: StructuredStreamPool<string>,
|
|
52
|
-
|
|
53
|
+
options?: StructuredStreamIntegrationOptions,
|
|
54
|
+
): BatchEventStructuredStream<VercelAiToolInputEvent, string>;
|
package/src/vercel-ai.js
CHANGED
|
@@ -1,22 +1,40 @@
|
|
|
1
1
|
import { createStructuredStreamPool } from "./index.js";
|
|
2
2
|
import { createIntegration } from "./internal/integration.js";
|
|
3
3
|
|
|
4
|
-
export const vercelAI = (pool = createStructuredStreamPool()) =>
|
|
5
|
-
createIntegration(
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
export const vercelAI = (pool = createStructuredStreamPool(), options) =>
|
|
5
|
+
createIntegration(
|
|
6
|
+
pool,
|
|
7
|
+
(event, calls) => {
|
|
8
|
+
const id = event.id ?? event.toolCallId;
|
|
9
|
+
if (id === undefined) {
|
|
10
|
+
if (
|
|
11
|
+
[
|
|
12
|
+
"tool-input-start",
|
|
13
|
+
"tool-input-delta",
|
|
14
|
+
"tool-input-end",
|
|
15
|
+
"tool-input-available",
|
|
16
|
+
].includes(event.type)
|
|
17
|
+
) {
|
|
18
|
+
calls.unmatched("Tool input event is missing its call id");
|
|
19
|
+
}
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
8
22
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
23
|
+
if (event.type === "tool-input-start") return calls.start(id);
|
|
24
|
+
if (event.type === "tool-input-delta") {
|
|
25
|
+
return calls.push(id, event.delta ?? event.inputTextDelta);
|
|
26
|
+
}
|
|
27
|
+
if (
|
|
28
|
+
event.type === "tool-input-end" ||
|
|
29
|
+
event.type === "tool-input-available"
|
|
30
|
+
) {
|
|
31
|
+
return calls.complete(id);
|
|
32
|
+
}
|
|
33
|
+
return undefined;
|
|
34
|
+
},
|
|
35
|
+
undefined,
|
|
36
|
+
"vercel-ai",
|
|
37
|
+
options,
|
|
38
|
+
);
|
|
21
39
|
|
|
22
40
|
export { vercelAI as createStructuredStream };
|