@ably/ai-transport 0.1.0 → 0.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 +91 -100
- package/dist/ably-ai-transport.js +1553 -1238
- package/dist/ably-ai-transport.js.map +1 -1
- package/dist/ably-ai-transport.umd.cjs +1 -1
- package/dist/ably-ai-transport.umd.cjs.map +1 -1
- package/dist/constants.d.ts +116 -42
- package/dist/core/agent.d.ts +29 -0
- package/dist/core/codec/decoder.d.ts +20 -23
- package/dist/core/codec/encoder.d.ts +11 -8
- package/dist/core/codec/index.d.ts +1 -2
- package/dist/core/codec/lifecycle-tracker.d.ts +10 -9
- package/dist/core/codec/types.d.ts +407 -115
- package/dist/core/transport/agent-session.d.ts +10 -0
- package/dist/core/transport/branch-chain.d.ts +43 -0
- package/dist/core/transport/client-session.d.ts +13 -0
- package/dist/core/transport/decode-fold.d.ts +47 -0
- package/dist/core/transport/headers.d.ts +96 -18
- package/dist/core/transport/index.d.ts +5 -6
- package/dist/core/transport/internal/bounded-map.d.ts +20 -0
- package/dist/core/transport/invocation.d.ts +74 -0
- package/dist/core/transport/load-conversation.d.ts +128 -0
- package/dist/core/transport/load-history.d.ts +39 -0
- package/dist/core/transport/pipe-stream.d.ts +9 -9
- package/dist/core/transport/run-manager.d.ts +78 -0
- package/dist/core/transport/tree.d.ts +373 -109
- package/dist/core/transport/types/agent.d.ts +353 -0
- package/dist/core/transport/types/client.d.ts +168 -0
- package/dist/core/transport/types/shared.d.ts +24 -0
- package/dist/core/transport/types/tree.d.ts +315 -0
- package/dist/core/transport/types/view.d.ts +222 -0
- package/dist/core/transport/types.d.ts +13 -553
- package/dist/core/transport/view.d.ts +272 -84
- package/dist/errors.d.ts +21 -10
- package/dist/index.d.ts +6 -8
- package/dist/logger.d.ts +12 -0
- package/dist/react/ably-ai-transport-react.js +976 -990
- package/dist/react/ably-ai-transport-react.js.map +1 -1
- package/dist/react/ably-ai-transport-react.umd.cjs +1 -1
- package/dist/react/ably-ai-transport-react.umd.cjs.map +1 -1
- package/dist/react/contexts/client-session-context.d.ts +36 -0
- package/dist/react/contexts/client-session-provider.d.ts +53 -0
- package/dist/react/create-session-hooks.d.ts +116 -0
- package/dist/react/index.d.ts +12 -12
- package/dist/react/internal/use-resolved-session.d.ts +36 -0
- package/dist/react/use-ably-messages.d.ts +17 -14
- package/dist/react/use-client-session.d.ts +81 -0
- package/dist/react/use-create-view.d.ts +14 -13
- package/dist/react/use-tree.d.ts +30 -15
- package/dist/react/use-view.d.ts +82 -51
- package/dist/utils.d.ts +32 -23
- package/dist/vercel/ably-ai-transport-vercel.js +2573 -2086
- package/dist/vercel/ably-ai-transport-vercel.js.map +1 -1
- package/dist/vercel/ably-ai-transport-vercel.umd.cjs +1 -1
- package/dist/vercel/ably-ai-transport-vercel.umd.cjs.map +1 -1
- package/dist/vercel/codec/decoder.d.ts +5 -18
- package/dist/vercel/codec/encoder.d.ts +6 -36
- package/dist/vercel/codec/events.d.ts +51 -0
- package/dist/vercel/codec/index.d.ts +24 -12
- package/dist/vercel/codec/reducer.d.ts +144 -0
- package/dist/vercel/codec/tool-transitions.d.ts +2 -2
- package/dist/vercel/index.d.ts +4 -5
- package/dist/vercel/react/ably-ai-transport-vercel-react.js +3907 -3266
- package/dist/vercel/react/ably-ai-transport-vercel-react.js.map +1 -1
- package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs +33 -8
- package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs.map +1 -1
- package/dist/vercel/react/contexts/chat-transport-context.d.ts +7 -6
- package/dist/vercel/react/contexts/chat-transport-provider.d.ts +53 -41
- package/dist/vercel/react/index.d.ts +1 -2
- package/dist/vercel/react/use-chat-transport.d.ts +30 -26
- package/dist/vercel/react/use-message-sync.d.ts +17 -30
- package/dist/vercel/run-end-reason.d.ts +29 -0
- package/dist/vercel/transport/chat-transport.d.ts +43 -24
- package/dist/vercel/transport/index.d.ts +25 -21
- package/dist/vercel/transport/run-output-stream.d.ts +56 -0
- package/dist/version.d.ts +2 -0
- package/package.json +30 -23
- package/src/constants.ts +124 -51
- package/src/core/agent.ts +68 -0
- package/src/core/codec/decoder.ts +71 -98
- package/src/core/codec/encoder.ts +113 -65
- package/src/core/codec/index.ts +13 -6
- package/src/core/codec/lifecycle-tracker.ts +10 -9
- package/src/core/codec/types.ts +436 -120
- package/src/core/transport/agent-session.ts +1344 -0
- package/src/core/transport/branch-chain.ts +58 -0
- package/src/core/transport/client-session.ts +775 -0
- package/src/core/transport/decode-fold.ts +91 -0
- package/src/core/transport/headers.ts +181 -22
- package/src/core/transport/index.ts +25 -26
- package/src/core/transport/internal/bounded-map.ts +27 -0
- package/src/core/transport/invocation.ts +98 -0
- package/src/core/transport/load-conversation.ts +355 -0
- package/src/core/transport/load-history.ts +269 -0
- package/src/core/transport/pipe-stream.ts +54 -39
- package/src/core/transport/run-manager.ts +249 -0
- package/src/core/transport/tree.ts +926 -308
- package/src/core/transport/types/agent.ts +407 -0
- package/src/core/transport/types/client.ts +211 -0
- package/src/core/transport/types/shared.ts +27 -0
- package/src/core/transport/types/tree.ts +344 -0
- package/src/core/transport/types/view.ts +259 -0
- package/src/core/transport/types.ts +13 -706
- package/src/core/transport/view.ts +864 -433
- package/src/errors.ts +22 -9
- package/src/event-emitter.ts +3 -2
- package/src/index.ts +52 -41
- package/src/logger.ts +14 -1
- package/src/react/contexts/client-session-context.ts +41 -0
- package/src/react/contexts/client-session-provider.tsx +186 -0
- package/src/react/create-session-hooks.ts +141 -0
- package/src/react/index.ts +23 -13
- package/src/react/internal/use-resolved-session.ts +63 -0
- package/src/react/use-ably-messages.ts +32 -22
- package/src/react/use-client-session.ts +201 -0
- package/src/react/use-create-view.ts +33 -29
- package/src/react/use-tree.ts +61 -30
- package/src/react/use-view.ts +139 -97
- package/src/utils.ts +63 -45
- package/src/vercel/codec/decoder.ts +336 -258
- package/src/vercel/codec/encoder.ts +343 -205
- package/src/vercel/codec/events.ts +87 -0
- package/src/vercel/codec/index.ts +60 -13
- package/src/vercel/codec/reducer.ts +977 -0
- package/src/vercel/codec/tool-transitions.ts +2 -2
- package/src/vercel/index.ts +6 -19
- package/src/vercel/react/contexts/chat-transport-context.ts +7 -6
- package/src/vercel/react/contexts/chat-transport-provider.tsx +87 -59
- package/src/vercel/react/index.ts +3 -5
- package/src/vercel/react/use-chat-transport.ts +47 -49
- package/src/vercel/react/use-message-sync.ts +80 -39
- package/src/vercel/run-end-reason.ts +78 -0
- package/src/vercel/transport/chat-transport.ts +392 -98
- package/src/vercel/transport/index.ts +39 -38
- package/src/vercel/transport/run-output-stream.ts +170 -0
- package/src/version.ts +2 -0
- package/dist/core/transport/client-transport.d.ts +0 -10
- package/dist/core/transport/decode-history.d.ts +0 -43
- package/dist/core/transport/server-transport.d.ts +0 -7
- package/dist/core/transport/stream-router.d.ts +0 -29
- package/dist/core/transport/turn-manager.d.ts +0 -37
- package/dist/react/contexts/transport-context.d.ts +0 -31
- package/dist/react/contexts/transport-provider.d.ts +0 -49
- package/dist/react/create-transport-hooks.d.ts +0 -124
- package/dist/react/use-active-turns.d.ts +0 -12
- package/dist/react/use-client-transport.d.ts +0 -80
- package/dist/vercel/codec/accumulator.d.ts +0 -21
- package/dist/vercel/react/use-staged-add-tool-approval-response.d.ts +0 -30
- package/dist/vercel/tool-approvals.d.ts +0 -124
- package/dist/vercel/tool-events.d.ts +0 -26
- package/src/core/transport/client-transport.ts +0 -977
- package/src/core/transport/decode-history.ts +0 -485
- package/src/core/transport/server-transport.ts +0 -612
- package/src/core/transport/stream-router.ts +0 -136
- package/src/core/transport/turn-manager.ts +0 -165
- package/src/react/contexts/transport-context.ts +0 -37
- package/src/react/contexts/transport-provider.tsx +0 -164
- package/src/react/create-transport-hooks.ts +0 -144
- package/src/react/use-active-turns.ts +0 -72
- package/src/react/use-client-transport.ts +0 -197
- package/src/vercel/codec/accumulator.ts +0 -588
- package/src/vercel/react/use-staged-add-tool-approval-response.ts +0 -87
- package/src/vercel/tool-approvals.ts +0 -380
- package/src/vercel/tool-events.ts +0 -53
|
@@ -1,21 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* useMessageSync:
|
|
2
|
+
* useMessageSync: wire view updates into useChat's setMessages.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* During active own-run streams, setMessages is gated to avoid an
|
|
5
|
+
* ID-mismatch in useChat's write(). When the stream ends, the gate
|
|
6
|
+
* opens and the view is synced into useChat's overlay.
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* the gate
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* All dependencies are resolved from the nearest ChatTransportProvider via
|
|
14
|
-
* useChatTransport(). Pass channelName to select a specific provider; omit to use
|
|
15
|
-
* the nearest. Pass skip: true to pause all subscriptions.
|
|
16
|
-
*
|
|
17
|
-
* Returns the unsubscribe function in the useEffect cleanup so handlers
|
|
18
|
-
* are removed on unmount or when dependencies change.
|
|
8
|
+
* The sync is a per-message merge, not a replace: when the overlay has
|
|
9
|
+
* resolved a client-side tool locally (via addToolResult) but the
|
|
10
|
+
* tree's echo hasn't landed yet, the overlay's resolution wins.
|
|
11
|
+
* Without that, the gate-open sync would race the AI SDK's post-stream
|
|
12
|
+
* sendAutomaticallyWhen check and could clobber the resolution before
|
|
13
|
+
* the continuation publishes.
|
|
19
14
|
*/
|
|
20
15
|
|
|
21
16
|
import type * as AI from 'ai';
|
|
@@ -26,47 +21,88 @@ import { useChatTransport } from './use-chat-transport.js';
|
|
|
26
21
|
/** Options for {@link useMessageSync}. */
|
|
27
22
|
export interface UseMessageSyncOptions {
|
|
28
23
|
/**
|
|
29
|
-
* The `setMessages` updater function from `useChat()`.
|
|
30
|
-
*
|
|
31
|
-
* transport's current authoritative message list.
|
|
24
|
+
* The `setMessages` updater function from `useChat()`. Called with an
|
|
25
|
+
* updater that returns the next overlay.
|
|
32
26
|
*/
|
|
33
27
|
setMessages: (updater: (prev: AI.UIMessage[]) => AI.UIMessage[]) => void;
|
|
34
28
|
/**
|
|
35
29
|
* Channel name of the {@link ChatTransportProvider} to observe.
|
|
36
|
-
* Omit to use the nearest provider
|
|
30
|
+
* Omit to use the nearest provider.
|
|
37
31
|
*/
|
|
38
32
|
channelName?: string;
|
|
39
|
-
/**
|
|
40
|
-
* When `true`, skip all subscriptions and do nothing.
|
|
41
|
-
* Use when the hook's dependencies are not yet resolved (e.g. auth pending).
|
|
42
|
-
*/
|
|
33
|
+
/** When `true`, skip all subscriptions. */
|
|
43
34
|
skip?: boolean;
|
|
44
35
|
}
|
|
45
36
|
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// Tool-resolution merge
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
//
|
|
41
|
+
// The Vercel codec normalises every tool part to `dynamic-tool`, but the
|
|
42
|
+
// AI SDK emits `tool-${name}` for statically-declared tools. Both shapes
|
|
43
|
+
// share `toolCallId` + `state`; the merge matches by toolCallId and keeps
|
|
44
|
+
// the tree's `type` on the result so downstream consumers narrowing on
|
|
45
|
+
// `dynamic-tool` keep working.
|
|
46
|
+
|
|
47
|
+
type ToolPart = AI.DynamicToolUIPart | AI.ToolUIPart;
|
|
48
|
+
|
|
49
|
+
const RESOLVED_TOOL_STATES = new Set(['output-available', 'output-error', 'approval-responded', 'output-denied']);
|
|
50
|
+
|
|
51
|
+
const isToolPart = (part: AI.UIMessage['parts'][number]): part is ToolPart =>
|
|
52
|
+
(part.type === 'dynamic-tool' || part.type.startsWith('tool-')) && 'toolCallId' in part && 'state' in part;
|
|
53
|
+
|
|
54
|
+
const mergeAssistant = (tree: AI.UIMessage, overlay: AI.UIMessage): AI.UIMessage => {
|
|
55
|
+
const overlayByCallId = new Map<string, ToolPart>();
|
|
56
|
+
for (const part of overlay.parts) {
|
|
57
|
+
if (isToolPart(part)) overlayByCallId.set(part.toolCallId, part);
|
|
58
|
+
}
|
|
59
|
+
if (overlayByCallId.size === 0) return tree;
|
|
60
|
+
|
|
61
|
+
const parts = tree.parts.map((part) => {
|
|
62
|
+
if (!isToolPart(part)) return part;
|
|
63
|
+
if (RESOLVED_TOOL_STATES.has(part.state)) return part;
|
|
64
|
+
const overlayPart = overlayByCallId.get(part.toolCallId);
|
|
65
|
+
if (!overlayPart || !RESOLVED_TOOL_STATES.has(overlayPart.state)) return part;
|
|
66
|
+
// CAST: tool-${name} and dynamic-tool share the discriminated payload schema.
|
|
67
|
+
return { ...overlayPart, type: part.type } as AI.UIMessage['parts'][number];
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const changed = parts.some((p, i) => p !== tree.parts[i]);
|
|
71
|
+
return changed ? { ...tree, parts } : tree;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const mergeMessages = (tree: AI.UIMessage[], overlay: AI.UIMessage[]): AI.UIMessage[] => {
|
|
75
|
+
if (overlay.length === 0) return tree;
|
|
76
|
+
const overlayById = new Map(overlay.map((m) => [m.id, m]));
|
|
77
|
+
return tree.map((treeMsg) => {
|
|
78
|
+
if (treeMsg.role !== 'assistant') return treeMsg;
|
|
79
|
+
const overlayMsg = overlayById.get(treeMsg.id);
|
|
80
|
+
return overlayMsg ? mergeAssistant(treeMsg, overlayMsg) : treeMsg;
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
// Hook
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
|
|
46
88
|
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* Resolves both the transport view and the streaming gate from the nearest
|
|
50
|
-
* `ChatTransportProvider`. Pass `channelName` to target a specific provider.
|
|
51
|
-
* Pass `skip: true` to pause all subscriptions.
|
|
89
|
+
* Subscribe to view updates and sync them into `useChat()`'s overlay.
|
|
52
90
|
* @param options - Hook options.
|
|
53
|
-
* @param options.setMessages - The `setMessages` function from `useChat()`.
|
|
54
|
-
* @param options.channelName - Channel name of the provider to observe; defaults to nearest.
|
|
91
|
+
* @param options.setMessages - The `setMessages` function from `useChat()`.
|
|
92
|
+
* @param options.channelName - Channel name of the provider to observe; defaults to the nearest.
|
|
55
93
|
* @param options.skip - When `true`, skip all subscriptions.
|
|
56
94
|
*/
|
|
57
95
|
export const useMessageSync = ({ setMessages, channelName, skip }: UseMessageSyncOptions): void => {
|
|
58
|
-
const {
|
|
96
|
+
const { session, chatTransport, chatTransportError } = useChatTransport({ channelName, skip });
|
|
59
97
|
|
|
60
|
-
// Only use resolved values when a provider was found and skip is false.
|
|
61
98
|
const resolved = !skip && !chatTransportError;
|
|
62
|
-
const view = resolved ?
|
|
99
|
+
const view = resolved ? session.view : undefined;
|
|
63
100
|
const resolvedChatTransport = resolved ? chatTransport : undefined;
|
|
64
101
|
|
|
65
102
|
const [gated, setGated] = useState(false);
|
|
66
103
|
|
|
67
|
-
// Subscribe to the ChatTransport's streaming state
|
|
68
|
-
//
|
|
69
|
-
// from a previous instance doesn't permanently suppress syncs.
|
|
104
|
+
// Subscribe to the ChatTransport's streaming state. Reset on transport
|
|
105
|
+
// change so a stale `true` doesn't permanently suppress syncs.
|
|
70
106
|
useEffect(() => {
|
|
71
107
|
if (!resolvedChatTransport) {
|
|
72
108
|
setGated(false);
|
|
@@ -76,15 +112,20 @@ export const useMessageSync = ({ setMessages, channelName, skip }: UseMessageSyn
|
|
|
76
112
|
return resolvedChatTransport.onStreamingChange(setGated);
|
|
77
113
|
}, [resolvedChatTransport]);
|
|
78
114
|
|
|
79
|
-
// Subscribe to view updates and sync
|
|
115
|
+
// Subscribe to view updates and sync, unless gated.
|
|
80
116
|
useEffect(() => {
|
|
81
117
|
if (!view || gated) return;
|
|
82
118
|
|
|
83
119
|
const sync = (): void => {
|
|
84
|
-
setMessages(() =>
|
|
120
|
+
setMessages((overlay) =>
|
|
121
|
+
mergeMessages(
|
|
122
|
+
view.getMessages().map((m) => m.message),
|
|
123
|
+
overlay,
|
|
124
|
+
),
|
|
125
|
+
);
|
|
85
126
|
};
|
|
86
127
|
|
|
87
|
-
// Sync immediately
|
|
128
|
+
// Sync immediately to cover gate-open and initial mount.
|
|
88
129
|
sync();
|
|
89
130
|
|
|
90
131
|
return view.on('update', sync);
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type * as AI from 'ai';
|
|
2
|
+
|
|
3
|
+
import type { RunEndReason, StreamResult } from '../core/transport/types.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Derive the outcome for a Vercel `streamText` response that was piped through
|
|
7
|
+
* `Run.pipe`: either a terminal {@link RunEndReason} the caller passes to
|
|
8
|
+
* `Run.end`, or the sentinel `'suspend'` telling the caller to call
|
|
9
|
+
* `Run.suspend` instead. Preserves transport-level outcomes (`'cancelled'`,
|
|
10
|
+
* `'error'`) from the pipe result; when the pipe completed naturally, awaits
|
|
11
|
+
* Vercel's `finishReason` and returns `'suspend'` for `'tool-calls'` (the LLM
|
|
12
|
+
* requested tools the SDK did not auto-execute, so the run should suspend
|
|
13
|
+
* rather than end), or `'complete'` otherwise.
|
|
14
|
+
*
|
|
15
|
+
* Tolerates `finishReason` rejection. Vercel AI SDK v6 rejects
|
|
16
|
+
* `streamText().finishReason` with the abort signal's reason when the stream
|
|
17
|
+
* is aborted before any step completes, and rejects with
|
|
18
|
+
* `NoOutputGeneratedError` when the model produced nothing at all. Without
|
|
19
|
+
* this guard the rejection would bubble out of the route handler's `after()`
|
|
20
|
+
* block, skip the developer's `Run.end(...)` call, and leave the run with no
|
|
21
|
+
* `ai-run-end` event on the channel — so observers' UIs stay stuck on
|
|
22
|
+
* `streaming` indefinitely.
|
|
23
|
+
*
|
|
24
|
+
* Saves callers from interpreting Vercel domain semantics inline at the end
|
|
25
|
+
* of every route handler.
|
|
26
|
+
* @param pipeResult - The result returned by `Run.pipe`.
|
|
27
|
+
* @param finishReason - The `finishReason` promise from a `streamText` result.
|
|
28
|
+
* @returns `'suspend'` when the run should suspend awaiting tool input, or the
|
|
29
|
+
* {@link RunEndReason} to pass to `Run.end` otherwise.
|
|
30
|
+
*/
|
|
31
|
+
export const vercelRunOutcome = async (
|
|
32
|
+
pipeResult: StreamResult,
|
|
33
|
+
finishReason: PromiseLike<AI.FinishReason>,
|
|
34
|
+
): Promise<RunEndReason | 'suspend'> => {
|
|
35
|
+
if (pipeResult.reason !== 'complete') {
|
|
36
|
+
// Vercel's `result.finishReason` getter creates the underlying Promise
|
|
37
|
+
// eagerly, before the caller hands it to us. When `streamText` is
|
|
38
|
+
// aborted before any step completes, Vercel rejects that Promise with
|
|
39
|
+
// the abort signal's reason — typically a DOMException whose
|
|
40
|
+
// `.message` is a read-only getter. Returning early without ever
|
|
41
|
+
// attaching a handler lets Node report it as an unhandled rejection;
|
|
42
|
+
// Next.js' dev bundler then tries to mutate `.message` for logging
|
|
43
|
+
// and crashes with a confusing TypeError. Attach a silent handler so
|
|
44
|
+
// the rejection is observed and discarded — the transport-level
|
|
45
|
+
// `pipeResult.reason` is already what we return.
|
|
46
|
+
Promise.resolve(finishReason).catch(() => {
|
|
47
|
+
/* intentionally discarded; reason already known from pipeResult */
|
|
48
|
+
});
|
|
49
|
+
return pipeResult.reason;
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
const finish = await finishReason;
|
|
53
|
+
return finish === 'tool-calls' ? 'suspend' : 'complete';
|
|
54
|
+
} catch (error) {
|
|
55
|
+
// Abort-shaped rejections are surfaced from streamText when the run was
|
|
56
|
+
// cancelled before any step finished — treat the run as cancelled so the
|
|
57
|
+
// observable lifecycle matches the cancel that triggered it. Everything
|
|
58
|
+
// else is a real error (e.g. NoOutputGeneratedError, network blow-ups);
|
|
59
|
+
// surface it as such so the developer sees the failure rather than a
|
|
60
|
+
// silent cancel.
|
|
61
|
+
return _isAbortLikeError(error) ? 'cancelled' : 'error';
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Heuristic for "this error came from an AbortSignal aborting".
|
|
67
|
+
* Covers `DOMException` aborts (browser / Node 20+ `streamText`),
|
|
68
|
+
* plain `Error` objects whose `name` is `'AbortError'`, and anything
|
|
69
|
+
* else carrying that conventional name. Avoids importing
|
|
70
|
+
* `@ai-sdk/provider-utils` just for `isAbortError`.
|
|
71
|
+
* @param error - The error to test.
|
|
72
|
+
* @returns `true` if the error looks like an abort.
|
|
73
|
+
*/
|
|
74
|
+
const _isAbortLikeError = (error: unknown): boolean => {
|
|
75
|
+
if (typeof error !== 'object' || error === null) return false;
|
|
76
|
+
const name = (error as { name?: unknown }).name;
|
|
77
|
+
return name === 'AbortError';
|
|
78
|
+
};
|