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

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
@@ -840,6 +840,27 @@ const { images } = await generateImage({
840
840
  });
841
841
  ```
842
842
 
843
+ #### Multi-Image Editing
844
+
845
+ Combine or reference multiple input images (up to 3) in the prompt:
846
+
847
+ ```ts
848
+ import { xai } from '@ai-sdk/xai';
849
+ import { generateImage } from 'ai';
850
+ import { readFileSync } from 'fs';
851
+
852
+ const cat = readFileSync('./cat.png');
853
+ const dog = readFileSync('./dog.png');
854
+
855
+ const { images } = await generateImage({
856
+ model: xai.image('grok-imagine-image'),
857
+ prompt: {
858
+ text: 'Combine these two animals into a group photo',
859
+ images: [cat, dog],
860
+ },
861
+ });
862
+ ```
863
+
843
864
  #### Style Transfer
844
865
 
845
866
  Apply artistic styles to an image:
@@ -859,7 +880,7 @@ const { images } = await generateImage({
859
880
 
860
881
  <Note>
861
882
  Input images can be provided as `Buffer`, `ArrayBuffer`, `Uint8Array`, or
862
- base64-encoded strings. xAI only supports a single input image per request.
883
+ base64-encoded strings. Up to 3 input images are supported per request.
863
884
  </Note>
864
885
 
865
886
  ### Model-specific options
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/xai",
3
- "version": "4.0.0-beta.2",
3
+ "version": "4.0.0-beta.4",
4
4
  "license": "Apache-2.0",
5
5
  "sideEffects": false,
6
6
  "main": "./dist/index.js",
@@ -29,8 +29,8 @@
29
29
  }
30
30
  },
31
31
  "dependencies": {
32
- "@ai-sdk/openai-compatible": "3.0.0-beta.2",
33
32
  "@ai-sdk/provider": "4.0.0-beta.0",
33
+ "@ai-sdk/openai-compatible": "3.0.0-beta.2",
34
34
  "@ai-sdk/provider-utils": "5.0.0-beta.1"
35
35
  },
36
36
  "devDependencies": {
@@ -27,7 +27,7 @@ interface XaiImageModelConfig {
27
27
 
28
28
  export class XaiImageModel implements ImageModelV3 {
29
29
  readonly specificationVersion = 'v3';
30
- readonly maxImagesPerCall = 1;
30
+ readonly maxImagesPerCall = 3;
31
31
 
32
32
  get provider(): string {
33
33
  return this.config.provider;
@@ -84,19 +84,9 @@ export class XaiImageModel implements ImageModelV3 {
84
84
  });
85
85
 
86
86
  const hasFiles = files != null && files.length > 0;
87
- let imageUrl: string | undefined;
88
-
89
- if (hasFiles) {
90
- imageUrl = convertImageModelFileToDataUri(files[0]);
91
-
92
- if (files.length > 1) {
93
- warnings.push({
94
- type: 'other',
95
- message:
96
- 'xAI only supports a single input image. Additional images are ignored.',
97
- });
98
- }
99
- }
87
+ const imageUrls = hasFiles
88
+ ? files.map(file => convertImageModelFileToDataUri(file))
89
+ : [];
100
90
 
101
91
  const endpoint = hasFiles ? '/images/edits' : '/images/generations';
102
92
 
@@ -104,7 +94,7 @@ export class XaiImageModel implements ImageModelV3 {
104
94
  model: this.modelId,
105
95
  prompt,
106
96
  n,
107
- response_format: 'url',
97
+ response_format: 'b64_json',
108
98
  };
109
99
 
110
100
  if (aspectRatio != null) {
@@ -127,8 +117,18 @@ export class XaiImageModel implements ImageModelV3 {
127
117
  body.resolution = xaiOptions.resolution;
128
118
  }
129
119
 
130
- if (imageUrl != null) {
131
- body.image = { url: imageUrl, type: 'image_url' };
120
+ if (xaiOptions?.quality != null) {
121
+ body.quality = xaiOptions.quality;
122
+ }
123
+
124
+ if (xaiOptions?.user != null) {
125
+ body.user = xaiOptions.user;
126
+ }
127
+
128
+ if (imageUrls.length === 1) {
129
+ body.image = { url: imageUrls[0], type: 'image_url' };
130
+ } else if (imageUrls.length > 1) {
131
+ body.images = imageUrls.map(url => ({ url, type: 'image_url' }));
132
132
  }
133
133
 
134
134
  const baseURL = this.config.baseURL ?? 'https://api.x.ai/v1';
@@ -145,12 +145,18 @@ export class XaiImageModel implements ImageModelV3 {
145
145
  fetch: this.config.fetch,
146
146
  });
147
147
 
148
- const downloadedImages = await Promise.all(
149
- response.data.map(image => this.downloadImage(image.url, abortSignal)),
150
- );
148
+ const hasAllBase64 = response.data.every(image => image.b64_json != null);
149
+
150
+ const images = hasAllBase64
151
+ ? response.data.map(image => image.b64_json!)
152
+ : await Promise.all(
153
+ response.data.map(image =>
154
+ this.downloadImage(image.url!, abortSignal),
155
+ ),
156
+ );
151
157
 
152
158
  return {
153
- images: downloadedImages,
159
+ images,
154
160
  warnings,
155
161
  response: {
156
162
  timestamp: currentDate,
@@ -164,6 +170,9 @@ export class XaiImageModel implements ImageModelV3 {
164
170
  ? { revisedPrompt: item.revised_prompt }
165
171
  : {}),
166
172
  })),
173
+ ...(response.usage?.cost_in_usd_ticks != null
174
+ ? { costInUsdTicks: response.usage.cost_in_usd_ticks }
175
+ : {}),
167
176
  },
168
177
  },
169
178
  };
@@ -187,8 +196,14 @@ export class XaiImageModel implements ImageModelV3 {
187
196
  const xaiImageResponseSchema = z.object({
188
197
  data: z.array(
189
198
  z.object({
190
- url: z.string(),
199
+ url: z.string().nullish(),
200
+ b64_json: z.string().nullish(),
191
201
  revised_prompt: z.string().nullish(),
192
202
  }),
193
203
  ),
204
+ usage: z
205
+ .object({
206
+ cost_in_usd_ticks: z.number().nullish(),
207
+ })
208
+ .nullish(),
194
209
  });
@@ -5,6 +5,8 @@ export const xaiImageModelOptions = z.object({
5
5
  output_format: z.string().optional(),
6
6
  sync_mode: z.boolean().optional(),
7
7
  resolution: z.enum(['1k', '2k']).optional(),
8
+ quality: z.enum(['low', 'medium', 'high']).optional(),
9
+ user: z.string().optional(),
8
10
  });
9
11
 
10
12
  export type XaiImageModelOptions = z.infer<typeof xaiImageModelOptions>;