ai-stream-utils 2.0.1 → 2.2.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 +81 -15
- package/dist/index.d.mts +156 -5
- package/dist/index.mjs +59 -1
- package/dist/{types-B4nePmEd.d.mts → types-CNApJ39t.d.mts} +2 -2
- package/dist/utils/index.d.mts +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -15,9 +15,9 @@ This library provides composable filter and transformation utilities for UI mess
|
|
|
15
15
|
|
|
16
16
|
The AI SDK UI message stream created by [`toUIMessageStream()`](https://ai-sdk.dev/docs/reference/ai-sdk-core/stream-text#to-ui-message-stream) streams all parts (text, tools, reasoning, etc.) to the client by default. However, you may want to:
|
|
17
17
|
|
|
18
|
-
- **Filter**: Tool calls like database
|
|
18
|
+
- **Filter**: Tool calls like database searches often contain large amounts of data or sensitive information that should not be streamed to the client
|
|
19
19
|
- **Transform**: Modify text or tool outputs while they are streamed to the client
|
|
20
|
-
- **Observe**: Log stream lifecycle events,
|
|
20
|
+
- **Observe**: Log stream lifecycle events, update states, or run side-effects without modifying the stream
|
|
21
21
|
|
|
22
22
|
This library provides type-safe, composable utilities for all these use cases.
|
|
23
23
|
|
|
@@ -52,21 +52,56 @@ const stream = pipe(result.toUIMessageStream())
|
|
|
52
52
|
.toStream();
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
#### Type Guards
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
- `excludeChunks("text-delta")` or `excludeChunks(["text-delta", "text-end"])`: Exclude specific chunk types
|
|
59
|
-
- `includeParts("text")` or `includeParts(["text", "reasoning"])`: Include specific part types
|
|
60
|
-
- `excludeParts("reasoning")` or `excludeParts(["reasoning", "tool-database"])`: Exclude specific part types
|
|
57
|
+
Generic type guards provide a simpler API for common filtering patterns:
|
|
61
58
|
|
|
62
|
-
|
|
59
|
+
- `includeChunks("text-delta")` or `includeChunks(["text-delta", "text-end"])`: Include **only** specific chunk types
|
|
60
|
+
- `excludeChunks("text-delta")` or `excludeChunks(["text-delta", "text-end"])`: Exclude **only** specific chunk types
|
|
61
|
+
- `includeParts("text")` or `includeParts(["text", "reasoning"])`: Include **only** specific part types
|
|
62
|
+
- `excludeParts("reasoning")` or `excludeParts(["reasoning", "tool-database"])`: Exclude **only** specific part types
|
|
63
|
+
|
|
64
|
+
Filtering tools is the most common use case and the tool-filter type guards provide a convenient API for filtering tool chunks by tool name:
|
|
65
|
+
|
|
66
|
+
- `excludeTools()` or `excludeTools("weather")` or `excludeTools(["weather", "database"])`: Exclude all tools or specific tools by name
|
|
67
|
+
- `includeTools()` or `includeTools("weather")` or `includeTools(["weather", "database"])`: Include all tools or specific tools by name
|
|
68
|
+
|
|
69
|
+
> [!NOTE]
|
|
70
|
+
> The tool-filter type guards only affect tool chunks. Non-tool chunks will pass through.
|
|
71
|
+
|
|
72
|
+
#### Examples
|
|
73
|
+
|
|
74
|
+
Exclude tool calls from the client.
|
|
63
75
|
|
|
64
76
|
```typescript
|
|
77
|
+
// Exclude by part type (requires "tool-" prefix)
|
|
65
78
|
const stream = pipe(result.toUIMessageStream())
|
|
66
79
|
.filter(excludeParts(["tool-weather", "tool-database"]))
|
|
67
80
|
.toStream();
|
|
81
|
+
|
|
82
|
+
// Exclude by tool name (without "tool-" prefix)
|
|
83
|
+
const stream = pipe(result.toUIMessageStream())
|
|
84
|
+
.filter(excludeTools(["weather", "database"]))
|
|
85
|
+
.toStream();
|
|
86
|
+
|
|
87
|
+
// Exclude all tools
|
|
88
|
+
const stream = pipe(result.toUIMessageStream()).filter(excludeTools()).toStream();
|
|
89
|
+
|
|
90
|
+
// Include only specific tools (without "tool-" prefix)
|
|
91
|
+
const stream = pipe(result.toUIMessageStream())
|
|
92
|
+
.filter(includeTools(["weather"]))
|
|
93
|
+
.toStream();
|
|
68
94
|
```
|
|
69
95
|
|
|
96
|
+
> [!NOTE]
|
|
97
|
+
> `excludeTools()` and `includeTools()` filters tool chunks on the server before streaming to the client. This affects all tool types including:
|
|
98
|
+
>
|
|
99
|
+
> - Server-side tools with `execute` functions
|
|
100
|
+
> - Client-side tools without `execute` functions
|
|
101
|
+
> - Tools that require human approval via `needsApproval`
|
|
102
|
+
>
|
|
103
|
+
> Excluded tools will not appear in the client's message parts, so users won't see tool call UI or be able to approve/reject filtered tools.
|
|
104
|
+
|
|
70
105
|
### `.map()`
|
|
71
106
|
|
|
72
107
|
Transform chunks by returning a chunk, an array of chunks, or `null` to exclude.
|
|
@@ -94,7 +129,9 @@ const stream = pipe(result.toUIMessageStream())
|
|
|
94
129
|
.toStream();
|
|
95
130
|
```
|
|
96
131
|
|
|
97
|
-
|
|
132
|
+
#### Examples
|
|
133
|
+
|
|
134
|
+
Convert text to uppercase.
|
|
98
135
|
|
|
99
136
|
```typescript
|
|
100
137
|
const stream = pipe(result.toUIMessageStream())
|
|
@@ -127,30 +164,59 @@ const stream = pipe(result.toUIMessageStream())
|
|
|
127
164
|
.toStream();
|
|
128
165
|
```
|
|
129
166
|
|
|
167
|
+
#### Type Guards
|
|
168
|
+
|
|
130
169
|
**Type guard** provides a type-safe way to observe specific chunk types:
|
|
131
170
|
|
|
132
171
|
- `chunkType("text-delta")` or `chunkType(["start", "finish"])`: Observe specific chunk types
|
|
133
172
|
- `partType("text")` or `partType(["text", "reasoning"])`: Observe chunks belonging to specific part types
|
|
173
|
+
- `toolCall()` or `toolCall({ tool: "weather" })` or `toolCall({ state: "output-available" })`: Observe tool state transitions
|
|
174
|
+
|
|
175
|
+
The `toolCall()` guard matches tool chunks representing state transitions (not streaming events):
|
|
176
|
+
|
|
177
|
+
- `input-available`: Tool input fully parsed
|
|
178
|
+
- `approval-requested`: Tool awaiting user approval
|
|
179
|
+
- `output-available`: Tool execution completed
|
|
180
|
+
- `output-error`: Tool execution failed
|
|
181
|
+
- `output-denied`: User denied approval
|
|
134
182
|
|
|
135
183
|
> [!NOTE]
|
|
136
184
|
> The `partType` type guard still operates on chunks. That means `partType("text")` will match any text chunks such as `text-start`, `text-delta`, and `text-end`.
|
|
137
185
|
|
|
138
|
-
|
|
186
|
+
#### Examples
|
|
187
|
+
|
|
188
|
+
Log stream lifecycle events.
|
|
139
189
|
|
|
140
190
|
```typescript
|
|
141
191
|
const stream = pipe(result.toUIMessageStream())
|
|
142
|
-
.on(chunkType("start"), () => {
|
|
143
|
-
console.log("Stream started");
|
|
192
|
+
.on(chunkType("start"), ({ chunk }) => {
|
|
193
|
+
console.log("Stream started:", chunk.messageId);
|
|
144
194
|
})
|
|
145
195
|
.on(chunkType("finish"), ({ chunk }) => {
|
|
146
196
|
console.log("Stream finished:", chunk.finishReason);
|
|
147
197
|
})
|
|
148
198
|
.on(chunkType("tool-input-available"), ({ chunk }) => {
|
|
149
|
-
console.log("Tool
|
|
199
|
+
console.log("Tool input:", chunk.toolName, chunk.input);
|
|
200
|
+
})
|
|
201
|
+
.on(chunkType("tool-output-available"), ({ chunk }) => {
|
|
202
|
+
console.log("Tool output:", chunk.toolName, chunk.output);
|
|
150
203
|
})
|
|
151
204
|
.toStream();
|
|
152
205
|
```
|
|
153
206
|
|
|
207
|
+
Observe tool state transitions.
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
const stream = pipe(result.toUIMessageStream())
|
|
211
|
+
.on(toolCall({ tool: "weather", state: "approval-requested" }), ({ chunk }) => {
|
|
212
|
+
console.log("Weather tool needs approval");
|
|
213
|
+
})
|
|
214
|
+
.toStream();
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
> [!NOTE]
|
|
218
|
+
> The `tool` option filters by part type (`tool-{name}`). Dynamic tools have part type `dynamic-tool`, so `toolCall({ tool: "myTool" })` will not match dynamic tools. Use `toolCall()` without the `tool` option to observe all tools including dynamic ones.
|
|
219
|
+
|
|
154
220
|
### `.toStream()`
|
|
155
221
|
|
|
156
222
|
Convert the pipeline back to a `AsyncIterableStream<InferUIMessageChunk<UI_MESSAGE>>` that can be returned to the client or consumed.
|
|
@@ -183,8 +249,8 @@ Multiple operators can be chained together. After filtering with type guards, ch
|
|
|
183
249
|
const stream = pipe<MyUIMessage>(result.toUIMessageStream())
|
|
184
250
|
.filter(includeParts("text"))
|
|
185
251
|
.map(({ chunk, part }) => {
|
|
186
|
-
// chunk is narrowed to text chunks
|
|
187
|
-
// part
|
|
252
|
+
// chunk is narrowed to text chunks: "text-start" | "text-delta" | "text-end"
|
|
253
|
+
// part is narrowed to "text"
|
|
188
254
|
return chunk;
|
|
189
255
|
})
|
|
190
256
|
.toStream();
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { a as convertSSEToUIMessageStream, c as convertArrayToStream, i as convertStreamToArray, l as convertArrayToAsyncIterable, n as createAsyncIterableStream, o as convertAsyncIterableToStream, r as convertUIMessageToSSEStream, s as convertAsyncIterableToArray, t as AsyncIterableStream } from "./types-
|
|
1
|
+
import { a as convertSSEToUIMessageStream, c as convertArrayToStream, i as convertStreamToArray, l as convertArrayToAsyncIterable, n as createAsyncIterableStream, o as convertAsyncIterableToStream, r as convertUIMessageToSSEStream, s as convertAsyncIterableToArray, t as AsyncIterableStream } from "./types-CNApJ39t.mjs";
|
|
2
2
|
import "./utils/index.mjs";
|
|
3
|
-
import { AsyncIterableStream as AsyncIterableStream$1, InferUIMessageChunk, UIMessage } from "ai";
|
|
3
|
+
import { AsyncIterableStream as AsyncIterableStream$1, InferUIMessageChunk, UIDataTypes, UIMessage, UITools } from "ai";
|
|
4
4
|
|
|
5
5
|
//#region src/consume/consume-ui-message-stream.d.ts
|
|
6
6
|
/**
|
|
@@ -181,6 +181,7 @@ declare function filterUIMessageStream<UI_MESSAGE extends UIMessage>(stream: Rea
|
|
|
181
181
|
type InferUIMessagePart<UI_MESSAGE extends UIMessage> = UI_MESSAGE["parts"][number];
|
|
182
182
|
type InferUIMessagePartType<UI_MESSAGE extends UIMessage> = InferUIMessagePart<UI_MESSAGE>["type"];
|
|
183
183
|
type InferUIMessageChunkType<UI_MESSAGE extends UIMessage> = InferUIMessageChunk<UI_MESSAGE>["type"];
|
|
184
|
+
type InferUIMessageTools<UI_MESSAGE extends UIMessage> = UI_MESSAGE extends UIMessage<unknown, UIDataTypes, infer TOOLS> ? TOOLS : UITools;
|
|
184
185
|
/**
|
|
185
186
|
* Extracts chunk type strings that match the prefix exactly or as `${PREFIX}-*`.
|
|
186
187
|
* Dynamically derives chunk types from the actual UIMessageChunk union.
|
|
@@ -318,6 +319,47 @@ type InferPartForChunk<UI_MESSAGE extends UIMessage, CHUNK_TYPE extends string>
|
|
|
318
319
|
* ```
|
|
319
320
|
*/
|
|
320
321
|
type ExcludePartForChunks<UI_MESSAGE extends UIMessage, EXCLUDED_CHUNK_TYPE extends string> = { [PT in InferUIMessagePartType<UI_MESSAGE>]: [Exclude<PartTypeToChunkTypes<UI_MESSAGE, PT>, EXCLUDED_CHUNK_TYPE>] extends [never] ? never : PT }[InferUIMessagePartType<UI_MESSAGE>];
|
|
322
|
+
/**
|
|
323
|
+
* Extract tool names from UIMessage tools.
|
|
324
|
+
*
|
|
325
|
+
* @example
|
|
326
|
+
* ```typescript
|
|
327
|
+
* type Tools = InferToolName<MyUIMessage>;
|
|
328
|
+
* // => 'weather' | 'calculator'
|
|
329
|
+
* ```
|
|
330
|
+
*/
|
|
331
|
+
type InferToolName<UI_MESSAGE extends UIMessage> = keyof InferUIMessageTools<UI_MESSAGE> & string;
|
|
332
|
+
/**
|
|
333
|
+
* All tool-related part types (tool-* and dynamic-tool).
|
|
334
|
+
*/
|
|
335
|
+
type ToolPartTypes<UI_MESSAGE extends UIMessage> = Extract<InferUIMessagePartType<UI_MESSAGE>, `tool-${string}` | `dynamic-tool`>;
|
|
336
|
+
/**
|
|
337
|
+
* Part types remaining after excluding all tools.
|
|
338
|
+
*/
|
|
339
|
+
type ExcludeToolPartTypes<UI_MESSAGE extends UIMessage> = Exclude<InferUIMessagePartType<UI_MESSAGE>, ToolPartTypes<UI_MESSAGE>>;
|
|
340
|
+
/**
|
|
341
|
+
* Chunk types for tools (tool-input-*, tool-output-*, etc.).
|
|
342
|
+
*/
|
|
343
|
+
type ToolChunkTypes<UI_MESSAGE extends UIMessage> = Extract<InferUIMessageChunkType<UI_MESSAGE>, `tool-${string}`>;
|
|
344
|
+
/**
|
|
345
|
+
* Content chunk types remaining after excluding all tool chunks.
|
|
346
|
+
*/
|
|
347
|
+
type ExcludeToolChunkTypes<UI_MESSAGE extends UIMessage> = Exclude<ContentChunkType<UI_MESSAGE>, ToolChunkTypes<UI_MESSAGE>>;
|
|
348
|
+
/**
|
|
349
|
+
* Tool states that have corresponding stream chunks.
|
|
350
|
+
* These are the "final" states from UIToolInvocation, not streaming events.
|
|
351
|
+
*/
|
|
352
|
+
type ToolCallState = "input-available" | "approval-requested" | "output-available" | "output-error" | "output-denied";
|
|
353
|
+
/**
|
|
354
|
+
* Map tool states to their corresponding chunk types.
|
|
355
|
+
*/
|
|
356
|
+
type ToolStateToChunkType = {
|
|
357
|
+
"input-available": "tool-input-available";
|
|
358
|
+
"approval-requested": "tool-approval-request";
|
|
359
|
+
"output-available": "tool-output-available";
|
|
360
|
+
"output-error": "tool-output-error";
|
|
361
|
+
"output-denied": "tool-output-denied";
|
|
362
|
+
};
|
|
321
363
|
//#endregion
|
|
322
364
|
//#region src/flat-map/flat-map-ui-message-stream.d.ts
|
|
323
365
|
/**
|
|
@@ -549,8 +591,8 @@ declare class ChunkPipeline<UI_MESSAGE extends UIMessage, CHUNK extends InferUIM
|
|
|
549
591
|
* Content chunks include a part object with the type, while meta chunks have undefined part.
|
|
550
592
|
* All chunks pass through regardless of whether the callback is invoked.
|
|
551
593
|
*/
|
|
552
|
-
on<NARROWED_CHUNK extends
|
|
553
|
-
type:
|
|
594
|
+
on<NARROWED_CHUNK extends InferUIMessageChunk<UI_MESSAGE>, NARROWED_PART extends {
|
|
595
|
+
type: string;
|
|
554
596
|
} | undefined>(guard: ObserveGuard<UI_MESSAGE, NARROWED_CHUNK, NARROWED_PART>, callback: ChunkObserveFn<NARROWED_CHUNK, NARROWED_PART>): ChunkPipeline<UI_MESSAGE, CHUNK, PART>;
|
|
555
597
|
/**
|
|
556
598
|
* Observes chunks matching a predicate without filtering them.
|
|
@@ -707,5 +749,114 @@ declare function chunkType<UI_MESSAGE extends UIMessage, CHUNK_TYPE extends Infe
|
|
|
707
749
|
declare function partType<UI_MESSAGE extends UIMessage, PART_TYPE extends InferUIMessagePartType<UI_MESSAGE>>(types: PART_TYPE | Array<PART_TYPE>): ObserveGuard<UI_MESSAGE, ExtractChunkForPart<UI_MESSAGE, ExtractPart<UI_MESSAGE, PART_TYPE>>, {
|
|
708
750
|
type: PART_TYPE;
|
|
709
751
|
}>;
|
|
752
|
+
/**
|
|
753
|
+
* Creates a filter guard that includes only specific tools while keeping non-tool chunks.
|
|
754
|
+
* Use with `.filter()` to include only specific tools.
|
|
755
|
+
*
|
|
756
|
+
* @example
|
|
757
|
+
* ```typescript
|
|
758
|
+
* // No-op: all chunks pass through
|
|
759
|
+
* pipe<MyUIMessage>(stream)
|
|
760
|
+
* .filter(includeTools())
|
|
761
|
+
* .map(({ chunk }) => chunk); // all chunks pass
|
|
762
|
+
*
|
|
763
|
+
* // Include specific tool (non-tool chunks still pass)
|
|
764
|
+
* pipe<MyUIMessage>(stream)
|
|
765
|
+
* .filter(includeTools('weather'))
|
|
766
|
+
* .map(({ chunk }) => chunk); // text + tool-weather chunks
|
|
767
|
+
*
|
|
768
|
+
* // Include multiple tools
|
|
769
|
+
* pipe<MyUIMessage>(stream)
|
|
770
|
+
* .filter(includeTools(['weather', 'calculator']))
|
|
771
|
+
* .map(({ chunk }) => chunk); // text + specified tool chunks
|
|
772
|
+
* ```
|
|
773
|
+
*/
|
|
774
|
+
declare function includeTools<UI_MESSAGE extends UIMessage>(): FilterGuard<UI_MESSAGE, InferUIMessageChunk<UI_MESSAGE>, {
|
|
775
|
+
type: InferUIMessagePartType<UI_MESSAGE>;
|
|
776
|
+
}>;
|
|
777
|
+
declare function includeTools<UI_MESSAGE extends UIMessage, TOOL_NAME extends InferToolName<UI_MESSAGE>>(toolNames: TOOL_NAME | Array<TOOL_NAME>): FilterGuard<UI_MESSAGE, InferUIMessageChunk<UI_MESSAGE>, {
|
|
778
|
+
type: ExcludeToolPartTypes<UI_MESSAGE> | `tool-${TOOL_NAME}`;
|
|
779
|
+
}>;
|
|
780
|
+
/**
|
|
781
|
+
* Creates a filter guard that excludes all or only specific tools while keeping non-tool chunks.
|
|
782
|
+
* Use with `.filter()` to exclude specific tools or all tools.
|
|
783
|
+
*
|
|
784
|
+
* @example
|
|
785
|
+
* ```typescript
|
|
786
|
+
* // Exclude all tools
|
|
787
|
+
* pipe<MyUIMessage>(stream)
|
|
788
|
+
* .filter(excludeTools())
|
|
789
|
+
* .map(({ chunk }) => chunk); // no tool chunks
|
|
790
|
+
*
|
|
791
|
+
* // Exclude specific tool
|
|
792
|
+
* pipe<MyUIMessage>(stream)
|
|
793
|
+
* .filter(excludeTools('weather'))
|
|
794
|
+
* .map(({ chunk }) => chunk); // excludes tool-weather chunks
|
|
795
|
+
*
|
|
796
|
+
* // Exclude multiple tools
|
|
797
|
+
* pipe<MyUIMessage>(stream)
|
|
798
|
+
* .filter(excludeTools(['weather', 'calculator']))
|
|
799
|
+
* .map(({ chunk }) => chunk); // excludes weather and calculator
|
|
800
|
+
* ```
|
|
801
|
+
*/
|
|
802
|
+
declare function excludeTools<UI_MESSAGE extends UIMessage>(): FilterGuard<UI_MESSAGE, ExtractChunk<UI_MESSAGE, ExcludeToolChunkTypes<UI_MESSAGE>>, {
|
|
803
|
+
type: ExcludeToolPartTypes<UI_MESSAGE>;
|
|
804
|
+
}>;
|
|
805
|
+
declare function excludeTools<UI_MESSAGE extends UIMessage, TOOL_NAME extends InferToolName<UI_MESSAGE>>(toolNames: TOOL_NAME | Array<TOOL_NAME>): FilterGuard<UI_MESSAGE, InferUIMessageChunk<UI_MESSAGE>, {
|
|
806
|
+
type: Exclude<InferUIMessagePartType<UI_MESSAGE>, `tool-${TOOL_NAME}`>;
|
|
807
|
+
}>;
|
|
808
|
+
/**
|
|
809
|
+
* Creates an observe guard that matches tool call chunks by tool name and/or state.
|
|
810
|
+
* Use with `.on()` to observe tool call state transitions without filtering.
|
|
811
|
+
*
|
|
812
|
+
* @example
|
|
813
|
+
* ```typescript
|
|
814
|
+
* // Match all tool state transitions (any tool, any state)
|
|
815
|
+
* pipe<MyUIMessage>(stream)
|
|
816
|
+
* .on(toolCall(), ({ chunk, part }) => {
|
|
817
|
+
* // Observes: tool-input-available, tool-approval-request,
|
|
818
|
+
* // tool-output-available, tool-output-error, tool-output-denied
|
|
819
|
+
* });
|
|
820
|
+
*
|
|
821
|
+
* // Match specific tool (any state)
|
|
822
|
+
* pipe<MyUIMessage>(stream)
|
|
823
|
+
* .on(toolCall({ tool: "weather" }), ({ chunk, part }) => {
|
|
824
|
+
* // part.type is 'tool-weather'
|
|
825
|
+
* });
|
|
826
|
+
*
|
|
827
|
+
* // Match specific state (all tools)
|
|
828
|
+
* pipe<MyUIMessage>(stream)
|
|
829
|
+
* .on(toolCall({ state: "output-available" }), ({ chunk, part }) => {
|
|
830
|
+
* // chunk.type is 'tool-output-available'
|
|
831
|
+
* });
|
|
832
|
+
*
|
|
833
|
+
* // Match specific tool AND state
|
|
834
|
+
* pipe<MyUIMessage>(stream)
|
|
835
|
+
* .on(toolCall({ tool: "weather", state: "output-available" }), ({ chunk, part }) => {
|
|
836
|
+
* // chunk.type is 'tool-output-available', part.type is 'tool-weather'
|
|
837
|
+
* });
|
|
838
|
+
* ```
|
|
839
|
+
*/
|
|
840
|
+
declare function toolCall<UI_MESSAGE extends UIMessage>(): ObserveGuard<UI_MESSAGE, ExtractChunk<UI_MESSAGE, ToolStateToChunkType[ToolCallState]>, {
|
|
841
|
+
type: `tool-${InferToolName<UI_MESSAGE>}` | "dynamic-tool";
|
|
842
|
+
}>;
|
|
843
|
+
declare function toolCall<UI_MESSAGE extends UIMessage, TOOL_NAME extends InferToolName<UI_MESSAGE>, STATE extends ToolCallState>(options: {
|
|
844
|
+
tool: TOOL_NAME;
|
|
845
|
+
state: STATE;
|
|
846
|
+
}): ObserveGuard<UI_MESSAGE, ExtractChunk<UI_MESSAGE, ToolStateToChunkType[STATE]>, {
|
|
847
|
+
type: `tool-${TOOL_NAME}`;
|
|
848
|
+
}>;
|
|
849
|
+
declare function toolCall<UI_MESSAGE extends UIMessage, TOOL_NAME extends InferToolName<UI_MESSAGE>>(options: {
|
|
850
|
+
tool: TOOL_NAME;
|
|
851
|
+
state?: undefined;
|
|
852
|
+
}): ObserveGuard<UI_MESSAGE, ExtractChunk<UI_MESSAGE, ToolStateToChunkType[ToolCallState]>, {
|
|
853
|
+
type: `tool-${TOOL_NAME}`;
|
|
854
|
+
}>;
|
|
855
|
+
declare function toolCall<UI_MESSAGE extends UIMessage, STATE extends ToolCallState>(options: {
|
|
856
|
+
tool?: undefined;
|
|
857
|
+
state: STATE;
|
|
858
|
+
}): ObserveGuard<UI_MESSAGE, ExtractChunk<UI_MESSAGE, ToolStateToChunkType[STATE]>, {
|
|
859
|
+
type: `tool-${InferToolName<UI_MESSAGE>}` | "dynamic-tool";
|
|
860
|
+
}>;
|
|
710
861
|
//#endregion
|
|
711
|
-
export { AsyncIterableStream, type FlatMapContext, type FlatMapInput, type FlatMapUIMessageStreamFn, type FlatMapUIMessageStreamPredicate, type MapInput, type MapUIMessageStreamFn, chunkType, consumeUIMessageStream, convertArrayToAsyncIterable, convertArrayToStream, convertAsyncIterableToArray, convertAsyncIterableToStream, convertSSEToUIMessageStream, convertStreamToArray, convertUIMessageToSSEStream, createAsyncIterableStream, excludeChunks, excludeParts, filterUIMessageStream, flatMapUIMessageStream, includeChunks, includeParts, mapUIMessageStream, partType, partTypeIs, pipe };
|
|
862
|
+
export { AsyncIterableStream, type FlatMapContext, type FlatMapInput, type FlatMapUIMessageStreamFn, type FlatMapUIMessageStreamPredicate, type MapInput, type MapUIMessageStreamFn, chunkType, consumeUIMessageStream, convertArrayToAsyncIterable, convertArrayToStream, convertAsyncIterableToArray, convertAsyncIterableToStream, convertSSEToUIMessageStream, convertStreamToArray, convertUIMessageToSSEStream, createAsyncIterableStream, excludeChunks, excludeParts, excludeTools, filterUIMessageStream, flatMapUIMessageStream, includeChunks, includeParts, includeTools, mapUIMessageStream, partType, partTypeIs, pipe, toolCall };
|
package/dist/index.mjs
CHANGED
|
@@ -908,6 +908,64 @@ function partType(types) {
|
|
|
908
908
|
const guard = (input) => typeArray.includes(input.part?.type);
|
|
909
909
|
return guard;
|
|
910
910
|
}
|
|
911
|
+
function includeTools(toolNames) {
|
|
912
|
+
const toolNameArray = toolNames === void 0 ? void 0 : Array.isArray(toolNames) ? toolNames : [toolNames];
|
|
913
|
+
const guard = (input) => {
|
|
914
|
+
/** No args = no-op, all chunks pass */
|
|
915
|
+
if (toolNameArray === void 0) return true;
|
|
916
|
+
const partType = input.part?.type;
|
|
917
|
+
if (!partType) return true;
|
|
918
|
+
/** Non-tool chunks pass */
|
|
919
|
+
if (!partType.startsWith(`tool-`) && partType !== `dynamic-tool`) return true;
|
|
920
|
+
/** Only matching tool chunks pass */
|
|
921
|
+
for (const name of toolNameArray) if (partType === `tool-${name}`) return true;
|
|
922
|
+
return false;
|
|
923
|
+
};
|
|
924
|
+
return guard;
|
|
925
|
+
}
|
|
926
|
+
function excludeTools(toolNames) {
|
|
927
|
+
const toolNameArray = toolNames === void 0 ? void 0 : Array.isArray(toolNames) ? toolNames : [toolNames];
|
|
928
|
+
const guard = (input) => {
|
|
929
|
+
const partType = input.part?.type;
|
|
930
|
+
/** Meta chunks pass (no part type) */
|
|
931
|
+
if (!partType) return true;
|
|
932
|
+
/** No args = exclude all tool chunks */
|
|
933
|
+
if (toolNameArray === void 0) return !partType.startsWith(`tool-`) && partType !== `dynamic-tool`;
|
|
934
|
+
/** Exclude only matching tool chunks */
|
|
935
|
+
for (const name of toolNameArray) if (partType === `tool-${name}`) return false;
|
|
936
|
+
return true;
|
|
937
|
+
};
|
|
938
|
+
return guard;
|
|
939
|
+
}
|
|
940
|
+
/**
|
|
941
|
+
* Reverse mapping from chunk type to state.
|
|
942
|
+
*/
|
|
943
|
+
const chunkTypeToState = Object.fromEntries(Object.entries({
|
|
944
|
+
"input-available": "tool-input-available",
|
|
945
|
+
"approval-requested": "tool-approval-request",
|
|
946
|
+
"output-available": "tool-output-available",
|
|
947
|
+
"output-error": "tool-output-error",
|
|
948
|
+
"output-denied": "tool-output-denied"
|
|
949
|
+
}).map(([state, chunkType]) => [chunkType, state]));
|
|
950
|
+
function toolCall(options) {
|
|
951
|
+
const toolName = options?.tool;
|
|
952
|
+
const state = options?.state;
|
|
953
|
+
const guard = (input) => {
|
|
954
|
+
const chunkType = input.chunk.type;
|
|
955
|
+
const partType = input.part?.type;
|
|
956
|
+
/** Must be a tool chunk type that maps to a state */
|
|
957
|
+
const matchingState = chunkTypeToState[chunkType];
|
|
958
|
+
if (!matchingState) return false;
|
|
959
|
+
/** Check state match */
|
|
960
|
+
if (state && matchingState !== state) return false;
|
|
961
|
+
/** Check tool name match */
|
|
962
|
+
if (toolName && partType) {
|
|
963
|
+
if (partType !== `tool-${toolName}`) return false;
|
|
964
|
+
}
|
|
965
|
+
return true;
|
|
966
|
+
};
|
|
967
|
+
return guard;
|
|
968
|
+
}
|
|
911
969
|
|
|
912
970
|
//#endregion
|
|
913
|
-
export { chunkType, consumeUIMessageStream, convertArrayToAsyncIterable, convertArrayToStream, convertAsyncIterableToArray, convertAsyncIterableToStream, convertSSEToUIMessageStream, convertStreamToArray, convertUIMessageToSSEStream, createAsyncIterableStream, excludeChunks, excludeParts, filterUIMessageStream, flatMapUIMessageStream, includeChunks, includeParts, mapUIMessageStream, partType, partTypeIs, pipe };
|
|
971
|
+
export { chunkType, consumeUIMessageStream, convertArrayToAsyncIterable, convertArrayToStream, convertAsyncIterableToArray, convertAsyncIterableToStream, convertSSEToUIMessageStream, convertStreamToArray, convertUIMessageToSSEStream, createAsyncIterableStream, excludeChunks, excludeParts, excludeTools, filterUIMessageStream, flatMapUIMessageStream, includeChunks, includeParts, includeTools, mapUIMessageStream, partType, partTypeIs, pipe, toolCall };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AsyncIterableStream, AsyncIterableStream as AsyncIterableStream$1, UIMessageChunk } from "ai";
|
|
1
|
+
import { AsyncIterableStream, AsyncIterableStream as AsyncIterableStream$1, InferUIMessageChunk, UIMessage, UIMessageChunk } from "ai";
|
|
2
2
|
|
|
3
3
|
//#region src/utils/convert-array-to-async-iterable.d.ts
|
|
4
4
|
/**
|
|
@@ -29,7 +29,7 @@ declare function convertAsyncIterableToStream<T>(iterable: AsyncIterable<T>): Re
|
|
|
29
29
|
/**
|
|
30
30
|
* Converts an SSE stream to a UI message stream.
|
|
31
31
|
*/
|
|
32
|
-
declare function convertSSEToUIMessageStream(stream: ReadableStream<string>): ReadableStream<
|
|
32
|
+
declare function convertSSEToUIMessageStream<UI_MESSAGE extends UIMessage = UIMessage>(stream: ReadableStream<string>): ReadableStream<InferUIMessageChunk<UI_MESSAGE>>;
|
|
33
33
|
//#endregion
|
|
34
34
|
//#region src/utils/convert-stream-to-array.d.ts
|
|
35
35
|
/**
|
package/dist/utils/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as convertSSEToUIMessageStream, c as convertArrayToStream, i as convertStreamToArray, l as convertArrayToAsyncIterable, n as createAsyncIterableStream, o as convertAsyncIterableToStream, r as convertUIMessageToSSEStream, s as convertAsyncIterableToArray, t as AsyncIterableStream } from "../types-
|
|
1
|
+
import { a as convertSSEToUIMessageStream, c as convertArrayToStream, i as convertStreamToArray, l as convertArrayToAsyncIterable, n as createAsyncIterableStream, o as convertAsyncIterableToStream, r as convertUIMessageToSSEStream, s as convertAsyncIterableToArray, t as AsyncIterableStream } from "../types-CNApJ39t.mjs";
|
|
2
2
|
export { type AsyncIterableStream, convertArrayToAsyncIterable, convertArrayToStream, convertAsyncIterableToArray, convertAsyncIterableToStream, convertSSEToUIMessageStream, convertStreamToArray, convertUIMessageToSSEStream, createAsyncIterableStream };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-stream-utils",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "AI SDK: Filter and transform UI messages while streaming to the client",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"@ai-sdk/openai": "^3.0.29",
|
|
42
42
|
"@ai-sdk/provider": "^3.0.8",
|
|
43
43
|
"@ai-sdk/provider-utils": "^4.0.15",
|
|
44
|
+
"@ai-sdk/react": "^3.0.110",
|
|
44
45
|
"@arethetypeswrong/cli": "^0.18.2",
|
|
45
46
|
"@total-typescript/tsconfig": "^1.0.4",
|
|
46
47
|
"@types/node": "^25.2.3",
|