@ai-sdk/xai 4.0.0-beta.4 → 4.0.0-beta.40

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,10 +1,10 @@
1
1
  {
2
2
  "name": "@ai-sdk/xai",
3
- "version": "4.0.0-beta.4",
3
+ "version": "4.0.0-beta.40",
4
+ "type": "module",
4
5
  "license": "Apache-2.0",
5
6
  "sideEffects": false,
6
7
  "main": "./dist/index.js",
7
- "module": "./dist/index.mjs",
8
8
  "types": "./dist/index.d.ts",
9
9
  "files": [
10
10
  "dist/**/*",
@@ -24,21 +24,21 @@
24
24
  "./package.json": "./package.json",
25
25
  ".": {
26
26
  "types": "./dist/index.d.ts",
27
- "import": "./dist/index.mjs",
28
- "require": "./dist/index.js"
27
+ "import": "./dist/index.js",
28
+ "default": "./dist/index.js"
29
29
  }
30
30
  },
31
31
  "dependencies": {
32
- "@ai-sdk/provider": "4.0.0-beta.0",
33
- "@ai-sdk/openai-compatible": "3.0.0-beta.2",
34
- "@ai-sdk/provider-utils": "5.0.0-beta.1"
32
+ "@ai-sdk/openai-compatible": "3.0.0-beta.28",
33
+ "@ai-sdk/provider": "4.0.0-beta.12",
34
+ "@ai-sdk/provider-utils": "5.0.0-beta.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": "2.0.0-beta.0",
41
+ "@ai-sdk/test-server": "2.0.0-beta.1",
42
42
  "@vercel/ai-tsconfig": "0.0.0"
43
43
  },
44
44
  "peerDependencies": {
@@ -65,9 +65,7 @@
65
65
  "build": "pnpm clean && tsup --tsconfig tsconfig.build.json",
66
66
  "build:watch": "pnpm clean && tsup --watch",
67
67
  "clean": "del-cli dist docs *.tsbuildinfo",
68
- "lint": "eslint \"./**/*.ts*\"",
69
68
  "type-check": "tsc --build",
70
- "prettier-check": "prettier --check \"./**/*.ts*\"",
71
69
  "test": "pnpm test:node && pnpm test:edge",
72
70
  "test:update": "pnpm test:node -u",
73
71
  "test:watch": "vitest --config vitest.node.config.js",
@@ -1,17 +1,21 @@
1
1
  import {
2
- SharedV3Warning,
3
- LanguageModelV3Prompt,
2
+ SharedV4Warning,
3
+ LanguageModelV4Prompt,
4
4
  UnsupportedFunctionalityError,
5
5
  } from '@ai-sdk/provider';
6
- import { convertToBase64 } from '@ai-sdk/provider-utils';
6
+ import {
7
+ convertToBase64,
8
+ isProviderReference,
9
+ resolveProviderReference,
10
+ } from '@ai-sdk/provider-utils';
7
11
  import { XaiChatPrompt } from './xai-chat-prompt';
8
12
 
9
- export function convertToXaiChatMessages(prompt: LanguageModelV3Prompt): {
13
+ export function convertToXaiChatMessages(prompt: LanguageModelV4Prompt): {
10
14
  messages: XaiChatPrompt;
11
- warnings: Array<SharedV3Warning>;
15
+ warnings: Array<SharedV4Warning>;
12
16
  } {
13
17
  const messages: XaiChatPrompt = [];
14
- const warnings: Array<SharedV3Warning> = [];
18
+ const warnings: Array<SharedV4Warning> = [];
15
19
 
16
20
  for (const { role, content } of prompt) {
17
21
  switch (role) {
@@ -34,6 +38,18 @@ export function convertToXaiChatMessages(prompt: LanguageModelV3Prompt): {
34
38
  return { type: 'text', text: part.text };
35
39
  }
36
40
  case 'file': {
41
+ if (isProviderReference(part.data)) {
42
+ return {
43
+ type: 'file',
44
+ file: {
45
+ file_id: resolveProviderReference({
46
+ reference: part.data,
47
+ provider: 'xai',
48
+ }),
49
+ },
50
+ };
51
+ }
52
+
37
53
  if (part.mediaType.startsWith('image/')) {
38
54
  const mediaType =
39
55
  part.mediaType === 'image/*'
@@ -1,7 +1,7 @@
1
- import { LanguageModelV3Usage } from '@ai-sdk/provider';
1
+ import { LanguageModelV4Usage } from '@ai-sdk/provider';
2
2
  import { XaiChatUsage } from './xai-chat-language-model';
3
3
 
4
- export function convertXaiChatUsage(usage: XaiChatUsage): LanguageModelV3Usage {
4
+ export function convertXaiChatUsage(usage: XaiChatUsage): LanguageModelV4Usage {
5
5
  const cacheReadTokens = usage.prompt_tokens_details?.cached_tokens ?? 0;
6
6
  const reasoningTokens =
7
7
  usage.completion_tokens_details?.reasoning_tokens ?? 0;
@@ -0,0 +1,16 @@
1
+ import { lazySchema, zodSchema } from '@ai-sdk/provider-utils';
2
+ import { z } from 'zod/v4';
3
+
4
+ export const xaiFilesResponseSchema = lazySchema(() =>
5
+ zodSchema(
6
+ z.object({
7
+ id: z.string(),
8
+ object: z.string().nullish(),
9
+ bytes: z.number().nullish(),
10
+ created_at: z.number().nullish(),
11
+ filename: z.string().nullish(),
12
+ purpose: z.string().nullish(),
13
+ status: z.string().nullish(),
14
+ }),
15
+ ),
16
+ );
@@ -0,0 +1,15 @@
1
+ import { InferSchema, lazySchema, zodSchema } from '@ai-sdk/provider-utils';
2
+ import { z } from 'zod/v4';
3
+
4
+ export const xaiFilesOptionsSchema = lazySchema(() =>
5
+ zodSchema(
6
+ z
7
+ .object({
8
+ teamId: z.string().optional(),
9
+ filePath: z.string().optional(),
10
+ })
11
+ .passthrough(),
12
+ ),
13
+ );
14
+
15
+ export type XaiFilesOptions = InferSchema<typeof xaiFilesOptionsSchema>;
@@ -0,0 +1,93 @@
1
+ import {
2
+ FilesV4,
3
+ FilesV4UploadFileCallOptions,
4
+ FilesV4UploadFileResult,
5
+ } from '@ai-sdk/provider';
6
+ import {
7
+ combineHeaders,
8
+ convertBase64ToUint8Array,
9
+ createJsonResponseHandler,
10
+ FetchFunction,
11
+ parseProviderOptions,
12
+ postFormDataToApi,
13
+ } from '@ai-sdk/provider-utils';
14
+ import { xaiFailedResponseHandler } from '../xai-error';
15
+ import { xaiFilesResponseSchema } from './xai-files-api';
16
+ import { xaiFilesOptionsSchema, XaiFilesOptions } from './xai-files-options';
17
+
18
+ interface XaiFilesConfig {
19
+ provider: string;
20
+ baseURL: string | undefined;
21
+ headers: () => Record<string, string | undefined>;
22
+ fetch?: FetchFunction;
23
+ }
24
+
25
+ export class XaiFiles implements FilesV4 {
26
+ readonly specificationVersion = 'v4';
27
+
28
+ get provider(): string {
29
+ return this.config.provider;
30
+ }
31
+
32
+ constructor(private readonly config: XaiFilesConfig) {}
33
+
34
+ async uploadFile({
35
+ data,
36
+ mediaType,
37
+ filename,
38
+ providerOptions,
39
+ }: FilesV4UploadFileCallOptions): Promise<FilesV4UploadFileResult> {
40
+ const xaiOptions = (await parseProviderOptions({
41
+ provider: 'xai',
42
+ providerOptions,
43
+ schema: xaiFilesOptionsSchema,
44
+ })) as XaiFilesOptions | undefined;
45
+
46
+ const fileBytes =
47
+ data instanceof Uint8Array ? data : convertBase64ToUint8Array(data);
48
+
49
+ const blob = new Blob([fileBytes], {
50
+ type: mediaType,
51
+ });
52
+
53
+ const formData = new FormData();
54
+ if (filename != null) {
55
+ formData.append('file', blob, filename);
56
+ } else {
57
+ formData.append('file', blob);
58
+ }
59
+
60
+ if (xaiOptions?.teamId != null) {
61
+ formData.append('team_id', xaiOptions.teamId);
62
+ }
63
+
64
+ const { value: response } = await postFormDataToApi({
65
+ url: `${this.config.baseURL}/files`,
66
+ headers: combineHeaders(this.config.headers()),
67
+ formData,
68
+ failedResponseHandler: xaiFailedResponseHandler,
69
+ successfulResponseHandler: createJsonResponseHandler(
70
+ xaiFilesResponseSchema,
71
+ ),
72
+ fetch: this.config.fetch,
73
+ });
74
+
75
+ return {
76
+ warnings: [],
77
+ providerReference: { xai: response.id },
78
+ ...((response.filename ?? filename)
79
+ ? { filename: response.filename ?? filename }
80
+ : {}),
81
+ ...(mediaType != null ? { mediaType } : {}),
82
+ providerMetadata: {
83
+ xai: {
84
+ ...(response.filename != null ? { filename: response.filename } : {}),
85
+ ...(response.bytes != null ? { bytes: response.bytes } : {}),
86
+ ...(response.created_at != null
87
+ ? { createdAt: response.created_at }
88
+ : {}),
89
+ },
90
+ },
91
+ };
92
+ }
93
+ }
package/src/index.ts CHANGED
@@ -20,6 +20,7 @@ export type {
20
20
  /** @deprecated Use `XaiVideoModelOptions` instead. */
21
21
  XaiVideoModelOptions as XaiVideoProviderOptions,
22
22
  } from './xai-video-options';
23
+ export type { XaiFilesOptions } from './files/xai-files-options';
23
24
  export { createXai, xai } from './xai-provider';
24
25
  export type { XaiProvider, XaiProviderSettings } from './xai-provider';
25
26
  export {
@@ -1,8 +1,8 @@
1
- import { LanguageModelV3FinishReason } from '@ai-sdk/provider';
1
+ import { LanguageModelV4FinishReason } from '@ai-sdk/provider';
2
2
 
3
3
  export function mapXaiFinishReason(
4
4
  finishReason: string | null | undefined,
5
- ): LanguageModelV3FinishReason['unified'] {
5
+ ): LanguageModelV4FinishReason['unified'] {
6
6
  switch (finishReason) {
7
7
  case 'stop':
8
8
  return 'stop';
@@ -1,9 +1,13 @@
1
1
  import {
2
- SharedV3Warning,
3
- LanguageModelV3Message,
2
+ SharedV4Warning,
3
+ LanguageModelV4Message,
4
4
  UnsupportedFunctionalityError,
5
5
  } from '@ai-sdk/provider';
6
- import { convertToBase64 } from '@ai-sdk/provider-utils';
6
+ import {
7
+ convertToBase64,
8
+ isProviderReference,
9
+ resolveProviderReference,
10
+ } from '@ai-sdk/provider-utils';
7
11
  import {
8
12
  XaiResponsesInput,
9
13
  XaiResponsesUserMessageContentPart,
@@ -12,14 +16,14 @@ import {
12
16
  export async function convertToXaiResponsesInput({
13
17
  prompt,
14
18
  }: {
15
- prompt: LanguageModelV3Message[];
19
+ prompt: LanguageModelV4Message[];
16
20
  store?: boolean;
17
21
  }): Promise<{
18
22
  input: XaiResponsesInput;
19
- inputWarnings: SharedV3Warning[];
23
+ inputWarnings: SharedV4Warning[];
20
24
  }> {
21
25
  const input: XaiResponsesInput = [];
22
- const inputWarnings: SharedV3Warning[] = [];
26
+ const inputWarnings: SharedV4Warning[] = [];
23
27
 
24
28
  for (const message of prompt) {
25
29
  switch (message.role) {
@@ -42,7 +46,15 @@ export async function convertToXaiResponsesInput({
42
46
  }
43
47
 
44
48
  case 'file': {
45
- if (block.mediaType.startsWith('image/')) {
49
+ if (isProviderReference(block.data)) {
50
+ contentParts.push({
51
+ type: 'input_file',
52
+ file_id: resolveProviderReference({
53
+ reference: block.data,
54
+ provider: 'xai',
55
+ }),
56
+ });
57
+ } else if (block.mediaType.startsWith('image/')) {
46
58
  const mediaType =
47
59
  block.mediaType === 'image/*'
48
60
  ? 'image/jpeg'
@@ -123,7 +135,50 @@ export async function convertToXaiResponsesInput({
123
135
  break;
124
136
  }
125
137
 
126
- case 'reasoning':
138
+ case 'reasoning': {
139
+ const itemId =
140
+ typeof part.providerOptions?.xai?.itemId === 'string'
141
+ ? part.providerOptions.xai.itemId
142
+ : undefined;
143
+ const encryptedContent =
144
+ typeof part.providerOptions?.xai?.reasoningEncryptedContent ===
145
+ 'string'
146
+ ? part.providerOptions.xai.reasoningEncryptedContent
147
+ : undefined;
148
+
149
+ if (itemId != null || encryptedContent != null) {
150
+ const summaryParts: Array<{
151
+ type: 'summary_text';
152
+ text: string;
153
+ }> = [];
154
+ if (part.text.length > 0) {
155
+ summaryParts.push({
156
+ type: 'summary_text',
157
+ text: part.text,
158
+ });
159
+ }
160
+
161
+ input.push({
162
+ type: 'reasoning',
163
+ id: itemId ?? '',
164
+ summary: summaryParts,
165
+ status: 'completed',
166
+ ...(encryptedContent != null && {
167
+ encrypted_content: encryptedContent,
168
+ }),
169
+ });
170
+ } else {
171
+ inputWarnings.push({
172
+ type: 'other',
173
+ message:
174
+ 'Reasoning parts without itemId or encrypted content cannot be sent back to xAI. Skipping.',
175
+ });
176
+ }
177
+ break;
178
+ }
179
+
180
+ case 'reasoning-file':
181
+ case 'custom':
127
182
  case 'file': {
128
183
  inputWarnings.push({
129
184
  type: 'other',
@@ -1,9 +1,9 @@
1
- import { LanguageModelV3Usage } from '@ai-sdk/provider';
1
+ import { LanguageModelV4Usage } from '@ai-sdk/provider';
2
2
  import { XaiResponsesUsage } from './xai-responses-api';
3
3
 
4
4
  export function convertXaiResponsesUsage(
5
5
  usage: XaiResponsesUsage,
6
- ): LanguageModelV3Usage {
6
+ ): LanguageModelV4Usage {
7
7
  const cacheReadTokens = usage.input_tokens_details?.cached_tokens ?? 0;
8
8
  const reasoningTokens = usage.output_tokens_details?.reasoning_tokens ?? 0;
9
9
 
@@ -1,13 +1,14 @@
1
- import { LanguageModelV3FinishReason } from '@ai-sdk/provider';
1
+ import { LanguageModelV4FinishReason } from '@ai-sdk/provider';
2
2
 
3
3
  export function mapXaiResponsesFinishReason(
4
4
  finishReason: string | null | undefined,
5
- ): LanguageModelV3FinishReason['unified'] {
5
+ ): LanguageModelV4FinishReason['unified'] {
6
6
  switch (finishReason) {
7
7
  case 'stop':
8
8
  case 'completed':
9
9
  return 'stop';
10
10
  case 'length':
11
+ case 'max_output_tokens':
11
12
  return 'length';
12
13
  case 'tool_calls':
13
14
  case 'function_call':
@@ -26,7 +26,8 @@ export type XaiResponsesSystemMessage = {
26
26
 
27
27
  export type XaiResponsesUserMessageContentPart =
28
28
  | { type: 'input_text'; text: string }
29
- | { type: 'input_image'; image_url: string };
29
+ | { type: 'input_image'; image_url: string }
30
+ | { type: 'input_file'; file_id: string };
30
31
 
31
32
  export type XaiResponsesUserMessage = {
32
33
  role: 'user';
@@ -223,6 +224,9 @@ const outputItemSchema = z.discriminatedUnion('type', [
223
224
  type: z.literal('reasoning'),
224
225
  id: z.string(),
225
226
  summary: z.array(reasoningSummaryPartSchema),
227
+ content: z
228
+ .array(z.object({ type: z.string(), text: z.string() }))
229
+ .nullish(),
226
230
  status: z.string(),
227
231
  encrypted_content: z.string().nullish(),
228
232
  }),
@@ -518,6 +522,32 @@ export const xaiResponsesChunkSchema = z.union([
518
522
  output_index: z.number(),
519
523
  output: z.string().optional(),
520
524
  }),
525
+ z.object({
526
+ type: z.literal('response.incomplete'),
527
+ response: z.object({
528
+ incomplete_details: z.object({ reason: z.string() }).nullish(),
529
+ usage: xaiResponsesUsageSchema.nullish(),
530
+ }),
531
+ }),
532
+ z.object({
533
+ type: z.literal('response.failed'),
534
+ response: z.object({
535
+ error: z
536
+ .object({
537
+ code: z.string().nullish(),
538
+ message: z.string(),
539
+ })
540
+ .nullish(),
541
+ incomplete_details: z.object({ reason: z.string() }).nullish(),
542
+ usage: xaiResponsesUsageSchema.nullish(),
543
+ }),
544
+ }),
545
+ z.object({
546
+ type: z.literal('error'),
547
+ code: z.string().nullish(),
548
+ message: z.string(),
549
+ param: z.string().nullish(),
550
+ }),
521
551
  z.object({
522
552
  type: z.literal('response.done'),
523
553
  response: xaiResponsesResponseSchema,