@ai-sdk/xai 3.0.81 → 3.0.83

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/xai",
3
- "version": "3.0.81",
3
+ "version": "3.0.83",
4
4
  "license": "Apache-2.0",
5
5
  "sideEffects": false,
6
6
  "main": "./dist/index.js",
@@ -29,17 +29,17 @@
29
29
  }
30
30
  },
31
31
  "dependencies": {
32
+ "@ai-sdk/openai-compatible": "2.0.41",
32
33
  "@ai-sdk/provider": "3.0.8",
33
- "@ai-sdk/provider-utils": "4.0.23",
34
- "@ai-sdk/openai-compatible": "2.0.41"
34
+ "@ai-sdk/provider-utils": "4.0.23"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/node": "20.17.24",
38
38
  "tsup": "^8",
39
39
  "typescript": "5.8.3",
40
40
  "zod": "3.25.76",
41
- "@ai-sdk/test-server": "1.0.3",
42
- "@vercel/ai-tsconfig": "0.0.0"
41
+ "@vercel/ai-tsconfig": "0.0.0",
42
+ "@ai-sdk/test-server": "1.0.3"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "zod": "^3.25.76 || ^4.1.8"
@@ -123,7 +123,47 @@ export async function convertToXaiResponsesInput({
123
123
  break;
124
124
  }
125
125
 
126
- case 'reasoning':
126
+ case 'reasoning': {
127
+ const itemId =
128
+ typeof part.providerOptions?.xai?.itemId === 'string'
129
+ ? part.providerOptions.xai.itemId
130
+ : undefined;
131
+ const encryptedContent =
132
+ typeof part.providerOptions?.xai?.reasoningEncryptedContent ===
133
+ 'string'
134
+ ? part.providerOptions.xai.reasoningEncryptedContent
135
+ : undefined;
136
+
137
+ if (itemId != null || encryptedContent != null) {
138
+ const summaryParts: Array<{
139
+ type: 'summary_text';
140
+ text: string;
141
+ }> = [];
142
+ if (part.text.length > 0) {
143
+ summaryParts.push({
144
+ type: 'summary_text',
145
+ text: part.text,
146
+ });
147
+ }
148
+
149
+ input.push({
150
+ type: 'reasoning',
151
+ id: itemId ?? '',
152
+ summary: summaryParts,
153
+ status: 'completed',
154
+ ...(encryptedContent != null && {
155
+ encrypted_content: encryptedContent,
156
+ }),
157
+ });
158
+ } else {
159
+ inputWarnings.push({
160
+ type: 'other',
161
+ message:
162
+ 'Reasoning parts without itemId or encrypted content cannot be sent back to xAI. Skipping.',
163
+ });
164
+ }
165
+ break;
166
+ }
127
167
  case 'file': {
128
168
  inputWarnings.push({
129
169
  type: 'other',
@@ -538,6 +538,12 @@ export const xaiResponsesChunkSchema = z.union([
538
538
  usage: xaiResponsesUsageSchema.nullish(),
539
539
  }),
540
540
  }),
541
+ z.object({
542
+ type: z.literal('error'),
543
+ code: z.string().nullish(),
544
+ message: z.string(),
545
+ param: z.string().nullish(),
546
+ }),
541
547
  z.object({
542
548
  type: z.literal('response.done'),
543
549
  response: xaiResponsesResponseSchema,
@@ -110,7 +110,7 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
110
110
 
111
111
  const { input, inputWarnings } = await convertToXaiResponsesInput({
112
112
  prompt,
113
- store: true,
113
+ store: options.store ?? true,
114
114
  });
115
115
  warnings.push(...inputWarnings);
116
116
 
@@ -366,12 +366,15 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
366
366
  .map(s => s.text)
367
367
  .filter(text => text && text.length > 0);
368
368
 
369
- if (summaryTexts.length > 0) {
370
- const reasoningText = summaryTexts.join('');
371
- if (part.encrypted_content || part.id) {
372
- content.push({
373
- type: 'reasoning',
374
- text: reasoningText,
369
+ const reasoningText = summaryTexts.join('');
370
+
371
+ // condition changed here since encrypted content can now come with empty reasoning text
372
+ if (reasoningText || part.encrypted_content) {
373
+ const hasMetadata = part.encrypted_content || part.id;
374
+ content.push({
375
+ type: 'reasoning',
376
+ text: reasoningText,
377
+ ...(hasMetadata && {
375
378
  providerMetadata: {
376
379
  xai: {
377
380
  ...(part.encrypted_content && {
@@ -380,13 +383,8 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
380
383
  ...(part.id && { itemId: part.id }),
381
384
  },
382
385
  },
383
- });
384
- } else {
385
- content.push({
386
- type: 'reasoning',
387
- text: reasoningText,
388
- });
389
- }
386
+ }),
387
+ });
390
388
  }
391
389
  break;
392
390
  }
@@ -684,6 +682,11 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 {
684
682
  return;
685
683
  }
686
684
 
685
+ if (event.type === 'error') {
686
+ controller.enqueue({ type: 'error', error: event });
687
+ return;
688
+ }
689
+
687
690
  // Custom tool call input streaming - already handled by output_item events
688
691
  if (
689
692
  event.type === 'response.custom_tool_call_input.delta' ||
@@ -24,6 +24,8 @@ export const xaiLanguageModelResponsesOptions = z.object({
24
24
  topLogprobs: z.number().int().min(0).max(8).optional(),
25
25
  /**
26
26
  * Whether to store the input message(s) and model response for later retrieval.
27
+ * Must be set to `false` for teams with Zero Data Retention (ZDR) enabled,
28
+ * otherwise the API will return an error.
27
29
  * @default true
28
30
  */
29
31
  store: z.boolean().optional(),