ai 7.0.129 → 7.0.131

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.
@@ -111,7 +111,7 @@ function asAsyncIterableStream(stream) {
111
111
  }
112
112
  //#endregion
113
113
  //#region src/version.ts
114
- const VERSION = "7.0.129";
114
+ const VERSION = "7.0.131";
115
115
  //#endregion
116
116
  //#region src/util/download/download.ts
117
117
  /**
@@ -227,7 +227,7 @@ import { generateText, isStepCount } from 'ai';
227
227
  import { searchTool } from 'some-tool-package';
228
228
 
229
229
  const { text } = await generateText({
230
- model: 'anthropic/claude-haiku-4.5',
230
+ model: 'anthropic/claude-haiku-5.5',
231
231
  prompt: 'When was Vercel Ship AI?',
232
232
  tools: {
233
233
  webSearch: searchTool,
@@ -40,7 +40,7 @@ const memory = anthropic.tools.memory_20250818({
40
40
  });
41
41
 
42
42
  const agent = new ToolLoopAgent({
43
- model: 'anthropic/claude-haiku-4.5',
43
+ model: 'anthropic/claude-haiku-5.5',
44
44
  tools: { memory },
45
45
  });
46
46
 
@@ -248,6 +248,9 @@ substitute models automatically.
248
248
 
249
249
  Invalid inputs throw `InvalidArgumentError`. Missing answers, mismatched answer
250
250
  types, invalid options, scores, or probabilities throw `InvalidResponseDataError`.
251
+ If the model refuses any question, the call throws
252
+ `Experimental_DecisionRefusalError` with the refused question IDs, and is not
253
+ retried.
251
254
  Transient provider failures use the normal retry policy (`maxRetries: 2` by
252
255
  default). Use `abortSignal` to cancel the decisions request, `headers` for request headers,
253
256
  and `providerOptions` for provider-specific settings.
@@ -92,13 +92,13 @@ const batch = await startBatch({
92
92
  {
93
93
  id: 'capital-france',
94
94
  type: 'text',
95
- model: 'claude-haiku-4-5',
95
+ model: 'claude-haiku-5-5',
96
96
  prompt: 'What is the capital of France?',
97
97
  },
98
98
  {
99
99
  id: 'capital-germany',
100
100
  type: 'text',
101
- model: 'claude-haiku-4-5',
101
+ model: 'claude-haiku-5-5',
102
102
  prompt: 'What is the capital of Germany?',
103
103
  },
104
104
  ],
@@ -80,7 +80,7 @@ export const anthropic = customProvider({
80
80
  languageModels: {
81
81
  opus: gateway('anthropic/claude-opus-5.5'),
82
82
  sonnet: gateway('anthropic/claude-sonnet-5.5'),
83
- haiku: gateway('anthropic/claude-haiku-4.5'),
83
+ haiku: gateway('anthropic/claude-haiku-5.5'),
84
84
  },
85
85
  fallbackProvider: gateway,
86
86
  });
@@ -390,7 +390,7 @@ export const registry = createProviderRegistry(
390
390
  // setup model name aliases
391
391
  anthropic: customProvider({
392
392
  languageModels: {
393
- fast: anthropic('claude-haiku-4-5'),
393
+ fast: anthropic('claude-haiku-5-5'),
394
394
 
395
395
  // simple model
396
396
  writing: anthropic('claude-sonnet-4-5'),
@@ -480,7 +480,7 @@ To see `streamText` in action, check out [these examples](#examples).
480
480
  type: 'AbortSignal',
481
481
  isOptional: true,
482
482
  description:
483
- 'An optional abort signal that can be used to cancel the call.',
483
+ 'An optional abort signal that can be used to cancel the call. When it fires during streaming, pending result promises such as text and steps reject with the abort reason without waiting for onAbort or provider cancellation. Completed steps are available through onAbort.',
484
484
  },
485
485
  {
486
486
  name: 'timeout',
@@ -57,7 +57,8 @@ The public core types are `Experimental_DecisionModel`,
57
57
 
58
58
  Unsupported types throw `Experimental_DecisionUnsupportedQuestionTypeError`
59
59
  before provider I/O. Invalid inputs throw `InvalidArgumentError`; malformed
60
- answers throw `InvalidResponseDataError`. Invalid answers are not retried.
60
+ answers throw `InvalidResponseDataError`. Questions the model refuses throw
61
+ `Experimental_DecisionRefusalError`. Invalid answers and refusals are not retried.
61
62
  Neither partial results nor missing probability synthesis are supported.
62
63
 
63
64
  ## Model resolution
@@ -0,0 +1,33 @@
1
+ ---
2
+ title: AI_DecisionRefusalError
3
+ description: Identify decision questions that a model declined to answer.
4
+ ---
5
+
6
+ # AI_DecisionRefusalError
7
+
8
+ This experimental error identifies questions that a decision model declined to
9
+ answer, such as a question the provider's safety policy refuses. It is exported
10
+ from `ai` and `@ai-sdk/provider` as `Experimental_DecisionRefusalError`.
11
+
12
+ Decision models report a refusal as an answer with `type: 'refusal'`.
13
+ `experimental_decide` throws this error when any answer is a refusal, after the
14
+ provider call and without retrying it.
15
+
16
+ ## Properties
17
+
18
+ - `questionIds`: The IDs of the refused questions.
19
+ - `provider`: The provider of the decision model.
20
+ - `modelId`: The decision model ID.
21
+ - `message`: A description of the refused questions and model.
22
+
23
+ ## Checking for this Error
24
+
25
+ Use the marker-based `isInstance` check, which works across package copies:
26
+
27
+ ```typescript
28
+ import { Experimental_DecisionRefusalError as DecisionRefusalError } from 'ai';
29
+
30
+ if (DecisionRefusalError.isInstance(error)) {
31
+ console.log(error.questionIds, error.modelId);
32
+ }
33
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai",
3
- "version": "7.0.129",
3
+ "version": "7.0.131",
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.105",
46
- "@ai-sdk/provider": "4.0.23",
47
- "@ai-sdk/provider-utils": "5.0.55"
45
+ "@ai-sdk/gateway": "4.0.107",
46
+ "@ai-sdk/provider": "4.0.24",
47
+ "@ai-sdk/provider-utils": "5.0.56"
48
48
  },
49
49
  "devDependencies": {
50
- "@ai-sdk/amazon-bedrock": "5.0.107",
51
- "@ai-sdk/deepseek": "3.0.60",
52
- "@ai-sdk/google": "4.0.89",
53
- "@ai-sdk/groq": "4.0.56",
54
- "@ai-sdk/huggingface": "2.0.64",
55
- "@ai-sdk/moonshotai": "3.0.64",
56
- "@ai-sdk/openai": "4.0.85",
50
+ "@ai-sdk/amazon-bedrock": "5.0.109",
51
+ "@ai-sdk/deepseek": "3.0.61",
52
+ "@ai-sdk/google": "4.0.90",
53
+ "@ai-sdk/groq": "4.0.57",
54
+ "@ai-sdk/huggingface": "2.0.65",
55
+ "@ai-sdk/moonshotai": "3.0.65",
56
+ "@ai-sdk/openai": "4.0.87",
57
57
  "@ai-sdk/test-server": "2.0.4",
58
- "@ai-sdk/xai": "5.0.16",
58
+ "@ai-sdk/xai": "5.0.18",
59
59
  "@edge-runtime/vm": "^5.0.0",
60
60
  "@smithy/eventstream-codec": "^4.3.3",
61
61
  "@smithy/util-utf8": "^4.3.3",
@@ -1,4 +1,5 @@
1
1
  import {
2
+ Experimental_DecisionRefusalError as DecisionRefusalError,
2
3
  Experimental_DecisionUnsupportedQuestionTypeError as DecisionUnsupportedQuestionTypeError,
3
4
  type Experimental_DecisionModelV4CallOptions as DecisionModelV4CallOptions,
4
5
  } from '@ai-sdk/provider';
@@ -152,6 +153,16 @@ export async function decide<
152
153
  answers: result.answers,
153
154
  rounding: result.rounding,
154
155
  });
156
+ const refusedQuestionIds = Object.keys(questions).filter(
157
+ id => result.answers[id]?.type === 'refusal',
158
+ );
159
+ if (refusedQuestionIds.length > 0) {
160
+ throw new DecisionRefusalError({
161
+ questionIds: refusedQuestionIds,
162
+ provider: model.provider,
163
+ modelId: model.modelId,
164
+ });
165
+ }
155
166
  await notify({
156
167
  event: { ...modelCallEvent, ...result },
157
168
  callbacks: [telemetryDispatcher.experimental_onDecisionModelCallEnd],
@@ -203,6 +203,7 @@ export function validateDecisionAnswers({
203
203
 
204
204
  for (const [id, question] of Object.entries(questions)) {
205
205
  const answer = answers[id];
206
+ if (isRecord(answer) && answer.type === 'refusal') continue;
206
207
  if (!isRecord(answer) || answer.type !== question.type) {
207
208
  invalidAnswer(
208
209
  answers,
@@ -4,6 +4,7 @@ export {
4
4
  AISDKError,
5
5
  APICallError,
6
6
  EmptyResponseBodyError,
7
+ Experimental_DecisionRefusalError,
7
8
  Experimental_DecisionUnsupportedQuestionTypeError,
8
9
  InvalidPromptError,
9
10
  InvalidResponseDataError,
@@ -1849,16 +1849,30 @@ class DefaultStreamTextResult<
1849
1849
 
1850
1850
  // resilient stream that handles abort signals and errors:
1851
1851
  const reader = stitchableStream.stream.getReader();
1852
+ const cancelOnAbort = () => {
1853
+ // Result promises must settle before any potentially stalled callback.
1854
+ this.rejectResultPromises(abortSignal?.reason);
1855
+ // Cancelling the reader releases a pending read immediately, even when
1856
+ // the provider body or its cancellation promise does not settle.
1857
+ void reader.cancel(abortSignal?.reason).catch(() => {});
1858
+ };
1859
+ const removeAbortListener = () =>
1860
+ abortSignal?.removeEventListener('abort', cancelOnAbort);
1852
1861
  let stream = new ReadableStream<InternalTextStreamPart<TOOLS>>({
1853
1862
  async start(controller) {
1854
1863
  // send start event:
1855
1864
  controller.enqueue({ type: 'start' });
1865
+ abortSignal?.addEventListener('abort', cancelOnAbort, { once: true });
1866
+ if (abortSignal?.aborted) {
1867
+ cancelOnAbort();
1868
+ }
1856
1869
  },
1857
1870
 
1858
1871
  async pull(controller) {
1859
1872
  // abort handling:
1860
1873
  async function abort() {
1861
1874
  isAborted = true;
1875
+ removeAbortListener();
1862
1876
 
1863
1877
  await notify({
1864
1878
  event: {
@@ -1885,18 +1899,20 @@ class DefaultStreamTextResult<
1885
1899
  try {
1886
1900
  const { done, value } = await reader.read();
1887
1901
 
1888
- if (done) {
1889
- controller.close();
1902
+ if (abortSignal?.aborted) {
1903
+ await abort();
1890
1904
  return;
1891
1905
  }
1892
1906
 
1893
- if (abortSignal?.aborted) {
1894
- await abort();
1907
+ if (done) {
1908
+ removeAbortListener();
1909
+ controller.close();
1895
1910
  return;
1896
1911
  }
1897
1912
 
1898
1913
  controller.enqueue(value);
1899
1914
  } catch (error) {
1915
+ removeAbortListener();
1900
1916
  if (isAbortError(error) && abortSignal?.aborted) {
1901
1917
  await abort();
1902
1918
  } else {
@@ -1907,7 +1923,8 @@ class DefaultStreamTextResult<
1907
1923
  },
1908
1924
 
1909
1925
  cancel(reason) {
1910
- return stitchableStream.stream.cancel(reason);
1926
+ removeAbortListener();
1927
+ return reader.cancel(reason);
1911
1928
  },
1912
1929
  });
1913
1930