streamfold 0.1.4 → 0.1.6

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.
@@ -1,34 +1,123 @@
1
- export const createIntegration = (pool, accept, finishActive) => {
1
+ import {
2
+ annotateError,
3
+ isStructuredStreamError,
4
+ streamError,
5
+ } from "./errors.js";
6
+ import { emitDiagnostic } from "./diagnostics.js";
7
+
8
+ export const createIntegration = (
9
+ pool,
10
+ accept,
11
+ finishActive,
12
+ adapter,
13
+ options = {},
14
+ ) => {
2
15
  const completed = [];
3
16
  let failure;
17
+ let batch;
18
+ let eventType;
19
+ let receivedEvents = false;
20
+ let matched = false;
21
+ let reportedNoMatch = false;
22
+
23
+ const diagnose = (diagnostic) =>
24
+ emitDiagnostic(options, { adapter, eventType, ...diagnostic });
25
+ const unmatched = (message, id) =>
26
+ diagnose({
27
+ code: "UNMATCHED_TOOL_EVENT",
28
+ message,
29
+ id,
30
+ });
31
+ const record = (type, result) => {
32
+ matched = true;
33
+ if (batch !== undefined) batch.push({ type, ...result });
34
+ return result;
35
+ };
36
+ const missingId = (id) => {
37
+ if (id !== undefined && id !== null) return false;
38
+ unmatched("Tool event is missing its call id");
39
+ return true;
40
+ };
4
41
 
5
42
  const complete = (id) => {
6
- if (!pool.has(id)) return undefined;
43
+ if (missingId(id)) return undefined;
44
+ if (!pool.has(id)) {
45
+ if (
46
+ options.onDiagnostic &&
47
+ !completed.some((result) => result.id === id)
48
+ ) {
49
+ unmatched("Tool completion has no matching active call", id);
50
+ }
51
+ return undefined;
52
+ }
7
53
  const result = pool.finish(id);
8
54
  completed.push(result);
9
- return result;
55
+ return record("complete", result);
56
+ };
57
+ const operations = {
58
+ start: (id, initialChunk) =>
59
+ missingId(id) ? undefined : record("start", pool.start(id, initialChunk)),
60
+ push: (id, delta) =>
61
+ missingId(id) ? undefined : record("update", pool.push(id, delta)),
62
+ has: (id) => pool.has(id),
63
+ complete,
64
+ unmatched,
10
65
  };
11
66
 
12
67
  const fail = (error) => {
13
- failure = error instanceof Error ? error : new Error("Integration failed");
68
+ const original =
69
+ error instanceof Error ? error : new Error("Integration failed");
70
+ failure = isStructuredStreamError(original)
71
+ ? annotateError(original, { adapter, eventType })
72
+ : streamError(original, "INTEGRATION_ERROR", { adapter, eventType });
14
73
  for (const id of pool.activeIds) pool.abort(id);
74
+ diagnose({
75
+ code: "STREAM_ERROR",
76
+ message: failure.message,
77
+ error: failure,
78
+ id: failure.id,
79
+ });
15
80
  return failure;
16
81
  };
17
82
 
83
+ const push = (event, collect) => {
84
+ if (failure !== undefined) throw failure;
85
+ receivedEvents = true;
86
+ batch = collect ? [] : undefined;
87
+ try {
88
+ const type = event?.type ?? event?.event_type;
89
+ eventType =
90
+ typeof type === "string"
91
+ ? type
92
+ : event?.tool_call_chunks
93
+ ? "tool_call_chunks"
94
+ : undefined;
95
+ const update = accept(event, operations);
96
+ return collect ? batch : update;
97
+ } catch (error) {
98
+ throw fail(error);
99
+ } finally {
100
+ batch = undefined;
101
+ eventType = undefined;
102
+ }
103
+ };
104
+
18
105
  return {
19
- push(event) {
20
- if (failure !== undefined) throw failure;
21
- try {
22
- return accept(event, complete);
23
- } catch (error) {
24
- throw fail(error);
25
- }
26
- },
106
+ push: (event) => push(event, false),
107
+ pushAll: (event) => push(event, true),
27
108
  finish() {
28
109
  if (failure !== undefined) throw failure;
29
110
  try {
30
111
  finishActive?.(complete);
31
112
  for (const id of pool.activeIds) complete(id);
113
+ if (receivedEvents && !matched && !reportedNoMatch) {
114
+ reportedNoMatch = true;
115
+ diagnose({
116
+ code: "NO_TOOL_EVENTS",
117
+ message:
118
+ "No tool argument events matched this adapter; text-only streams are valid, otherwise check the event format",
119
+ });
120
+ }
32
121
  return [...completed];
33
122
  } catch (error) {
34
123
  throw fail(error);
@@ -37,7 +126,8 @@ export const createIntegration = (pool, accept, finishActive) => {
37
126
  };
38
127
  };
39
128
 
40
- export const append = (pool, id, delta) => {
41
- if (!pool.has(id)) pool.start(id);
42
- return pool.push(id, delta);
129
+ export const append = (operations, id, delta) => {
130
+ if (!operations.has(id) && operations.start(id) === undefined)
131
+ return undefined;
132
+ return operations.push(id, delta);
43
133
  };
@@ -0,0 +1,58 @@
1
+ const setOwn = (target, key, value) => {
2
+ Object.defineProperty(target, key, {
3
+ value,
4
+ enumerable: true,
5
+ configurable: true,
6
+ writable: true,
7
+ });
8
+ };
9
+
10
+ export const applyImmutableChanges = (previous, changes) => {
11
+ const owned = new Set();
12
+ const copy = (value) => {
13
+ if (owned.has(value)) return value;
14
+ const result = Array.isArray(value) ? value.slice() : { ...value };
15
+ owned.add(result);
16
+ return result;
17
+ };
18
+
19
+ let next = previous;
20
+ for (const change of changes) {
21
+ if (change.op === "complete") continue;
22
+ const replacement = (current) => {
23
+ if (change.op === "append") return current + change.value;
24
+ return change.value !== null && typeof change.value === "object"
25
+ ? copy(change.value)
26
+ : change.value;
27
+ };
28
+ if (change.path.length === 0) {
29
+ next = replacement(next);
30
+ continue;
31
+ }
32
+
33
+ // Copy each changed container once per batch, even for many array appends.
34
+ next = copy(next);
35
+ let target = next;
36
+ for (const key of change.path.slice(0, -1)) {
37
+ const child = copy(Object.hasOwn(target, key) ? target[key] : undefined);
38
+ setOwn(target, key, child);
39
+ target = child;
40
+ }
41
+ const key = change.path.at(-1);
42
+ setOwn(
43
+ target,
44
+ key,
45
+ replacement(Object.hasOwn(target, key) ? target[key] : undefined),
46
+ );
47
+ }
48
+ for (const value of owned) Object.freeze(value);
49
+ return next;
50
+ };
51
+
52
+ export const freezeJson = (value) => {
53
+ if (value !== null && typeof value === "object" && !Object.isFrozen(value)) {
54
+ for (const child of Object.values(value)) freezeJson(child);
55
+ Object.freeze(value);
56
+ }
57
+ return value;
58
+ };
@@ -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 parserError = ({ wasm, handle, maxBytes, maxDepth }, code) => {
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);
@@ -1,6 +1,7 @@
1
1
  import type {
2
- EventStructuredStream,
3
- StructuredStreamIntegration,
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: StructuredStreamIntegration<
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
- ): EventStructuredStream<LangChainToolCallMessage, string>;
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 = pool.start(id);
16
+ update = calls.start(id);
17
17
  }
18
18
  if (id !== undefined && chunk.args) {
19
- update = pool.push(id, chunk.args);
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
- EventStructuredStream,
3
- StructuredStreamIntegration,
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: StructuredStreamIntegration<
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
- ): EventStructuredStream<OpenAiResponsesToolEvent, string>;
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(pool, (event, complete) => {
6
- if (event.type === "response.output_item.added") {
7
- if (event.item.type !== "function_call") return undefined;
8
- return pool.start(event.item.id);
9
- }
10
- if (event.type === "response.function_call_arguments.delta") {
11
- return append(pool, event.item_id, event.delta);
12
- }
13
- if (event.type === "response.function_call_arguments.done") {
14
- if (!pool.has(event.item_id)) pool.start(event.item_id, event.arguments);
15
- return complete(event.item_id);
16
- }
17
- return undefined;
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,74 @@
1
+ import { createStructuredStreamPool } from "./index.js";
2
+ import { streamError } from "./internal/errors.js";
3
+ import { abortableSource } from "./internal/abortable-source.js";
4
+
5
+ export async function* readStructured(
6
+ events,
7
+ { adapter, integration, limits, onDiagnostic, signal },
8
+ ) {
9
+ if (
10
+ (adapter === undefined) === (integration === undefined) ||
11
+ (adapter !== undefined && typeof adapter !== "function") ||
12
+ (integration !== undefined && typeof integration !== "function")
13
+ ) {
14
+ throw streamError(
15
+ new TypeError("Choose exactly one adapter or integration factory"),
16
+ "INVALID_OPTIONS",
17
+ );
18
+ }
19
+ let pool;
20
+ let stream;
21
+ let source;
22
+ let failed = false;
23
+ const dispose = () => {
24
+ if (pool === undefined) stream?.dispose();
25
+ else for (const id of pool.activeIds) pool.abort(id);
26
+ };
27
+ try {
28
+ signal?.throwIfAborted();
29
+ if (integration !== undefined) {
30
+ pool = createStructuredStreamPool(limits);
31
+ stream = integration(pool, { onDiagnostic });
32
+ } else {
33
+ stream = adapter(
34
+ onDiagnostic === undefined ? limits : { ...limits, onDiagnostic },
35
+ );
36
+ }
37
+ signal?.throwIfAborted();
38
+ source = signal === undefined ? undefined : abortableSource(events, signal, dispose);
39
+ for await (const event of source ?? events) {
40
+ signal?.throwIfAborted();
41
+ for (const update of stream.pushAll(event)) {
42
+ signal?.throwIfAborted();
43
+ yield update;
44
+ }
45
+ }
46
+ signal?.throwIfAborted();
47
+ if (pool === undefined) {
48
+ for (const completed of stream.finish()) {
49
+ signal?.throwIfAborted();
50
+ yield completed;
51
+ }
52
+ } else {
53
+ // SDK finish() appends pending calls to its legacy completion history.
54
+ const pending = pool.size;
55
+ const completed = stream.finish();
56
+ if (pending > 0) {
57
+ for (const result of completed.slice(-pending)) {
58
+ signal?.throwIfAborted();
59
+ yield { type: "complete", ...result };
60
+ }
61
+ }
62
+ }
63
+ } catch (error) {
64
+ failed = true;
65
+ throw error;
66
+ } finally {
67
+ try { dispose(); }
68
+ catch (error) { if (!failed) throw error; }
69
+ finally {
70
+ try { await source?.dispose(); }
71
+ catch (error) { if (!failed) throw error; }
72
+ }
73
+ }
74
+ }
@@ -0,0 +1,13 @@
1
+ import type { StructuredStreamAdapter, StructuredStreamOperation } from "./index.js";
2
+
3
+ export interface AdapterContractTest {
4
+ readonly name: string;
5
+ run(): Promise<void>;
6
+ }
7
+
8
+ /** Node-only helpers for node:test, Vitest, and other Node-based test runners. */
9
+ export function adapterContractTests<Event>(options: {
10
+ readonly adapter: StructuredStreamAdapter<Event, string>;
11
+ /** Encode every operation in order; may split or batch your decoded events. */
12
+ readonly encode: (operations: readonly StructuredStreamOperation<string>[]) => Iterable<Event>;
13
+ }): readonly AdapterContractTest[];
package/src/testing.js ADDED
@@ -0,0 +1,121 @@
1
+ import assert from "node:assert/strict";
2
+ import { readStructured } from "./read-structured.js";
3
+
4
+ /** Contract tests for defineAdapter-compatible factories, not legacy SDK adapters. */
5
+ export function adapterContractTests({ adapter, encode }) {
6
+ const start = (id) => ({ type: "start", id });
7
+ const delta = (id, text) => ({ type: "delta", id, text });
8
+ const end = (id) => ({ type: "end", id });
9
+ const push = (session, operations) => {
10
+ const updates = [];
11
+ for (const event of encode(operations)) updates.push(...session.pushAll(event));
12
+ return updates;
13
+ };
14
+ const check = (name, run) => ({
15
+ name,
16
+ async run() {
17
+ const session = adapter({ snapshots: "immutable" });
18
+ try { await run(session); }
19
+ finally { session.dispose(); }
20
+ },
21
+ });
22
+ return [
23
+ check("interleaved calls preserve lifecycle order and values", (session) => {
24
+ const updates = push(session, [
25
+ start("a"), start("b"), delta("a", '{"city":"San'),
26
+ delta("b", "42"), delta("a", ' Francisco"}'), end("b"), end("a"),
27
+ ]);
28
+ assert.deepEqual(updates.map(({ type, id }) => [type, id]), [
29
+ ["start", "a"], ["start", "b"], ["update", "a"], ["update", "b"],
30
+ ["update", "a"], ["complete", "b"], ["complete", "a"],
31
+ ]);
32
+ assert.deepEqual(updates[2].partialValue, { city: "San" });
33
+ assert.equal(updates[5].value, 42);
34
+ assert.deepEqual(updates[6].value, { city: "San Francisco" });
35
+ assert.deepEqual(session.finish(), []);
36
+ }),
37
+ check("immutable snapshots retain nested values across later events", (session) => {
38
+ const first = push(session, [start("a"), delta("a", '{"items":[{"name":"A')]).at(-1);
39
+ push(session, [delta("a", 'B"}]}'), end("a")]);
40
+ assert.deepEqual(first.partialValue, { items: [{ name: "A" }] });
41
+ assert.ok(Object.isFrozen(first.partialValue));
42
+ assert.ok(Object.isFrozen(first.partialValue.items[0]));
43
+ }),
44
+ check("EOF finishes pending calls exactly once", (session) => {
45
+ push(session, [start("a"), delta("a", "null"), start("b"), delta("b", "[]"), end("b")]);
46
+ const completed = session.finish();
47
+ assert.equal(completed.length, 1);
48
+ assert.equal(completed[0].id, "a");
49
+ assert.equal(completed[0].type, "complete");
50
+ assert.equal(completed[0].value, null);
51
+ assert.deepEqual(session.finish(), []);
52
+ }),
53
+ check("abort abandons incomplete JSON and allows ID reuse", (session) => {
54
+ const updates = push(session, [
55
+ start("a"), delta("a", "{"), { type: "abort", id: "a" },
56
+ start("a"), delta("a", "true"), end("a"),
57
+ start("a"), delta("a", "false"), end("a"),
58
+ ]);
59
+ assert.deepEqual(updates.filter(({ type }) => type === "complete").map(({ value }) => value), [true, false]);
60
+ assert.deepEqual(session.finish(), []);
61
+ }),
62
+ check("malformed input fails terminally instead of emitting a completion", (session) => {
63
+ assert.throws(() => push(session, [start("a"), delta("a", "{]")]), SyntaxError);
64
+ assert.throws(() => session.finish());
65
+ assert.throws(() => push(session, [start("b")]));
66
+ }),
67
+ check("unknown calls and duplicate starts are rejected", (session) => {
68
+ assert.throws(() => push(session, [delta("missing", "{}")]), { code: "UNKNOWN_STREAM" });
69
+ const other = adapter();
70
+ try {
71
+ assert.throws(() => push(other, [start("a"), start("a")]), { code: "DUPLICATE_STREAM" });
72
+ } finally { other.dispose(); }
73
+ }),
74
+ check("sessions are isolated and dispose is idempotent", (session) => {
75
+ push(session, [start("a"), delta("a", "{")]);
76
+ const other = adapter();
77
+ try {
78
+ const updates = push(other, [start("a"), delta("a", "42"), end("a")]);
79
+ assert.equal(updates.at(-1).value, 42);
80
+ } finally { other.dispose(); }
81
+ session.dispose();
82
+ session.dispose();
83
+ assert.throws(() => push(session, [start("b")]), { code: "STREAM_DISPOSED" });
84
+ }),
85
+ check("configured parser limits are forwarded", () => {
86
+ const limited = adapter({ maxActiveStreams: 1, maxBytes: 2 });
87
+ try {
88
+ assert.throws(() => push(limited, [start("a"), start("b")]), { code: "MAX_ACTIVE_STREAMS_EXCEEDED" });
89
+ } finally { limited.dispose(); }
90
+ const bytes = adapter({ maxBytes: 2 });
91
+ try {
92
+ assert.throws(() => push(bytes, [start("a"), delta("a", "true")]), { code: "MAX_BYTES_EXCEEDED" });
93
+ } finally { bytes.dispose(); }
94
+ }),
95
+ {
96
+ name: "managed early exit closes the source and disposes its session",
97
+ async run() {
98
+ let closed = false;
99
+ let disposed = false;
100
+ function* source() {
101
+ try { yield* encode([start("a"), delta("a", "{")]); }
102
+ finally { closed = true; }
103
+ }
104
+ const factory = (options) => {
105
+ const session = adapter(options);
106
+ return {
107
+ pushAll: (event) => session.pushAll(event),
108
+ finish: () => session.finish(),
109
+ dispose() { disposed = true; session.dispose(); },
110
+ };
111
+ };
112
+ for await (const update of readStructured(source(), { adapter: factory })) {
113
+ assert.equal(update.type, "start");
114
+ break;
115
+ }
116
+ assert.equal(closed, true);
117
+ assert.equal(disposed, true);
118
+ },
119
+ },
120
+ ];
121
+ }
@@ -1,6 +1,7 @@
1
1
  import type {
2
- EventStructuredStream,
3
- StructuredStreamIntegration,
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: StructuredStreamIntegration<
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
- ): EventStructuredStream<VercelAiToolInputEvent, string>;
53
+ options?: StructuredStreamIntegrationOptions,
54
+ ): BatchEventStructuredStream<VercelAiToolInputEvent, string>;