ai 7.0.136 → 7.0.138
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/CHANGELOG.md +29 -0
- package/dist/index.d.ts +31 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +116 -77
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +73 -4
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +44 -17
- package/dist/internal/index.js.map +1 -1
- package/docs/03-ai-sdk-core/05-generating-text.mdx +70 -2
- package/docs/03-ai-sdk-core/15-tools-and-tool-calling.mdx +14 -1
- package/docs/04-ai-sdk-ui/03-chatbot-resume-streams.mdx +23 -1
- package/docs/04-ai-sdk-ui/03-chatbot-tool-usage.mdx +6 -0
- package/docs/04-ai-sdk-ui/21-error-handling.mdx +34 -0
- package/docs/07-reference/01-ai-sdk-core/01-generate-text.mdx +4 -2
- package/docs/07-reference/01-ai-sdk-core/02-stream-text.mdx +11 -1
- package/docs/07-reference/02-ai-sdk-ui/01-use-chat.mdx +8 -1
- package/docs/07-reference/04-ai-sdk-workflow/02-workflow-chat-transport.mdx +2 -2
- package/package.json +12 -12
- package/src/decide/validate-decision.ts +2 -1
- package/src/generate-text/content-part.ts +7 -1
- package/src/generate-text/is-output-chunk.ts +59 -0
- package/src/generate-text/stream-text-result.ts +2 -0
- package/src/generate-text/stream-text.ts +7 -57
- package/src/global.ts +23 -4
- package/src/middleware/simulate-streaming-middleware.ts +8 -2
- package/src/prompt/convert-to-language-model-prompt.ts +0 -42
- package/src/types/citation.ts +31 -0
- package/src/types/index.ts +1 -0
- package/src/ui/chat.ts +63 -13
- package/src/ui/default-chat-transport.ts +14 -1
- package/src/ui/process-ui-message-stream.ts +23 -11
- package/src/ui/ui-messages.ts +4 -0
- package/src/ui/validate-ui-messages.ts +2 -0
- package/src/ui-message-stream/read-ui-message-stream.ts +1 -0
- package/src/ui-message-stream/to-ui-message-chunk.ts +1 -0
- package/src/ui-message-stream/ui-message-chunks.ts +3 -0
|
@@ -703,8 +703,12 @@ const result = streamText({
|
|
|
703
703
|
Some providers such as [Perplexity](/providers/ai-sdk-providers/perplexity#sources) and
|
|
704
704
|
[Google](/providers/ai-sdk-providers/google#sources) include sources in the response.
|
|
705
705
|
|
|
706
|
-
|
|
707
|
-
|
|
706
|
+
Sources can reference URLs or documents. You can access them using the `sources`
|
|
707
|
+
property of the result. For OpenAI Responses and Anthropic web search, sources
|
|
708
|
+
contain the retrieved references, which can include more references than the
|
|
709
|
+
model cites in its answer. OpenAI file-search results are also exposed as document
|
|
710
|
+
sources when the API returns those results. If retrieval data is unavailable,
|
|
711
|
+
citation-derived references remain available as sources for compatibility.
|
|
708
712
|
|
|
709
713
|
Each `url` source contains the following properties:
|
|
710
714
|
|
|
@@ -759,6 +763,70 @@ for await (const part of result.stream) {
|
|
|
759
763
|
|
|
760
764
|
The sources are also available in the `result.sources` promise.
|
|
761
765
|
|
|
766
|
+
### Citations
|
|
767
|
+
|
|
768
|
+
OpenAI Responses and Anthropic attach an optional `citations` array to each
|
|
769
|
+
`text` content part. A citation is a reference supporting that text, independently
|
|
770
|
+
of the complete retrieved source set. Use citations to render inline references;
|
|
771
|
+
use `result.sources` to list retrieved sources.
|
|
772
|
+
|
|
773
|
+
Each `Citation` (exported from `ai`) contains:
|
|
774
|
+
|
|
775
|
+
- `source`: A URL or document source, including its title when available. Its ID
|
|
776
|
+
identifies the cited resource and does not necessarily match an ID in
|
|
777
|
+
`result.sources`.
|
|
778
|
+
- `startIndex` and `endIndex`: Optional provider-supplied offsets within the
|
|
779
|
+
containing generated text; the end is exclusive. Providers can use different
|
|
780
|
+
character indexing conventions. The SDK preserves their offsets.
|
|
781
|
+
- `citedText`: An optional supporting passage from the source. This is source
|
|
782
|
+
text, rather than an offset in the generated answer.
|
|
783
|
+
|
|
784
|
+
For inline rendering, use the offsets within the containing text part and follow
|
|
785
|
+
the provider's character indexing convention. JavaScript's `slice()` uses UTF-16
|
|
786
|
+
code units, so provider offsets may need conversion when the text contains emoji
|
|
787
|
+
or other characters outside the basic multilingual plane. When offsets are
|
|
788
|
+
absent or cannot be interpreted reliably, render the references alongside the
|
|
789
|
+
containing text block instead of guessing a range.
|
|
790
|
+
|
|
791
|
+
`citedText` and provider-specific document locations identify the supporting
|
|
792
|
+
evidence in the source; they do not locate the claim in the generated answer.
|
|
793
|
+
Preserve query parameters and fragments in source URLs, since they can identify a
|
|
794
|
+
particular version, page, or passage. Multiple citations can reference the same
|
|
795
|
+
source at different locations in the generated text.
|
|
796
|
+
|
|
797
|
+
Anthropic supplies supporting passages and document locations instead of output
|
|
798
|
+
text offsets. Provider-specific location details remain in provider metadata.
|
|
799
|
+
OpenAI file-search citation `index` values are retained in provider metadata;
|
|
800
|
+
they are not interpreted as output text ranges.
|
|
801
|
+
|
|
802
|
+
```ts
|
|
803
|
+
for (const part of result.content) {
|
|
804
|
+
if (part.type === 'text') {
|
|
805
|
+
for (const citation of part.citations ?? []) {
|
|
806
|
+
console.log(citation.source, citation.startIndex, citation.endIndex);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+
For `streamText`, citations are available on `text-end` events, associated with
|
|
813
|
+
the text block's `id`, and on text parts in the completed `result.content`.
|
|
814
|
+
They also survive `toUIMessageStream` as `TextUIPart.citations`, including when
|
|
815
|
+
source events are disabled. Citation fallback source events can arrive at the
|
|
816
|
+
end of the provider stream, after the SDK knows whether retrieval data exists.
|
|
817
|
+
|
|
818
|
+
```ts
|
|
819
|
+
for await (const part of result.stream) {
|
|
820
|
+
if (part.type === 'text-end') {
|
|
821
|
+
console.log(part.id, part.citations);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
```
|
|
825
|
+
|
|
826
|
+
Raw OpenAI annotations and Anthropic citations remain in text provider metadata.
|
|
827
|
+
Other providers may omit `citations`; an omitted field does not imply that their
|
|
828
|
+
sources are a complete retrieval set.
|
|
829
|
+
|
|
762
830
|
## Examples
|
|
763
831
|
|
|
764
832
|
You can see `generateText` and `streamText` in action using various frameworks in the following examples:
|
|
@@ -307,7 +307,7 @@ for (const part of result.content) {
|
|
|
307
307
|
type: 'tool-approval-response',
|
|
308
308
|
approvalId: part.approvalId,
|
|
309
309
|
approved: true, // or false to deny
|
|
310
|
-
reason: 'User confirmed the command', // Optional
|
|
310
|
+
reason: 'User confirmed the command', // Optional, see below
|
|
311
311
|
};
|
|
312
312
|
approvals.push(response);
|
|
313
313
|
}
|
|
@@ -319,6 +319,19 @@ messages.push({ role: 'tool', content: approvals });
|
|
|
319
319
|
|
|
320
320
|
Then call `generateText` or `streamText` again with the updated messages. If approved, the tool executes. If denied, the model receives the denial and can respond accordingly.
|
|
321
321
|
|
|
322
|
+
The optional `reason` is handled differently depending on the decision:
|
|
323
|
+
|
|
324
|
+
- **Denied**: the reason is included in the `execution-denied` tool result, so the model can see why the tool call was not executed.
|
|
325
|
+
- **Approved** (tools executed by the AI SDK): the reason remains in the approval response in your messages (e.g. for rendering in the UI or for audit logs). The SDK does not include it in the model prompt, modify the tool's input, or expose it through a dedicated execution option. The model only sees the tool result. In manual `generateText` and `streamText` continuations, the approval response is available to the tool's `execute` function through `options.messages`.
|
|
326
|
+
|
|
327
|
+
<Note>
|
|
328
|
+
The approval `reason` is not a channel for passing data to an approved tool.
|
|
329
|
+
If you need input from the user (for example, an answer to a question or a
|
|
330
|
+
corrected value), use a tool without an `execute` function and provide its
|
|
331
|
+
result from the client instead. See [Chatbot Tool
|
|
332
|
+
Usage](/docs/ai-sdk-ui/chatbot-tool-usage).
|
|
333
|
+
</Note>
|
|
334
|
+
|
|
322
335
|
When the tool should execute without any approval metadata in the output, use
|
|
323
336
|
`'not-applicable'`, return `undefined` from an approval function, or omit `toolApproval`. Use `'approved'`, `'denied'`, or
|
|
324
337
|
their object forms only when you want the result to include an automatic
|
|
@@ -90,6 +90,28 @@ export function Chat({
|
|
|
90
90
|
|
|
91
91
|
When you enable `resume`, the `useChat` hook makes a `GET` request to `/api/chat/[id]/stream` on mount to check for and resume any active streams.
|
|
92
92
|
|
|
93
|
+
The hook also reconnects when the page becomes visible again after being in the background (for example, after switching browser tabs or returning to a mobile app). This only happens when the stream was interrupted by a network disconnect. Healthy streams and other errors are left untouched.
|
|
94
|
+
|
|
95
|
+
`DefaultChatTransport` treats resumed streams as full replays by default, as
|
|
96
|
+
returned by the `resumable-stream` setup below. On the first `start` chunk, the
|
|
97
|
+
client resets the interrupted response and rebuilds it from the replay. This
|
|
98
|
+
prevents duplicate text, reasoning, and step parts after a network disconnect.
|
|
99
|
+
|
|
100
|
+
If your resume endpoint sends only the remaining chunks, set
|
|
101
|
+
`resumeStreamIsReplay: false`. This preserves the interrupted response even if
|
|
102
|
+
the continuation includes a `start` chunk with the same message ID:
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
const transport = new DefaultChatTransport({
|
|
106
|
+
resumeStreamIsReplay: false,
|
|
107
|
+
});
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
When a continuation omits the `start` chunk, the client preserves its interrupted
|
|
111
|
+
streaming state with either setting. Continuing active text or reasoning parts
|
|
112
|
+
requires the same chat instance; a page reload does not retain that streaming
|
|
113
|
+
state, so use a full replay for reloads.
|
|
114
|
+
|
|
93
115
|
The default transport URL-encodes the chat ID as a single path segment. IDs equal
|
|
94
116
|
to `.` or `..` are rejected with an `InvalidArgumentError` because URL parsers
|
|
95
117
|
interpret them as path traversal segments.
|
|
@@ -216,7 +238,7 @@ The diagram above shows the complete lifecycle of a resumable stream:
|
|
|
216
238
|
|
|
217
239
|
1. **Stream creation**: When you send a new message, the POST handler uses `streamText` to generate the response. The `consumeSseStream` callback creates a resumable stream with a unique ID and stores it in Redis through the `resumable-stream` package
|
|
218
240
|
2. **Stream tracking**: Your persistence layer saves the `activeStreamId` in the chat data
|
|
219
|
-
3. **Client reconnection**: When the client reconnects (page reload), the `resume` option triggers a GET request to `/api/chat/[id]/stream`
|
|
241
|
+
3. **Client reconnection**: When the client reconnects (page reload, or returning to a backgrounded page after a network disconnect), the `resume` option triggers a GET request to `/api/chat/[id]/stream`
|
|
220
242
|
4. **Stream recovery**: The GET handler checks for an `activeStreamId` and uses `resumeExistingStream` to reconnect. If no active stream exists, it returns a 204 (No Content) response
|
|
221
243
|
5. **Completion cleanup**: When the stream finishes, the `onEnd` callback clears the `activeStreamId` by setting it to `null`
|
|
222
244
|
|
|
@@ -555,6 +555,12 @@ as `part.approval.reason`.
|
|
|
555
555
|
For manual approval requests, the reason for requiring approval is available as
|
|
556
556
|
`part.approval.requestReason`. It remains separate from an optional response
|
|
557
557
|
reason supplied to `addToolApprovalResponse`.
|
|
558
|
+
When a response reason is supplied with a denial, it is included in the
|
|
559
|
+
`execution-denied` tool result that the model sees. When it is supplied with an
|
|
560
|
+
approval for a tool executed by the AI SDK, it is recorded on the message part,
|
|
561
|
+
but the SDK does not include it in the model prompt, modify the tool's input, or
|
|
562
|
+
expose it through a dedicated execution option. To collect input from the user
|
|
563
|
+
for a tool, use a client-side tool and `addToolOutput` instead.
|
|
558
564
|
|
|
559
565
|
Approval request chunks can also include an `approvalDescriptor` with opaque
|
|
560
566
|
application-specific metadata. UI message processing exposes it as
|
|
@@ -52,6 +52,40 @@ globalThis.AI_SDK_LOG_WARNINGS = ({ warnings, provider, model }) => {
|
|
|
52
52
|
};
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
+
If your application loads multiple AI SDK versions in the same runtime, they all
|
|
56
|
+
share `globalThis.AI_SDK_LOG_WARNINGS`. Each version calls the logger with its own
|
|
57
|
+
arguments and warning types. The merged global type declarations prevent
|
|
58
|
+
TypeScript declaration conflicts, but do not convert these arguments at runtime.
|
|
59
|
+
A handler typed for only one version may therefore fail when another version
|
|
60
|
+
calls it.
|
|
61
|
+
|
|
62
|
+
Import `LogWarningsFunction` from every installed version that can call the logger
|
|
63
|
+
and accept a union of their parameter types. For example, with AI SDK 7 installed
|
|
64
|
+
as `ai` and AI SDK 6 installed under the package alias `ai-v6` (`npm:ai@6`):
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
import type { LogWarningsFunction as LogWarningsFunctionV7 } from 'ai';
|
|
68
|
+
import type { LogWarningsFunction as LogWarningsFunctionV6 } from 'ai-v6';
|
|
69
|
+
|
|
70
|
+
type LoggerOptions =
|
|
71
|
+
| Parameters<LogWarningsFunctionV7>[0]
|
|
72
|
+
| Parameters<LogWarningsFunctionV6>[0];
|
|
73
|
+
|
|
74
|
+
const logger = ({ warnings, provider, model }: LoggerOptions) => {
|
|
75
|
+
for (const warning of warnings) {
|
|
76
|
+
console.warn('AI SDK Warning:', warning, { provider, model });
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
globalThis.AI_SDK_LOG_WARNINGS = logger;
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Check the argument shape and warning type before accessing version-specific
|
|
84
|
+
fields. If AI SDK 5 is also installed, include its
|
|
85
|
+
`Experimental_LogWarningsFunction` parameter type and use `Array.isArray` to
|
|
86
|
+
handle its warning array before accessing the options object used by AI SDK 6
|
|
87
|
+
and 7.
|
|
88
|
+
|
|
55
89
|
### Deprecation Warnings
|
|
56
90
|
|
|
57
91
|
The default logger emits each deprecation code once per loaded SDK instance.
|
|
@@ -2157,7 +2157,8 @@ To see `generateText` in action, check out [these examples](#examples).
|
|
|
2157
2157
|
{
|
|
2158
2158
|
name: 'content',
|
|
2159
2159
|
type: 'Array<ContentPart<TOOLS>>',
|
|
2160
|
-
description:
|
|
2160
|
+
description:
|
|
2161
|
+
'The content that was generated in all steps. Text parts can include an optional citations array of references supporting that text; see Sources and Citations in the generating text guide.',
|
|
2161
2162
|
},
|
|
2162
2163
|
{
|
|
2163
2164
|
name: 'providerMetadata',
|
|
@@ -2457,7 +2458,8 @@ To see `generateText` in action, check out [these examples](#examples).
|
|
|
2457
2458
|
{
|
|
2458
2459
|
name: 'content',
|
|
2459
2460
|
type: 'Array<ContentPart<TOOLS>>',
|
|
2460
|
-
description:
|
|
2461
|
+
description:
|
|
2462
|
+
'The content that was generated in all steps. Text parts can include an optional citations array of references supporting that text; see Sources and Citations in the generating text guide.',
|
|
2461
2463
|
},
|
|
2462
2464
|
{
|
|
2463
2465
|
name: 'text',
|
|
@@ -2650,7 +2650,7 @@ To see `streamText` in action, check out [these examples](#examples).
|
|
|
2650
2650
|
name: 'content',
|
|
2651
2651
|
type: 'Promise<Array<ContentPart<TOOLS>>>',
|
|
2652
2652
|
description:
|
|
2653
|
-
'The content that was generated in all steps. Automatically consumes the stream.',
|
|
2653
|
+
'The content that was generated in all steps. Text parts can include an optional citations array of references supporting that text. Automatically consumes the stream.',
|
|
2654
2654
|
},
|
|
2655
2655
|
{
|
|
2656
2656
|
name: 'finishReason',
|
|
@@ -3352,6 +3352,16 @@ To see `streamText` in action, check out [these examples](#examples).
|
|
|
3352
3352
|
description:
|
|
3353
3353
|
'A stream with all events, including text deltas, tool calls, tool results, and errors. Well-formed mid-stream provider failures are emitted in error parts as StreamProviderError instances. You can use it as either an AsyncIterable or a ReadableStream. Only errors that stop the stream, such as network errors, are thrown.',
|
|
3354
3354
|
properties: [
|
|
3355
|
+
{
|
|
3356
|
+
type: 'TextStreamTextEndPart',
|
|
3357
|
+
description: 'End of a text block, including optional citations supporting that block.',
|
|
3358
|
+
parameters: [
|
|
3359
|
+
{ name: 'type', type: "'text-end'", description: 'The event type.' },
|
|
3360
|
+
{ name: 'id', type: 'string', description: 'The text block ID.' },
|
|
3361
|
+
{ name: 'citations', type: 'Array<Citation>', isOptional: true, description: 'References supporting this text block. Each citation contains a URL or document source, optional startIndex/endIndex offsets in this block, and an optional citedText passage from the source.' },
|
|
3362
|
+
{ name: 'providerMetadata', type: 'ProviderMetadata', isOptional: true, description: 'Additional provider metadata, including raw annotations.' },
|
|
3363
|
+
],
|
|
3364
|
+
},
|
|
3355
3365
|
{
|
|
3356
3366
|
type: 'TextStreamPart',
|
|
3357
3367
|
description: 'Text content part from ContentPart<TOOLS>',
|
|
@@ -69,6 +69,13 @@ Allows you to easily create a conversational user interface for your chatbot app
|
|
|
69
69
|
isOptional: true,
|
|
70
70
|
description: 'The API endpoint for chat requests.',
|
|
71
71
|
},
|
|
72
|
+
{
|
|
73
|
+
name: 'resumeStreamIsReplay',
|
|
74
|
+
type: 'boolean',
|
|
75
|
+
isOptional: true,
|
|
76
|
+
description:
|
|
77
|
+
'Defaults to true. Resets the interrupted response on the first resumed start chunk and rebuilds it from the replay. Set to false for endpoints that only send the remaining chunks of the same response.',
|
|
78
|
+
},
|
|
72
79
|
{
|
|
73
80
|
name: 'credentials',
|
|
74
81
|
type: 'RequestCredentials',
|
|
@@ -345,7 +352,7 @@ Allows you to easily create a conversational user interface for your chatbot app
|
|
|
345
352
|
type: 'boolean',
|
|
346
353
|
isOptional: true,
|
|
347
354
|
description:
|
|
348
|
-
'Whether to resume an ongoing chat generation stream. Defaults to false.',
|
|
355
|
+
'Whether to automatically resume an ongoing chat generation stream on mount and after the page becomes visible again following a network disconnect. Defaults to false.',
|
|
349
356
|
},
|
|
350
357
|
]}
|
|
351
358
|
/>
|
|
@@ -59,7 +59,7 @@ export default function Chat() {
|
|
|
59
59
|
type: 'number',
|
|
60
60
|
isOptional: true,
|
|
61
61
|
description:
|
|
62
|
-
'Maximum number of consecutive errors allowed during reconnection attempts before giving up. Default: 3.',
|
|
62
|
+
'Maximum number of consecutive errors allowed during reconnection attempts before giving up. Streams that end without receiving any UI message chunks count as errors. Default: 3.',
|
|
63
63
|
},
|
|
64
64
|
{
|
|
65
65
|
name: 'initialStartIndex',
|
|
@@ -199,7 +199,7 @@ The transport follows this flow:
|
|
|
199
199
|
2. **Stream** the SSE response, counting chunks as they arrive.
|
|
200
200
|
3. **Detect interruption**: If the stream closes without a `finish` event (e.g., function timeout, network error), the transport knows the response is incomplete.
|
|
201
201
|
4. **Reconnect** via GET to `{api}/{runId}/stream?startIndex={chunkIndex}` to resume from the last received chunk.
|
|
202
|
-
5. **Retry**: If the reconnection stream also interrupts,
|
|
202
|
+
5. **Retry**: If the reconnection stream also interrupts, resume from the updated chunk index. Streams that end without receiving any UI message chunks count toward `maxConsecutiveErrors`, as do stream parsing errors. A successfully parsed stream that receives chunks resets the consecutive error count.
|
|
203
203
|
6. **Complete**: Once a `finish` event is received, call `onChatEnd` and close the stream.
|
|
204
204
|
|
|
205
205
|
### Negative Start Index
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.138",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AI SDK by Vercel - build apps like ChatGPT, Claude, Gemini, and more with a single interface for any model using the Vercel AI Gateway or go direct to OpenAI, Anthropic, Google, or any other model provider.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -42,20 +42,20 @@
|
|
|
42
42
|
}
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@ai-sdk/gateway": "4.0.
|
|
46
|
-
"@ai-sdk/provider": "4.0.
|
|
47
|
-
"@ai-sdk/provider-utils": "5.0.
|
|
45
|
+
"@ai-sdk/gateway": "4.0.111",
|
|
46
|
+
"@ai-sdk/provider": "4.0.27",
|
|
47
|
+
"@ai-sdk/provider-utils": "5.0.59"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@ai-sdk/amazon-bedrock": "5.0.
|
|
51
|
-
"@ai-sdk/deepseek": "3.0.
|
|
52
|
-
"@ai-sdk/google": "4.0.
|
|
53
|
-
"@ai-sdk/groq": "4.0.
|
|
54
|
-
"@ai-sdk/huggingface": "2.0.
|
|
55
|
-
"@ai-sdk/moonshotai": "3.0.
|
|
56
|
-
"@ai-sdk/openai": "4.0.
|
|
50
|
+
"@ai-sdk/amazon-bedrock": "5.0.114",
|
|
51
|
+
"@ai-sdk/deepseek": "3.0.64",
|
|
52
|
+
"@ai-sdk/google": "4.0.94",
|
|
53
|
+
"@ai-sdk/groq": "4.0.60",
|
|
54
|
+
"@ai-sdk/huggingface": "2.0.68",
|
|
55
|
+
"@ai-sdk/moonshotai": "3.0.68",
|
|
56
|
+
"@ai-sdk/openai": "4.0.92",
|
|
57
57
|
"@ai-sdk/test-server": "2.0.4",
|
|
58
|
-
"@ai-sdk/xai": "5.0.
|
|
58
|
+
"@ai-sdk/xai": "5.0.21",
|
|
59
59
|
"@edge-runtime/vm": "^5.0.0",
|
|
60
60
|
"@smithy/eventstream-codec": "^4.3.3",
|
|
61
61
|
"@smithy/util-utf8": "^4.3.3",
|
|
@@ -253,7 +253,8 @@ export function validateDecisionAnswers({
|
|
|
253
253
|
const selected = answer.probabilities[answer.choice];
|
|
254
254
|
if (
|
|
255
255
|
Object.values(answer.probabilities).some(
|
|
256
|
-
probability =>
|
|
256
|
+
probability =>
|
|
257
|
+
probability > selected + tolerance + 2 * probabilityError,
|
|
257
258
|
)
|
|
258
259
|
) {
|
|
259
260
|
invalidAnswer(
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ToolSet } from '@ai-sdk/provider-utils';
|
|
2
2
|
import type { ProviderMetadata } from '../types';
|
|
3
|
+
import type { Citation } from '../types/citation';
|
|
3
4
|
import type { Source } from '../types/language-model';
|
|
4
5
|
import type { GeneratedFile } from './generated-file';
|
|
5
6
|
import type { ReasoningFileOutput, ReasoningOutput } from './reasoning-output';
|
|
@@ -10,7 +11,12 @@ import type { TypedToolError } from './tool-error';
|
|
|
10
11
|
import type { TypedToolResult } from './tool-result';
|
|
11
12
|
|
|
12
13
|
export type ContentPart<TOOLS extends ToolSet> =
|
|
13
|
-
| {
|
|
14
|
+
| {
|
|
15
|
+
type: 'text';
|
|
16
|
+
text: string;
|
|
17
|
+
citations?: Array<Citation>;
|
|
18
|
+
providerMetadata?: ProviderMetadata;
|
|
19
|
+
}
|
|
14
20
|
| {
|
|
15
21
|
type: 'custom';
|
|
16
22
|
kind: `${string}.${string}`;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { ExecuteToolsStreamPart } from './execute-tools-from-stream';
|
|
2
|
+
import type { StreamRetryAttemptBoundaryPart } from './stream-retry-attempt-boundary';
|
|
3
|
+
|
|
4
|
+
// Chunk types that contain semantic model output. This classification is used
|
|
5
|
+
// for first-content and inter-content timeouts as well as to distinguish empty
|
|
6
|
+
// incomplete streams from incomplete streams with partial results. It is
|
|
7
|
+
// exhaustive so that new chunk types must be classified explicitly.
|
|
8
|
+
const isOutputChunkType = {
|
|
9
|
+
file: true,
|
|
10
|
+
custom: false,
|
|
11
|
+
source: false,
|
|
12
|
+
'text-start': false,
|
|
13
|
+
'text-end': false,
|
|
14
|
+
'text-delta': true,
|
|
15
|
+
'reasoning-start': false,
|
|
16
|
+
'reasoning-end': false,
|
|
17
|
+
'reasoning-delta': true,
|
|
18
|
+
'reasoning-file': true,
|
|
19
|
+
'tool-input-start': false,
|
|
20
|
+
'tool-input-end': false,
|
|
21
|
+
'tool-input-delta': true,
|
|
22
|
+
'tool-approval-request': false,
|
|
23
|
+
'tool-approval-response': false,
|
|
24
|
+
'tool-call': true,
|
|
25
|
+
'tool-result': false,
|
|
26
|
+
'tool-error': false,
|
|
27
|
+
'tool-output-denied': false,
|
|
28
|
+
'tool-execution-end': false,
|
|
29
|
+
'model-call-start': false,
|
|
30
|
+
'model-call-response-metadata': false,
|
|
31
|
+
'model-call-end': false,
|
|
32
|
+
error: false,
|
|
33
|
+
raw: false,
|
|
34
|
+
} as const satisfies Record<
|
|
35
|
+
Exclude<ExecuteToolsStreamPart, StreamRetryAttemptBoundaryPart>['type'],
|
|
36
|
+
boolean
|
|
37
|
+
>;
|
|
38
|
+
|
|
39
|
+
export function isOutputChunk(
|
|
40
|
+
chunk: Exclude<ExecuteToolsStreamPart, StreamRetryAttemptBoundaryPart>,
|
|
41
|
+
): boolean {
|
|
42
|
+
if (!isOutputChunkType[chunk.type]) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
switch (chunk.type) {
|
|
47
|
+
case 'text-delta':
|
|
48
|
+
case 'reasoning-delta':
|
|
49
|
+
return chunk.text.length > 0;
|
|
50
|
+
case 'tool-input-delta':
|
|
51
|
+
return chunk.delta.length > 0;
|
|
52
|
+
case 'file':
|
|
53
|
+
case 'reasoning-file':
|
|
54
|
+
case 'tool-call':
|
|
55
|
+
return true;
|
|
56
|
+
default:
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Citation } from '../types/citation';
|
|
1
2
|
import type { JSONObject } from '@ai-sdk/provider';
|
|
2
3
|
import type { Context, IdGenerator, ToolSet } from '@ai-sdk/provider-utils';
|
|
3
4
|
import type { ServerResponse } from 'node:http';
|
|
@@ -458,6 +459,7 @@ export type TextStreamTextStartPart = {
|
|
|
458
459
|
|
|
459
460
|
export type TextStreamTextEndPart = {
|
|
460
461
|
type: 'text-end';
|
|
462
|
+
citations?: Array<Citation>;
|
|
461
463
|
id: string;
|
|
462
464
|
providerMetadata?: ProviderMetadata;
|
|
463
465
|
};
|
|
@@ -54,6 +54,7 @@ import type {
|
|
|
54
54
|
LanguageModel,
|
|
55
55
|
ToolChoice,
|
|
56
56
|
} from '../types/language-model';
|
|
57
|
+
import type { Citation } from '../types/citation';
|
|
57
58
|
import type { ProviderMetadata } from '../types/provider-metadata';
|
|
58
59
|
import {
|
|
59
60
|
addLanguageModelUsage,
|
|
@@ -89,6 +90,7 @@ import {
|
|
|
89
90
|
executeToolsFromStream,
|
|
90
91
|
type ExecuteToolsStreamPart,
|
|
91
92
|
} from './execute-tools-from-stream';
|
|
93
|
+
import { isOutputChunk } from './is-output-chunk';
|
|
92
94
|
import { createToolSearchState } from '../tool-search/prepare-tool-search';
|
|
93
95
|
import { executeToolCall } from './execute-tool-call';
|
|
94
96
|
import {
|
|
@@ -175,63 +177,6 @@ const originalGenerateCallId = createIdGenerator({
|
|
|
175
177
|
size: 24,
|
|
176
178
|
});
|
|
177
179
|
|
|
178
|
-
// Chunk types that contain semantic model output. This classification is used
|
|
179
|
-
// for first-content and inter-content timeouts as well as to distinguish empty
|
|
180
|
-
// incomplete streams from incomplete streams with partial results. It is
|
|
181
|
-
// exhaustive so that new chunk types must be classified explicitly.
|
|
182
|
-
const isOutputChunkType = {
|
|
183
|
-
file: true,
|
|
184
|
-
custom: false,
|
|
185
|
-
source: false,
|
|
186
|
-
'text-start': false,
|
|
187
|
-
'text-end': false,
|
|
188
|
-
'text-delta': true,
|
|
189
|
-
'reasoning-start': false,
|
|
190
|
-
'reasoning-end': false,
|
|
191
|
-
'reasoning-delta': true,
|
|
192
|
-
'reasoning-file': true,
|
|
193
|
-
'tool-input-start': false,
|
|
194
|
-
'tool-input-end': false,
|
|
195
|
-
'tool-input-delta': true,
|
|
196
|
-
'tool-approval-request': false,
|
|
197
|
-
'tool-approval-response': false,
|
|
198
|
-
'tool-call': true,
|
|
199
|
-
'tool-result': false,
|
|
200
|
-
'tool-error': false,
|
|
201
|
-
'tool-output-denied': false,
|
|
202
|
-
'tool-execution-end': false,
|
|
203
|
-
'model-call-start': false,
|
|
204
|
-
'model-call-response-metadata': false,
|
|
205
|
-
'model-call-end': false,
|
|
206
|
-
error: false,
|
|
207
|
-
raw: false,
|
|
208
|
-
} as const satisfies Record<
|
|
209
|
-
Exclude<ExecuteToolsStreamPart, StreamRetryAttemptBoundaryPart>['type'],
|
|
210
|
-
boolean
|
|
211
|
-
>;
|
|
212
|
-
|
|
213
|
-
function isOutputChunk(
|
|
214
|
-
chunk: Exclude<ExecuteToolsStreamPart, StreamRetryAttemptBoundaryPart>,
|
|
215
|
-
): boolean {
|
|
216
|
-
if (!isOutputChunkType[chunk.type]) {
|
|
217
|
-
return false;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
switch (chunk.type) {
|
|
221
|
-
case 'text-delta':
|
|
222
|
-
case 'reasoning-delta':
|
|
223
|
-
return chunk.text.length > 0;
|
|
224
|
-
case 'tool-input-delta':
|
|
225
|
-
return chunk.delta.length > 0;
|
|
226
|
-
case 'file':
|
|
227
|
-
case 'reasoning-file':
|
|
228
|
-
case 'tool-call':
|
|
229
|
-
return true;
|
|
230
|
-
default:
|
|
231
|
-
return false;
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
180
|
export type StreamTextInclude = {
|
|
236
181
|
/**
|
|
237
182
|
* Whether to retain the request body in step results.
|
|
@@ -1463,6 +1408,7 @@ class DefaultStreamTextResult<
|
|
|
1463
1408
|
{
|
|
1464
1409
|
type: 'text';
|
|
1465
1410
|
text: string;
|
|
1411
|
+
citations?: Array<Citation>;
|
|
1466
1412
|
providerMetadata: ProviderMetadata | undefined;
|
|
1467
1413
|
}
|
|
1468
1414
|
> = createIdMap();
|
|
@@ -1583,6 +1529,10 @@ class DefaultStreamTextResult<
|
|
|
1583
1529
|
activeText.providerMetadata =
|
|
1584
1530
|
part.providerMetadata ?? activeText.providerMetadata;
|
|
1585
1531
|
|
|
1532
|
+
if (part.citations != null) {
|
|
1533
|
+
activeText.citations = part.citations;
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1586
1536
|
delete activeTextContent[part.id];
|
|
1587
1537
|
}
|
|
1588
1538
|
|
package/src/global.ts
CHANGED
|
@@ -2,8 +2,20 @@ import type { ProviderV2, ProviderV3, ProviderV4 } from '@ai-sdk/provider';
|
|
|
2
2
|
import type { LogWarningsFunction } from './logger/log-warnings';
|
|
3
3
|
import type { Telemetry } from './telemetry/telemetry';
|
|
4
4
|
|
|
5
|
-
//
|
|
5
|
+
// Each installed copy contributes its own types without conflicting with other
|
|
6
|
+
// versions. This is a type-only key; no symbol is created at runtime.
|
|
7
|
+
declare const aiSdkGlobalsKey: unique symbol;
|
|
8
|
+
|
|
6
9
|
declare global {
|
|
10
|
+
interface AISDKGlobalTypes {
|
|
11
|
+
[aiSdkGlobalsKey]: {
|
|
12
|
+
provider: ProviderV4 | ProviderV3 | ProviderV2;
|
|
13
|
+
logWarnings: LogWarningsFunction;
|
|
14
|
+
telemetry: Telemetry;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Keep these registry-based declarations identical across major versions.
|
|
7
19
|
/**
|
|
8
20
|
* The default provider to use for the AI SDK.
|
|
9
21
|
* String model ids are resolved to the default provider and model id.
|
|
@@ -13,7 +25,9 @@ declare global {
|
|
|
13
25
|
*
|
|
14
26
|
* @see https://ai-sdk.dev/docs/ai-sdk-core/provider-management#global-provider-configuration
|
|
15
27
|
*/
|
|
16
|
-
var AI_SDK_DEFAULT_PROVIDER:
|
|
28
|
+
var AI_SDK_DEFAULT_PROVIDER:
|
|
29
|
+
| AISDKGlobalTypes[keyof AISDKGlobalTypes]['provider']
|
|
30
|
+
| undefined;
|
|
17
31
|
|
|
18
32
|
/**
|
|
19
33
|
* The warning logger to use for the AI SDK.
|
|
@@ -23,7 +37,10 @@ declare global {
|
|
|
23
37
|
*
|
|
24
38
|
* If set to false, no warnings are logged.
|
|
25
39
|
*/
|
|
26
|
-
var AI_SDK_LOG_WARNINGS:
|
|
40
|
+
var AI_SDK_LOG_WARNINGS:
|
|
41
|
+
| AISDKGlobalTypes[keyof AISDKGlobalTypes]['logWarnings']
|
|
42
|
+
| undefined
|
|
43
|
+
| false;
|
|
27
44
|
|
|
28
45
|
/**
|
|
29
46
|
* Globally registered telemetry integrations for the AI SDK.
|
|
@@ -34,5 +51,7 @@ declare global {
|
|
|
34
51
|
* Prefer using `registerTelemetry()` from `'ai'` instead of
|
|
35
52
|
* assigning this directly.
|
|
36
53
|
*/
|
|
37
|
-
var AI_SDK_TELEMETRY_INTEGRATIONS:
|
|
54
|
+
var AI_SDK_TELEMETRY_INTEGRATIONS:
|
|
55
|
+
| Array<AISDKGlobalTypes[keyof AISDKGlobalTypes]['telemetry']>
|
|
56
|
+
| undefined;
|
|
38
57
|
}
|
|
@@ -24,7 +24,7 @@ export function simulateStreamingMiddleware(): LanguageModelMiddleware {
|
|
|
24
24
|
for (const part of result.content) {
|
|
25
25
|
switch (part.type) {
|
|
26
26
|
case 'text': {
|
|
27
|
-
if (part.text.length > 0) {
|
|
27
|
+
if (part.text.length > 0 || (part.citations?.length ?? 0) > 0) {
|
|
28
28
|
controller.enqueue({
|
|
29
29
|
type: 'text-start',
|
|
30
30
|
id: String(id),
|
|
@@ -37,7 +37,13 @@ export function simulateStreamingMiddleware(): LanguageModelMiddleware {
|
|
|
37
37
|
id: String(id),
|
|
38
38
|
delta: part.text,
|
|
39
39
|
});
|
|
40
|
-
controller.enqueue({
|
|
40
|
+
controller.enqueue({
|
|
41
|
+
type: 'text-end',
|
|
42
|
+
id: String(id),
|
|
43
|
+
...(part.citations != null
|
|
44
|
+
? { citations: part.citations }
|
|
45
|
+
: {}),
|
|
46
|
+
});
|
|
41
47
|
id++;
|
|
42
48
|
}
|
|
43
49
|
break;
|