@ai-sdk/xai 4.0.59 → 5.0.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/docs/01-xai.mdx CHANGED
@@ -65,13 +65,6 @@ first argument is the model id, e.g. `grok-4.6`.
65
65
  const model = xai('grok-4.6');
66
66
  ```
67
67
 
68
- <Note>
69
- Since AI SDK 7, `xai(modelId)` uses the xAI Responses API by default. To use
70
- the [Chat Completions
71
- API](https://docs.x.ai/docs/api-reference#chat-completions) (legacy), use
72
- `xai.chat(modelId)`.
73
- </Note>
74
-
75
68
  ### Example
76
69
 
77
70
  You can use xAI language models to generate text with the `generateText` function:
@@ -94,8 +87,7 @@ and support structured data generation with [`Output`](/docs/reference/ai-sdk-co
94
87
 
95
88
  For models with configurable reasoning, you can control how much effort the
96
89
  model spends thinking before responding via
97
- `providerOptions.xai.reasoningEffort`. This works for both the Responses API
98
- (default) and the Chat Completions API (`xai.chat()`).
90
+ `providerOptions.xai.reasoningEffort`.
99
91
 
100
92
  ```ts
101
93
  import { xai } from '@ai-sdk/xai';
@@ -141,9 +133,7 @@ The AI SDK option accepts these values, but each xAI model supports a subset:
141
133
  ### Priority Processing
142
134
 
143
135
  `providerOptions.xai.serviceTier` requests higher scheduling priority, which
144
- typically lowers time-to-first-token and speeds up inter-token latency. This
145
- works for both the Responses API (default) and the Chat Completions API
146
- (`xai.chat()`).
136
+ typically lowers time-to-first-token and speeds up inter-token latency.
147
137
 
148
138
  ```ts
149
139
  import { xai } from '@ai-sdk/xai';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/xai",
3
- "version": "4.0.59",
3
+ "version": "5.0.0",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
package/src/index.ts CHANGED
@@ -1,8 +1,3 @@
1
- export type {
2
- XaiLanguageModelChatOptions,
3
- /** @deprecated Use `XaiLanguageModelChatOptions` instead. */
4
- XaiLanguageModelChatOptions as XaiProviderOptions,
5
- } from './xai-chat-language-model-options';
6
1
  export type { XaiErrorData } from './xai-error';
7
2
  export type { XaiFilePartProviderOptions } from './xai-file-part-options';
8
3
  export type {
package/src/xai-batch.ts CHANGED
@@ -40,13 +40,8 @@ import {
40
40
  type InferSchema,
41
41
  } from '@ai-sdk/provider-utils';
42
42
  import { z } from 'zod/v4';
43
- import { convertXaiChatUsage } from './convert-xai-chat-usage';
44
43
  import { getResponseMetadata } from './get-response-metadata';
45
44
  import { mapXaiFinishReason } from './map-xai-finish-reason';
46
- import {
47
- xaiChatResponseSchema,
48
- type XaiChatResponse,
49
- } from './xai-chat-language-model';
50
45
  import { xaiFailedResponseHandler } from './xai-error';
51
46
  import { xaiFilesResponseSchema } from './files/xai-files-api';
52
47
  import {
@@ -171,6 +166,59 @@ const xaiBatchResultSchema = z.object({
171
166
 
172
167
  type XaiBatchResult = z.infer<typeof xaiBatchResultSchema>;
173
168
 
169
+ // xAI returns text batch results in Chat Completions API shape even when the
170
+ // request was submitted to the Responses API endpoint.
171
+ const xaiBatchTextResponseSchema = z.object({
172
+ id: z.string().nullish(),
173
+ created: z.number().nullish(),
174
+ model: z.string().nullish(),
175
+ choices: z
176
+ .array(
177
+ z.object({
178
+ message: z.object({
179
+ role: z.enum(['assistant', 'tool']),
180
+ content: z.string().nullish(),
181
+ reasoning_content: z.string().nullish(),
182
+ tool_calls: z
183
+ .array(
184
+ z.object({
185
+ id: z.string(),
186
+ type: z.literal('function'),
187
+ function: z.object({
188
+ name: z.string(),
189
+ arguments: z.string(),
190
+ }),
191
+ }),
192
+ )
193
+ .nullish(),
194
+ }),
195
+ index: z.number(),
196
+ finish_reason: z.string().nullish(),
197
+ }),
198
+ )
199
+ .nullish(),
200
+ usage: z
201
+ .object({
202
+ prompt_tokens: z.number(),
203
+ completion_tokens: z.number(),
204
+ total_tokens: z.number(),
205
+ cost_in_usd_ticks: z.number().nullish(),
206
+ prompt_tokens_details: z
207
+ .object({ cached_tokens: z.number().nullish() })
208
+ .nullish(),
209
+ completion_tokens_details: z
210
+ .object({ reasoning_tokens: z.number().nullish() })
211
+ .nullish(),
212
+ })
213
+ .nullish(),
214
+ citations: z.array(z.string().url()).nullish(),
215
+ service_tier: z.string().nullish(),
216
+ code: z.string().nullish(),
217
+ error: z.string().nullish(),
218
+ });
219
+
220
+ type XaiBatchTextResponse = z.infer<typeof xaiBatchTextResponseSchema>;
221
+
174
222
  const xaiBatchResultsPageSchema = lazySchema(() =>
175
223
  zodSchema(
176
224
  z.object({
@@ -460,19 +508,17 @@ export class XaiBatch implements BatchV4<XaiBatchModelIds> {
460
508
  };
461
509
  }
462
510
 
463
- // xAI returns text batch results in chat completion format, including for
464
- // requests submitted to the Responses API endpoint.
465
511
  const response = result.batch_result?.response;
466
512
  if (response?.chat_get_completion != null) {
467
513
  const validation = await safeValidateTypes({
468
514
  value: response.chat_get_completion,
469
- schema: xaiChatResponseSchema,
515
+ schema: zodSchema(xaiBatchTextResponseSchema),
470
516
  });
471
517
  if (!validation.success) {
472
518
  return invalidXaiBatchResult(result.batch_request_id);
473
519
  }
474
520
 
475
- const conversion = convertXaiChatBatchResponse(validation.value);
521
+ const conversion = convertXaiBatchTextResponse(validation.value);
476
522
  return conversion.success
477
523
  ? {
478
524
  type: 'text',
@@ -738,8 +784,8 @@ function invalidXaiImageBatchResult(id: string): ImageBatchV4ItemResult {
738
784
  };
739
785
  }
740
786
 
741
- function convertXaiChatBatchResponse(
742
- response: XaiChatResponse,
787
+ function convertXaiBatchTextResponse(
788
+ response: XaiBatchTextResponse,
743
789
  ): XaiBatchResponseConversion {
744
790
  if (response.error != null) {
745
791
  return {
@@ -829,7 +875,7 @@ function convertXaiChatBatchResponse(
829
875
  raw: lastAssistantChoice?.finish_reason ?? undefined,
830
876
  },
831
877
  usage: response.usage
832
- ? convertXaiChatUsage(response.usage)
878
+ ? convertXaiBatchTextUsage(response.usage)
833
879
  : createNullLanguageModelUsage(),
834
880
  response: getResponseMetadata(response),
835
881
  warnings: [],
@@ -849,3 +895,31 @@ function convertXaiChatBatchResponse(
849
895
  },
850
896
  };
851
897
  }
898
+
899
+ function convertXaiBatchTextUsage(
900
+ usage: NonNullable<XaiBatchTextResponse['usage']>,
901
+ ) {
902
+ const cacheReadTokens = usage.prompt_tokens_details?.cached_tokens ?? 0;
903
+ const reasoningTokens =
904
+ usage.completion_tokens_details?.reasoning_tokens ?? 0;
905
+ const promptTokensIncludesCached = cacheReadTokens <= usage.prompt_tokens;
906
+
907
+ return {
908
+ inputTokens: {
909
+ total: promptTokensIncludesCached
910
+ ? usage.prompt_tokens
911
+ : usage.prompt_tokens + cacheReadTokens,
912
+ noCache: promptTokensIncludesCached
913
+ ? usage.prompt_tokens - cacheReadTokens
914
+ : usage.prompt_tokens,
915
+ cacheRead: cacheReadTokens,
916
+ cacheWrite: undefined,
917
+ },
918
+ outputTokens: {
919
+ total: usage.completion_tokens + reasoningTokens,
920
+ text: usage.completion_tokens,
921
+ reasoning: reasoningTokens,
922
+ },
923
+ raw: usage,
924
+ };
925
+ }
package/src/xai-error.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { createJsonErrorResponseHandler } from '@ai-sdk/provider-utils';
2
2
  import { z } from 'zod/v4';
3
3
 
4
- const chatCompletionsErrorSchema = z.object({
4
+ const apiErrorSchema = z.object({
5
5
  error: z.object({
6
6
  message: z.string(),
7
7
  type: z.string().nullish(),
@@ -21,7 +21,7 @@ const speechErrorSchema = z.object({
21
21
  });
22
22
 
23
23
  export const xaiErrorDataSchema = z.union([
24
- chatCompletionsErrorSchema,
24
+ apiErrorSchema,
25
25
  responsesErrorSchema,
26
26
  speechErrorSchema,
27
27
  ]);
@@ -19,8 +19,6 @@ import {
19
19
  type FetchFunction,
20
20
  type WebSocketConstructor,
21
21
  } from '@ai-sdk/provider-utils';
22
- import { XaiChatLanguageModel } from './xai-chat-language-model';
23
- import type { XaiChatModelId } from './xai-chat-language-model-options';
24
22
  import { XaiImageModel } from './xai-image-model';
25
23
  import type { XaiImageModelId } from './xai-image-settings';
26
24
  import { XaiBatch } from './xai-batch';
@@ -43,11 +41,6 @@ export interface XaiProvider extends ProviderV4 {
43
41
  */
44
42
  languageModel(modelId: XaiResponsesModelId): LanguageModelV4;
45
43
 
46
- /**
47
- * Creates an Xai chat model for text generation.
48
- */
49
- chat: (modelId: XaiChatModelId) => LanguageModelV4;
50
-
51
44
  /**
52
45
  * Creates an Xai responses model for text generation.
53
46
  */
@@ -165,16 +158,6 @@ export function createXai(options: XaiProviderSettings = {}): XaiProvider {
165
158
  `ai-sdk/xai/${VERSION}`,
166
159
  );
167
160
 
168
- const createChatLanguageModel = (modelId: XaiChatModelId) => {
169
- return new XaiChatLanguageModel(modelId, {
170
- provider: 'xai.chat',
171
- baseURL,
172
- headers: getHeaders,
173
- generateId,
174
- fetch: options.fetch,
175
- });
176
- };
177
-
178
161
  const createResponsesLanguageModel = (modelId: XaiResponsesModelId) => {
179
162
  return new XaiResponsesLanguageModel(modelId, {
180
163
  provider: 'xai.responses',
@@ -275,7 +258,6 @@ export function createXai(options: XaiProviderSettings = {}): XaiProvider {
275
258
 
276
259
  provider.specificationVersion = 'v4' as const;
277
260
  provider.languageModel = createResponsesLanguageModel;
278
- provider.chat = createChatLanguageModel;
279
261
  provider.responses = createResponsesLanguageModel;
280
262
  provider.embeddingModel = (modelId: string) => {
281
263
  throw new NoSuchModelError({ modelId, modelType: 'embeddingModel' });
@@ -1,181 +0,0 @@
1
- import {
2
- UnsupportedFunctionalityError,
3
- type SharedV4Warning,
4
- type LanguageModelV4Prompt,
5
- } from '@ai-sdk/provider';
6
- import {
7
- convertToBase64,
8
- getTopLevelMediaType,
9
- parseProviderOptions,
10
- resolveFullMediaType,
11
- resolveProviderReference,
12
- } from '@ai-sdk/provider-utils';
13
- import type { XaiChatPrompt, XaiUserMessageContent } from './xai-chat-prompt';
14
- import { xaiFilePartProviderOptions } from './xai-file-part-options';
15
-
16
- export async function convertToXaiChatMessages(
17
- prompt: LanguageModelV4Prompt,
18
- ): Promise<{
19
- messages: XaiChatPrompt;
20
- warnings: Array<SharedV4Warning>;
21
- }> {
22
- const messages: XaiChatPrompt = [];
23
- const warnings: Array<SharedV4Warning> = [];
24
-
25
- for (const { role, content } of prompt) {
26
- switch (role) {
27
- case 'system': {
28
- messages.push({ role: 'system', content });
29
- break;
30
- }
31
-
32
- case 'user': {
33
- if (content.length === 1 && content[0].type === 'text') {
34
- messages.push({ role: 'user', content: content[0].text });
35
- break;
36
- }
37
-
38
- const userContent: Array<XaiUserMessageContent> = [];
39
-
40
- for (const part of content) {
41
- switch (part.type) {
42
- case 'text': {
43
- userContent.push({ type: 'text', text: part.text });
44
- break;
45
- }
46
- case 'file': {
47
- switch (part.data.type) {
48
- case 'reference': {
49
- userContent.push({
50
- type: 'file',
51
- file: {
52
- file_id: resolveProviderReference({
53
- reference: part.data.reference,
54
- provider: 'xai',
55
- }),
56
- },
57
- });
58
- break;
59
- }
60
- case 'text': {
61
- throw new UnsupportedFunctionalityError({
62
- functionality: 'text file parts',
63
- });
64
- }
65
- case 'url':
66
- case 'data': {
67
- if (getTopLevelMediaType(part.mediaType) === 'image') {
68
- const filePartOptions = await parseProviderOptions({
69
- provider: 'xai',
70
- providerOptions: part.providerOptions,
71
- schema: xaiFilePartProviderOptions,
72
- });
73
-
74
- userContent.push({
75
- type: 'image_url',
76
- image_url: {
77
- url:
78
- part.data.type === 'url'
79
- ? part.data.url.toString()
80
- : `data:${resolveFullMediaType({ part })};base64,${convertToBase64(part.data.data)}`,
81
- ...(filePartOptions?.imageDetail != null && {
82
- detail: filePartOptions.imageDetail,
83
- }),
84
- },
85
- });
86
- } else {
87
- throw new UnsupportedFunctionalityError({
88
- functionality: `file part media type ${part.mediaType}`,
89
- });
90
- }
91
- break;
92
- }
93
- }
94
- break;
95
- }
96
- }
97
- }
98
-
99
- messages.push({ role: 'user', content: userContent });
100
-
101
- break;
102
- }
103
-
104
- case 'assistant': {
105
- let text = '';
106
- const toolCalls: Array<{
107
- id: string;
108
- type: 'function';
109
- function: { name: string; arguments: string };
110
- }> = [];
111
-
112
- for (const part of content) {
113
- switch (part.type) {
114
- case 'text': {
115
- text += part.text;
116
- break;
117
- }
118
- case 'tool-call': {
119
- toolCalls.push({
120
- id: part.toolCallId,
121
- type: 'function',
122
- function: {
123
- name: part.toolName,
124
- arguments: JSON.stringify(part.input),
125
- },
126
- });
127
- break;
128
- }
129
- }
130
- }
131
-
132
- messages.push({
133
- role: 'assistant',
134
- content: text,
135
- tool_calls: toolCalls.length > 0 ? toolCalls : undefined,
136
- });
137
-
138
- break;
139
- }
140
-
141
- case 'tool': {
142
- for (const toolResponse of content) {
143
- if (toolResponse.type === 'tool-approval-response') {
144
- continue;
145
- }
146
- const output = toolResponse.output;
147
-
148
- let contentValue: string;
149
- switch (output.type) {
150
- case 'text':
151
- case 'error-text':
152
- contentValue = output.value;
153
- break;
154
- case 'execution-denied':
155
- contentValue = output.reason ?? 'Tool call execution denied.';
156
- break;
157
- case 'content':
158
- case 'json':
159
- case 'error-json':
160
- contentValue = JSON.stringify(output.value);
161
- break;
162
- }
163
-
164
- messages.push({
165
- role: 'tool',
166
- tool_call_id: toolResponse.toolCallId,
167
- content: contentValue,
168
- });
169
- }
170
- break;
171
- }
172
-
173
- default: {
174
- const _exhaustiveCheck: never = role;
175
- throw new Error(`Unsupported role: ${_exhaustiveCheck}`);
176
- }
177
- }
178
- }
179
-
180
- return { messages, warnings };
181
- }
@@ -1,29 +0,0 @@
1
- import type { LanguageModelV4Usage } from '@ai-sdk/provider';
2
- import type { XaiChatUsage } from './xai-chat-language-model';
3
-
4
- export function convertXaiChatUsage(usage: XaiChatUsage): LanguageModelV4Usage {
5
- const cacheReadTokens = usage.prompt_tokens_details?.cached_tokens ?? 0;
6
- const reasoningTokens =
7
- usage.completion_tokens_details?.reasoning_tokens ?? 0;
8
-
9
- const promptTokensIncludesCached = cacheReadTokens <= usage.prompt_tokens;
10
-
11
- return {
12
- inputTokens: {
13
- total: promptTokensIncludesCached
14
- ? usage.prompt_tokens
15
- : usage.prompt_tokens + cacheReadTokens,
16
- noCache: promptTokensIncludesCached
17
- ? usage.prompt_tokens - cacheReadTokens
18
- : usage.prompt_tokens,
19
- cacheRead: cacheReadTokens,
20
- cacheWrite: undefined,
21
- },
22
- outputTokens: {
23
- total: usage.completion_tokens + reasoningTokens,
24
- text: usage.completion_tokens,
25
- reasoning: reasoningTokens,
26
- },
27
- raw: usage,
28
- };
29
- }
@@ -1,141 +0,0 @@
1
- import { z } from 'zod/v4';
2
-
3
- // https://docs.x.ai/docs/models
4
- export type XaiChatModelId =
5
- | 'grok-4.20-non-reasoning'
6
- | 'grok-4.20-reasoning'
7
- | 'grok-4.3'
8
- | 'grok-4.5'
9
- | 'grok-4.6'
10
- | 'grok-latest'
11
- | (string & {});
12
-
13
- // search source schemas
14
- const webSourceSchema = z.object({
15
- type: z.literal('web'),
16
- country: z.string().length(2).optional(),
17
- excludedWebsites: z.array(z.string()).max(5).optional(),
18
- allowedWebsites: z.array(z.string()).max(5).optional(),
19
- safeSearch: z.boolean().optional(),
20
- });
21
-
22
- const xSourceSchema = z.object({
23
- type: z.literal('x'),
24
- excludedXHandles: z.array(z.string()).optional(),
25
- includedXHandles: z.array(z.string()).optional(),
26
- postFavoriteCount: z.number().int().optional(),
27
- postViewCount: z.number().int().optional(),
28
- /**
29
- * @deprecated use `includedXHandles` instead
30
- */
31
- xHandles: z.array(z.string()).optional(),
32
- });
33
-
34
- const newsSourceSchema = z.object({
35
- type: z.literal('news'),
36
- country: z.string().length(2).optional(),
37
- excludedWebsites: z.array(z.string()).max(5).optional(),
38
- safeSearch: z.boolean().optional(),
39
- });
40
-
41
- const rssSourceSchema = z.object({
42
- type: z.literal('rss'),
43
- links: z.array(z.string().url()).max(1), // currently only supports one RSS link
44
- });
45
-
46
- const searchSourceSchema = z.discriminatedUnion('type', [
47
- webSourceSchema,
48
- xSourceSchema,
49
- newsSourceSchema,
50
- rssSourceSchema,
51
- ]);
52
-
53
- // xai-specific provider options
54
- export const xaiLanguageModelChatOptions = z.object({
55
- /**
56
- * Constrains how hard a reasoning model thinks before responding.
57
- *
58
- * - `none`: Disables reasoning entirely (supported by `grok-4.3` and newer
59
- * reasoning models). When set, no thinking tokens are used.
60
- * - `low` (default): Uses some reasoning tokens, but still fast.
61
- * - `medium`: More thinking for less-latency-sensitive applications.
62
- * - `high`: Uses more reasoning tokens for deeper thinking.
63
- * - `xhigh`: Uses the most reasoning tokens (supported by `grok-4.6`).
64
- *
65
- * Note: Not every Grok model accepts every value. Refer to xAI's docs for
66
- * the values supported by your selected model.
67
- *
68
- * @see https://docs.x.ai/docs/guides/reasoning
69
- */
70
- reasoningEffort: z
71
- .enum(['none', 'low', 'medium', 'high', 'xhigh'])
72
- .optional(),
73
- logprobs: z.boolean().optional(),
74
- topLogprobs: z.number().int().min(0).max(8).optional(),
75
-
76
- serviceTier: z.enum(['default', 'priority']).optional(),
77
-
78
- /**
79
- * Whether to enable parallel function calling during tool use.
80
- * When true, the model can call multiple functions in parallel.
81
- * When false, the model will call functions sequentially.
82
- * Defaults to true.
83
- */
84
- parallel_function_calling: z.boolean().optional(),
85
-
86
- /**
87
- * @deprecated xAI has deprecated Live Search (`search_parameters`) in favor
88
- * of the Agent Tools API. Requests using this option now return a "Live
89
- * search is deprecated" error. Use the `web_search` / `x_search` tools
90
- * instead (e.g. `xai.tools.webSearch()`, `xai.tools.xSearch()`) with
91
- * `xai.responses(modelId)`.
92
- *
93
- * @see https://docs.x.ai/docs/guides/tools/overview
94
- */
95
- searchParameters: z
96
- .object({
97
- /**
98
- * search mode preference
99
- * - "off": disables search completely
100
- * - "auto": model decides whether to search (default)
101
- * - "on": always enables search
102
- */
103
- mode: z.enum(['off', 'auto', 'on']),
104
-
105
- /**
106
- * whether to return citations in the response
107
- * defaults to true
108
- */
109
- returnCitations: z.boolean().optional(),
110
-
111
- /**
112
- * start date for search data (ISO8601 format: YYYY-MM-DD)
113
- */
114
- fromDate: z.string().optional(),
115
-
116
- /**
117
- * end date for search data (ISO8601 format: YYYY-MM-DD)
118
- */
119
- toDate: z.string().optional(),
120
-
121
- /**
122
- * maximum number of search results to consider
123
- * defaults to 20
124
- */
125
- maxSearchResults: z.number().min(1).max(50).optional(),
126
-
127
- /**
128
- * data sources to search from.
129
- * defaults to [{ type: 'web' }, { type: 'x' }] if not specified.
130
- *
131
- * @example
132
- * sources: [{ type: 'web', country: 'US' }, { type: 'x' }]
133
- */
134
- sources: z.array(searchSourceSchema).optional(),
135
- })
136
- .optional(),
137
- });
138
-
139
- export type XaiLanguageModelChatOptions = z.infer<
140
- typeof xaiLanguageModelChatOptions
141
- >;