ai 6.0.199 → 6.0.200
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 +7 -0
- package/dist/index.js +77 -10
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +77 -10
- package/dist/index.mjs.map +1 -1
- package/dist/internal/index.js +1 -1
- package/dist/internal/index.mjs +1 -1
- package/docs/04-ai-sdk-ui/01-overview.mdx +1 -1
- package/package.json +3 -3
- package/src/generate-text/stream-text.ts +88 -5
- package/src/ui/process-ui-message-stream.ts +6 -5
- package/src/util/create-id-map.ts +11 -0
package/dist/internal/index.js
CHANGED
|
@@ -152,7 +152,7 @@ function detectMediaType({
|
|
|
152
152
|
var import_provider_utils2 = require("@ai-sdk/provider-utils");
|
|
153
153
|
|
|
154
154
|
// src/version.ts
|
|
155
|
-
var VERSION = true ? "6.0.
|
|
155
|
+
var VERSION = true ? "6.0.200" : "0.0.0-test";
|
|
156
156
|
|
|
157
157
|
// src/util/download/download.ts
|
|
158
158
|
var download = async ({
|
package/dist/internal/index.mjs
CHANGED
|
@@ -34,7 +34,7 @@ Here is a comparison of the supported functions across these frameworks:
|
|
|
34
34
|
|
|
35
35
|
Explore these example implementations for different frameworks:
|
|
36
36
|
|
|
37
|
-
- [**Next.js**](https://github.com/vercel/ai/tree/main/examples/next
|
|
37
|
+
- [**Next.js**](https://github.com/vercel/ai/tree/main/examples/ai-e2e-next)
|
|
38
38
|
- [**Nuxt**](https://github.com/vercel/ai/tree/main/examples/nuxt-openai)
|
|
39
39
|
- [**SvelteKit**](https://github.com/vercel/ai/tree/main/examples/sveltekit-openai)
|
|
40
40
|
- [**Angular**](https://github.com/vercel/ai/tree/main/examples/angular)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.200",
|
|
4
4
|
"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.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -45,9 +45,9 @@
|
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@opentelemetry/api": "^1.9.0",
|
|
48
|
-
"@ai-sdk/gateway": "3.0.127",
|
|
49
48
|
"@ai-sdk/provider": "3.0.10",
|
|
50
|
-
"@ai-sdk/provider-utils": "4.0.27"
|
|
49
|
+
"@ai-sdk/provider-utils": "4.0.27",
|
|
50
|
+
"@ai-sdk/gateway": "3.0.127"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@edge-runtime/vm": "^5.0.0",
|
|
@@ -128,6 +128,35 @@ const originalGenerateId = createIdGenerator({
|
|
|
128
128
|
size: 24,
|
|
129
129
|
});
|
|
130
130
|
|
|
131
|
+
// chunk types that count as model output; used to distinguish empty
|
|
132
|
+
// incomplete streams from incomplete streams with partial results.
|
|
133
|
+
// exhaustive so that new chunk types must be classified explicitly:
|
|
134
|
+
const isOutputChunkType = {
|
|
135
|
+
file: true,
|
|
136
|
+
source: true,
|
|
137
|
+
'text-start': true,
|
|
138
|
+
'text-end': true,
|
|
139
|
+
'text-delta': true,
|
|
140
|
+
'reasoning-start': true,
|
|
141
|
+
'reasoning-end': true,
|
|
142
|
+
'reasoning-delta': true,
|
|
143
|
+
'tool-input-start': true,
|
|
144
|
+
'tool-input-end': true,
|
|
145
|
+
'tool-input-delta': true,
|
|
146
|
+
'tool-approval-request': true,
|
|
147
|
+
'tool-call': true,
|
|
148
|
+
'tool-result': true,
|
|
149
|
+
'tool-error': true,
|
|
150
|
+
'stream-start': false,
|
|
151
|
+
'response-metadata': false,
|
|
152
|
+
finish: false,
|
|
153
|
+
error: false,
|
|
154
|
+
raw: false,
|
|
155
|
+
} as const satisfies Record<
|
|
156
|
+
SingleRequestTextStreamPart<ToolSet>['type'],
|
|
157
|
+
boolean
|
|
158
|
+
>;
|
|
159
|
+
|
|
131
160
|
/**
|
|
132
161
|
* A transformation that is applied to the stream.
|
|
133
162
|
*
|
|
@@ -836,6 +865,7 @@ class DefaultStreamTextResult<
|
|
|
836
865
|
let recordedRequest: LanguageModelRequestMetadata = {};
|
|
837
866
|
let recordedWarnings: Array<CallWarning> = [];
|
|
838
867
|
const recordedSteps: StepResult<TOOLS>[] = [];
|
|
868
|
+
let recordedNoOutputError: NoOutputGeneratedError | undefined;
|
|
839
869
|
|
|
840
870
|
// Track provider-executed tool calls that support deferred results
|
|
841
871
|
// (e.g., code_execution in programmatic tool calling scenarios).
|
|
@@ -885,7 +915,13 @@ class DefaultStreamTextResult<
|
|
|
885
915
|
}
|
|
886
916
|
|
|
887
917
|
if (part.type === 'error') {
|
|
888
|
-
|
|
918
|
+
const error = wrapGatewayError(part.error);
|
|
919
|
+
|
|
920
|
+
if (NoOutputGeneratedError.isInstance(error)) {
|
|
921
|
+
recordedNoOutputError = error;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
await onError({ error });
|
|
889
925
|
}
|
|
890
926
|
|
|
891
927
|
if (part.type === 'text-start') {
|
|
@@ -1080,12 +1116,13 @@ class DefaultStreamTextResult<
|
|
|
1080
1116
|
|
|
1081
1117
|
async flush(controller) {
|
|
1082
1118
|
try {
|
|
1083
|
-
if (recordedSteps.length === 0) {
|
|
1119
|
+
if (recordedSteps.length === 0 || recordedNoOutputError != null) {
|
|
1084
1120
|
const error = abortSignal?.aborted
|
|
1085
1121
|
? abortSignal.reason
|
|
1086
|
-
:
|
|
1122
|
+
: (recordedNoOutputError ??
|
|
1123
|
+
new NoOutputGeneratedError({
|
|
1087
1124
|
message: 'No output generated. Check the stream for errors.',
|
|
1088
|
-
});
|
|
1125
|
+
}));
|
|
1089
1126
|
|
|
1090
1127
|
self._finishReason.reject(error);
|
|
1091
1128
|
self._rawFinishReason.reject(error);
|
|
@@ -1721,6 +1758,14 @@ class DefaultStreamTextResult<
|
|
|
1721
1758
|
let stepFinishReason: FinishReason = 'other';
|
|
1722
1759
|
let stepRawFinishReason: string | undefined = undefined;
|
|
1723
1760
|
|
|
1761
|
+
// terminal chunk = 'finish' or 'error'; absence on stream
|
|
1762
|
+
// end means the model stream is incomplete:
|
|
1763
|
+
let hasReceivedTerminalChunk = false;
|
|
1764
|
+
|
|
1765
|
+
// output chunk = any content chunk (text, tool calls, etc.);
|
|
1766
|
+
// used to distinguish empty incomplete streams from partial results:
|
|
1767
|
+
let hasReceivedOutputChunk = false;
|
|
1768
|
+
|
|
1724
1769
|
let stepUsage: LanguageModelUsage = createNullLanguageModelUsage();
|
|
1725
1770
|
let stepProviderMetadata: ProviderMetadata | undefined;
|
|
1726
1771
|
let stepFirstChunk = true;
|
|
@@ -1771,6 +1816,11 @@ class DefaultStreamTextResult<
|
|
|
1771
1816
|
}
|
|
1772
1817
|
|
|
1773
1818
|
const chunkType = chunk.type;
|
|
1819
|
+
|
|
1820
|
+
if (isOutputChunkType[chunkType]) {
|
|
1821
|
+
hasReceivedOutputChunk = true;
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1774
1824
|
switch (chunkType) {
|
|
1775
1825
|
case 'tool-approval-request':
|
|
1776
1826
|
case 'text-start':
|
|
@@ -1841,6 +1891,8 @@ class DefaultStreamTextResult<
|
|
|
1841
1891
|
}
|
|
1842
1892
|
|
|
1843
1893
|
case 'finish': {
|
|
1894
|
+
hasReceivedTerminalChunk = true;
|
|
1895
|
+
|
|
1844
1896
|
// Note: tool executions might not be finished yet when the finish event is emitted.
|
|
1845
1897
|
// store usage and finish reason for promises and onFinish callback:
|
|
1846
1898
|
stepUsage = chunk.usage;
|
|
@@ -1917,6 +1969,7 @@ class DefaultStreamTextResult<
|
|
|
1917
1969
|
}
|
|
1918
1970
|
|
|
1919
1971
|
case 'error': {
|
|
1972
|
+
hasReceivedTerminalChunk = true;
|
|
1920
1973
|
controller.enqueue(chunk);
|
|
1921
1974
|
stepFinishReason = 'error';
|
|
1922
1975
|
break;
|
|
@@ -1940,6 +1993,25 @@ class DefaultStreamTextResult<
|
|
|
1940
1993
|
|
|
1941
1994
|
// invoke onFinish callback and resolve toolResults promise when the stream is about to close:
|
|
1942
1995
|
async flush(controller) {
|
|
1996
|
+
// emit an error when an incomplete model stream produced no
|
|
1997
|
+
// output instead of recording an empty step. incomplete
|
|
1998
|
+
// streams with partial output retain the partial result:
|
|
1999
|
+
if (!hasReceivedTerminalChunk && !hasReceivedOutputChunk) {
|
|
2000
|
+
controller.enqueue({
|
|
2001
|
+
type: 'error',
|
|
2002
|
+
error: new NoOutputGeneratedError({
|
|
2003
|
+
message:
|
|
2004
|
+
'No output generated. The model stream ended without a finish chunk.',
|
|
2005
|
+
}),
|
|
2006
|
+
});
|
|
2007
|
+
|
|
2008
|
+
doStreamSpan.end();
|
|
2009
|
+
clearStepTimeout();
|
|
2010
|
+
clearChunkTimeout();
|
|
2011
|
+
self.closeStream();
|
|
2012
|
+
return;
|
|
2013
|
+
}
|
|
2014
|
+
|
|
1943
2015
|
const stepToolCallsJson =
|
|
1944
2016
|
stepToolCalls.length > 0
|
|
1945
2017
|
? JSON.stringify(stepToolCalls)
|
|
@@ -2316,13 +2388,24 @@ class DefaultStreamTextResult<
|
|
|
2316
2388
|
);
|
|
2317
2389
|
}
|
|
2318
2390
|
|
|
2391
|
+
private rejectResultPromises(error: unknown) {
|
|
2392
|
+
if (this._finishReason.isPending()) this._finishReason.reject(error);
|
|
2393
|
+
if (this._rawFinishReason.isPending()) this._rawFinishReason.reject(error);
|
|
2394
|
+
if (this._totalUsage.isPending()) this._totalUsage.reject(error);
|
|
2395
|
+
if (this._steps.isPending()) this._steps.reject(error);
|
|
2396
|
+
}
|
|
2397
|
+
|
|
2319
2398
|
async consumeStream(options?: ConsumeStreamOptions): Promise<void> {
|
|
2320
2399
|
try {
|
|
2321
2400
|
await consumeStream({
|
|
2322
2401
|
stream: this.fullStream,
|
|
2323
|
-
onError:
|
|
2402
|
+
onError: error => {
|
|
2403
|
+
this.rejectResultPromises(error);
|
|
2404
|
+
options?.onError?.(error);
|
|
2405
|
+
},
|
|
2324
2406
|
});
|
|
2325
2407
|
} catch (error) {
|
|
2408
|
+
this.rejectResultPromises(error);
|
|
2326
2409
|
options?.onError?.(error);
|
|
2327
2410
|
}
|
|
2328
2411
|
}
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
type InferUIMessageChunk,
|
|
10
10
|
type UIMessageChunk,
|
|
11
11
|
} from '../ui-message-stream/ui-message-chunks';
|
|
12
|
+
import { createIdMap } from '../util/create-id-map';
|
|
12
13
|
import type { ErrorHandler } from '../util/error-handler';
|
|
13
14
|
import { mergeObjects } from '../util/merge-objects';
|
|
14
15
|
import { parsePartialJson } from '../util/parse-partial-json';
|
|
@@ -68,9 +69,9 @@ export function createStreamingUIMessageState<UI_MESSAGE extends UIMessage>({
|
|
|
68
69
|
InferUIMessageTools<UI_MESSAGE>
|
|
69
70
|
>[],
|
|
70
71
|
} as UI_MESSAGE),
|
|
71
|
-
activeTextParts:
|
|
72
|
-
activeReasoningParts:
|
|
73
|
-
partialToolCalls:
|
|
72
|
+
activeTextParts: createIdMap(),
|
|
73
|
+
activeReasoningParts: createIdMap(),
|
|
74
|
+
partialToolCalls: createIdMap(),
|
|
74
75
|
};
|
|
75
76
|
}
|
|
76
77
|
|
|
@@ -763,8 +764,8 @@ export function processUIMessageStream<UI_MESSAGE extends UIMessage>({
|
|
|
763
764
|
|
|
764
765
|
case 'finish-step': {
|
|
765
766
|
// reset the current text and reasoning parts
|
|
766
|
-
state.activeTextParts =
|
|
767
|
-
state.activeReasoningParts =
|
|
767
|
+
state.activeTextParts = createIdMap();
|
|
768
|
+
state.activeReasoningParts = createIdMap();
|
|
768
769
|
break;
|
|
769
770
|
}
|
|
770
771
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a string-keyed map without an object prototype.
|
|
3
|
+
*
|
|
4
|
+
* Use this for lookup tables keyed by IDs or names that may come from outside
|
|
5
|
+
* the SDK, such as streamed chunk IDs or tool call IDs. Unlike `{}`, these maps
|
|
6
|
+
* do not inherit `__proto__`, `constructor`, or other Object prototype members,
|
|
7
|
+
* so a missing untrusted key cannot resolve to a shared prototype object.
|
|
8
|
+
*/
|
|
9
|
+
export function createIdMap<T>(): Record<string, T> {
|
|
10
|
+
return Object.create(null);
|
|
11
|
+
}
|