@tanstack/ai-grok 0.14.8 → 0.14.10

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.
Files changed (36) hide show
  1. package/dist/esm/adapters/image.js +228 -193
  2. package/dist/esm/adapters/image.js.map +1 -1
  3. package/dist/esm/adapters/summarize.js +49 -12
  4. package/dist/esm/adapters/summarize.js.map +1 -1
  5. package/dist/esm/adapters/text.js +82 -38
  6. package/dist/esm/adapters/text.js.map +1 -1
  7. package/dist/esm/adapters/transcription.js +133 -114
  8. package/dist/esm/adapters/transcription.js.map +1 -1
  9. package/dist/esm/adapters/tts.js +147 -130
  10. package/dist/esm/adapters/tts.js.map +1 -1
  11. package/dist/esm/adapters/video.js +275 -226
  12. package/dist/esm/adapters/video.js.map +1 -1
  13. package/dist/esm/image/image-provider-options.js +64 -66
  14. package/dist/esm/image/image-provider-options.js.map +1 -1
  15. package/dist/esm/index.js +3 -31
  16. package/dist/esm/model-meta.js +149 -47
  17. package/dist/esm/model-meta.js.map +1 -1
  18. package/dist/esm/realtime/adapter.js +729 -812
  19. package/dist/esm/realtime/adapter.js.map +1 -1
  20. package/dist/esm/realtime/index.js +3 -0
  21. package/dist/esm/realtime/token.js +78 -72
  22. package/dist/esm/realtime/token.js.map +1 -1
  23. package/dist/esm/tools/index.js +57 -96
  24. package/dist/esm/tools/index.js.map +1 -1
  25. package/dist/esm/utils/audio.js +116 -162
  26. package/dist/esm/utils/audio.js.map +1 -1
  27. package/dist/esm/utils/client.js +22 -16
  28. package/dist/esm/utils/client.js.map +1 -1
  29. package/dist/esm/utils/index.js +5 -0
  30. package/dist/esm/utils/schema-converter.js +2 -0
  31. package/dist/esm/video/video-provider-options.js +83 -57
  32. package/dist/esm/video/video-provider-options.js.map +1 -1
  33. package/package.json +8 -8
  34. package/src/realtime/adapter.ts +1 -1
  35. package/src/utils/audio.ts +1 -1
  36. package/dist/esm/index.js.map +0 -1
@@ -1,205 +1,240 @@
1
+ import { getGrokApiKeyFromEnv, withGrokDefaults } from "../utils/client.js";
2
+ import { isGrokImagineImageModel, parseGrokImagineSize, validateImageSize, validateNumberOfImages, validatePrompt } from "../image/image-provider-options.js";
1
3
  import OpenAI from "openai";
4
+ import { buildImagesUsage } from "@tanstack/openai-base";
5
+ import { generateId } from "@tanstack/ai-utils";
2
6
  import { resolveMediaPrompt } from "@tanstack/ai";
3
7
  import { BaseImageAdapter } from "@tanstack/ai/adapters";
4
8
  import { toRunErrorPayload } from "@tanstack/ai/adapter-internals";
5
- import { buildImagesUsage } from "@tanstack/openai-base";
6
- import { generateId } from "@tanstack/ai-utils";
7
- import { withGrokDefaults, getGrokApiKeyFromEnv } from "../utils/client.js";
8
- import { isGrokImagineImageModel, validatePrompt, validateImageSize, validateNumberOfImages, parseGrokImagineSize } from "../image/image-provider-options.js";
9
- const MAX_EDIT_IMAGES = 3;
9
+ //#region src/adapters/image.ts
10
+ /** Maximum source images accepted by xAI's image edit endpoint. */
11
+ var MAX_EDIT_IMAGES = 3;
12
+ /**
13
+ * Maps the generic `size` option onto Imagine API parameters: the
14
+ * "aspectRatio_resolution" template ("16:9_2k") splits into `aspect_ratio`
15
+ * and optional `resolution` request fields.
16
+ */
10
17
  function imagineSizeParams(size) {
11
- if (!size) return {};
12
- const parsed = parseGrokImagineSize(size);
13
- if (!parsed) return {};
14
- return {
15
- aspect_ratio: parsed.aspectRatio,
16
- ...parsed.resolution !== void 0 && { resolution: parsed.resolution }
17
- };
18
+ if (!size) return {};
19
+ const parsed = parseGrokImagineSize(size);
20
+ if (!parsed) return {};
21
+ return {
22
+ aspect_ratio: parsed.aspectRatio,
23
+ ...parsed.resolution !== void 0 && { resolution: parsed.resolution }
24
+ };
18
25
  }
26
+ /**
27
+ * Convert a TanStack ImagePart to the URL string accepted by xAI's edit
28
+ * endpoint: public URLs pass through (fetched by xAI's servers), data
29
+ * sources become base64 data URIs.
30
+ */
19
31
  function imagePartToUrl(part) {
20
- if (part.source.type === "url") return part.source.value;
21
- return `data:${part.source.mimeType};base64,${part.source.value}`;
22
- }
23
- class GrokImageAdapter extends BaseImageAdapter {
24
- kind = "image";
25
- name = "grok";
26
- client;
27
- clientConfig;
28
- constructor(config, model) {
29
- super(model, {});
30
- this.clientConfig = withGrokDefaults(config);
31
- this.client = new OpenAI(this.clientConfig);
32
- }
33
- async generateImages(options) {
34
- const { model, numberOfImages, size, modelOptions } = options;
35
- const resolved = resolveMediaPrompt(options.prompt);
36
- const prompt = resolved.text;
37
- if (resolved.videos.length > 0 || resolved.audios.length > 0) {
38
- throw new Error(
39
- `grok.generateImages does not support video / audio prompt parts on model ${model}.`
40
- );
41
- }
42
- if (resolved.images.length > 0) {
43
- if (!isGrokImagineImageModel(model)) {
44
- throw new Error(
45
- `grok: model "${model}" does not support image prompt parts. Image-conditioned generation requires an Imagine API model ('grok-imagine-image' or 'grok-imagine-image-quality').`
46
- );
47
- }
48
- return await this.editImages(options, resolved);
49
- }
50
- validatePrompt({ prompt });
51
- validateImageSize(model, size);
52
- validateNumberOfImages(model, numberOfImages);
53
- const isImagine = isGrokImagineImageModel(model);
54
- const request = {
55
- model,
56
- prompt,
57
- n: numberOfImages ?? 1,
58
- ...isImagine ? imagineSizeParams(size) : size !== void 0 && {
59
- size
60
- },
61
- stream: false,
62
- ...modelOptions
63
- };
64
- try {
65
- options.logger.request(
66
- `activity=image provider=${this.name} model=${model} n=${request.n ?? 1} size=${request.size ?? "default"}`,
67
- { provider: this.name, model }
68
- );
69
- const response = await this.client.images.generate(request);
70
- const images = (response.data ?? []).flatMap(
71
- (item) => {
72
- const revisedPrompt = item.revised_prompt;
73
- if (item.b64_json) {
74
- return [
75
- {
76
- b64Json: item.b64_json,
77
- ...revisedPrompt !== void 0 && { revisedPrompt }
78
- }
79
- ];
80
- }
81
- if (item.url) {
82
- return [
83
- {
84
- url: item.url,
85
- ...revisedPrompt !== void 0 && { revisedPrompt }
86
- }
87
- ];
88
- }
89
- return [];
90
- }
91
- );
92
- const usage = buildImagesUsage(response.usage);
93
- return {
94
- id: generateId(this.name),
95
- model,
96
- images,
97
- ...usage ? { usage } : {}
98
- };
99
- } catch (error) {
100
- options.logger.errors(`${this.name}.generateImages fatal`, {
101
- error: toRunErrorPayload(error, `${this.name}.generateImages failed`),
102
- source: `${this.name}.generateImages`
103
- });
104
- throw error;
105
- }
106
- }
107
- /**
108
- * Image-conditioned generation via xAI's Imagine API.
109
- *
110
- * The `/v1/images/edits` endpoint takes `application/json` (the OpenAI
111
- * SDK's `images.edit()` sends `multipart/form-data`, which xAI rejects),
112
- * so this path issues the request directly. One input is sent as
113
- * `image: { url }`; multiple inputs (up to 3) as `images: [{ url }, ...]`,
114
- * addressed by xAI in the order they are sent. The prompt text is sent
115
- * verbatim — no referencing markers are injected.
116
- */
117
- async editImages(options, resolved) {
118
- const { model, numberOfImages, size, modelOptions, logger } = options;
119
- const prompt = resolved.text;
120
- const imageInputs = resolved.images;
121
- const unsupportedRole = imageInputs.find(
122
- (part) => part.metadata?.role === "mask" || part.metadata?.role === "control"
123
- );
124
- if (unsupportedRole) {
125
- throw new Error(
126
- `grok: the Imagine API has no ${unsupportedRole.metadata?.role} input; only source/reference images are supported.`
127
- );
128
- }
129
- if (imageInputs.length > MAX_EDIT_IMAGES) {
130
- throw new Error(
131
- `grok: model "${model}" accepts at most ${MAX_EDIT_IMAGES} source images; received ${imageInputs.length}.`
132
- );
133
- }
134
- validatePrompt({ prompt });
135
- validateImageSize(model, size);
136
- validateNumberOfImages(model, numberOfImages);
137
- const urls = imageInputs.map((part) => imagePartToUrl(part));
138
- const request = {
139
- model,
140
- prompt,
141
- ...urls.length === 1 ? { image: { url: urls[0] } } : { images: urls.map((url) => ({ url })) },
142
- ...numberOfImages !== void 0 && { n: numberOfImages },
143
- ...imagineSizeParams(size),
144
- ...modelOptions
145
- };
146
- try {
147
- logger.request(
148
- `activity=image provider=${this.name} model=${model} edit images=${urls.length}`,
149
- { provider: this.name, model }
150
- );
151
- const response = await fetch(
152
- `${this.clientConfig.baseURL}/images/edits`,
153
- {
154
- method: "POST",
155
- headers: {
156
- "Content-Type": "application/json",
157
- Authorization: `Bearer ${this.clientConfig.apiKey}`
158
- },
159
- body: JSON.stringify(request)
160
- }
161
- );
162
- if (!response.ok) {
163
- const body = await response.text();
164
- throw new Error(
165
- `grok: image edit request failed (${response.status} ${response.statusText}): ${body}`
166
- );
167
- }
168
- const result = await response.json();
169
- const images = (result.data ?? []).flatMap(
170
- (item) => {
171
- if (item.b64_json) return [{ b64Json: item.b64_json }];
172
- if (item.url) return [{ url: item.url }];
173
- return [];
174
- }
175
- );
176
- if (images.length === 0) {
177
- throw new Error("grok: image edit response contained no images");
178
- }
179
- return {
180
- id: generateId(this.name),
181
- model,
182
- images
183
- };
184
- } catch (error) {
185
- logger.errors(`${this.name}.generateImages fatal`, {
186
- error: toRunErrorPayload(error, `${this.name}.generateImages failed`),
187
- source: `${this.name}.generateImages`
188
- });
189
- throw error;
190
- }
191
- }
32
+ if (part.source.type === "url") return part.source.value;
33
+ return `data:${part.source.mimeType};base64,${part.source.value}`;
192
34
  }
35
+ /**
36
+ * Grok Image Generation Adapter
37
+ *
38
+ * Tree-shakeable adapter for Grok image generation functionality.
39
+ * Supports the legacy grok-2-image-1212 model (text-to-image via the
40
+ * OpenAI-compat endpoint) and the grok-imagine image models, which also
41
+ * accept image prompt parts for image-conditioned generation via xAI's
42
+ * `/v1/images/edits` endpoint (up to 3 source images).
43
+ *
44
+ * Features:
45
+ * - Model-specific type-safe provider options
46
+ * - Size / aspect-ratio validation per model
47
+ * - Number of images validation
48
+ */
49
+ var GrokImageAdapter = class extends BaseImageAdapter {
50
+ kind = "image";
51
+ name = "grok";
52
+ client;
53
+ clientConfig;
54
+ constructor(config, model) {
55
+ super(model, {});
56
+ this.clientConfig = withGrokDefaults(config);
57
+ this.client = new OpenAI(this.clientConfig);
58
+ }
59
+ async generateImages(options) {
60
+ const { model, numberOfImages, size, modelOptions } = options;
61
+ const resolved = resolveMediaPrompt(options.prompt);
62
+ const prompt = resolved.text;
63
+ if (resolved.videos.length > 0 || resolved.audios.length > 0) throw new Error(`grok.generateImages does not support video / audio prompt parts on model ${model}.`);
64
+ if (resolved.images.length > 0) {
65
+ if (!isGrokImagineImageModel(model)) throw new Error(`grok: model "${model}" does not support image prompt parts. Image-conditioned generation requires an Imagine API model ('grok-imagine-image' or 'grok-imagine-image-quality').`);
66
+ return await this.editImages(options, resolved);
67
+ }
68
+ validatePrompt({
69
+ prompt,
70
+ model
71
+ });
72
+ validateImageSize(model, size);
73
+ validateNumberOfImages(model, numberOfImages);
74
+ const isImagine = isGrokImagineImageModel(model);
75
+ const request = {
76
+ model,
77
+ prompt,
78
+ n: numberOfImages ?? 1,
79
+ ...isImagine ? imagineSizeParams(size) : size !== void 0 && { size },
80
+ stream: false,
81
+ ...modelOptions
82
+ };
83
+ try {
84
+ options.logger.request(`activity=image provider=${this.name} model=${model} n=${request.n ?? 1} size=${request.size ?? "default"}`, {
85
+ provider: this.name,
86
+ model
87
+ });
88
+ const response = await this.client.images.generate(request);
89
+ const images = (response.data ?? []).flatMap((item) => {
90
+ const revisedPrompt = item.revised_prompt;
91
+ if (item.b64_json) return [{
92
+ b64Json: item.b64_json,
93
+ ...revisedPrompt !== void 0 && { revisedPrompt }
94
+ }];
95
+ if (item.url) return [{
96
+ url: item.url,
97
+ ...revisedPrompt !== void 0 && { revisedPrompt }
98
+ }];
99
+ return [];
100
+ });
101
+ const usage = buildImagesUsage(response.usage);
102
+ return {
103
+ id: generateId(this.name),
104
+ model,
105
+ images,
106
+ ...usage ? { usage } : {}
107
+ };
108
+ } catch (error) {
109
+ options.logger.errors(`${this.name}.generateImages fatal`, {
110
+ error: toRunErrorPayload(error, `${this.name}.generateImages failed`),
111
+ source: `${this.name}.generateImages`
112
+ });
113
+ throw error;
114
+ }
115
+ }
116
+ /**
117
+ * Image-conditioned generation via xAI's Imagine API.
118
+ *
119
+ * The `/v1/images/edits` endpoint takes `application/json` (the OpenAI
120
+ * SDK's `images.edit()` sends `multipart/form-data`, which xAI rejects),
121
+ * so this path issues the request directly. One input is sent as
122
+ * `image: { url }`; multiple inputs (up to 3) as `images: [{ url }, ...]`,
123
+ * addressed by xAI in the order they are sent. The prompt text is sent
124
+ * verbatim — no referencing markers are injected.
125
+ */
126
+ async editImages(options, resolved) {
127
+ const { model, numberOfImages, size, modelOptions, logger } = options;
128
+ const prompt = resolved.text;
129
+ const imageInputs = resolved.images;
130
+ const unsupportedRole = imageInputs.find((part) => part.metadata?.role === "mask" || part.metadata?.role === "control");
131
+ if (unsupportedRole) throw new Error(`grok: the Imagine API has no ${unsupportedRole.metadata?.role} input; only source/reference images are supported.`);
132
+ if (imageInputs.length > MAX_EDIT_IMAGES) throw new Error(`grok: model "${model}" accepts at most ${MAX_EDIT_IMAGES} source images; received ${imageInputs.length}.`);
133
+ validatePrompt({
134
+ prompt,
135
+ model
136
+ });
137
+ validateImageSize(model, size);
138
+ validateNumberOfImages(model, numberOfImages);
139
+ const urls = imageInputs.map((part) => imagePartToUrl(part));
140
+ const request = {
141
+ model,
142
+ prompt,
143
+ ...urls.length === 1 ? { image: { url: urls[0] } } : { images: urls.map((url) => ({ url })) },
144
+ ...numberOfImages !== void 0 && { n: numberOfImages },
145
+ ...imagineSizeParams(size),
146
+ ...modelOptions
147
+ };
148
+ try {
149
+ logger.request(`activity=image provider=${this.name} model=${model} edit images=${urls.length}`, {
150
+ provider: this.name,
151
+ model
152
+ });
153
+ const response = await fetch(`${this.clientConfig.baseURL}/images/edits`, {
154
+ method: "POST",
155
+ headers: {
156
+ "Content-Type": "application/json",
157
+ Authorization: `Bearer ${this.clientConfig.apiKey}`
158
+ },
159
+ body: JSON.stringify(request)
160
+ });
161
+ if (!response.ok) {
162
+ const body = await response.text();
163
+ throw new Error(`grok: image edit request failed (${response.status} ${response.statusText}): ${body}`);
164
+ }
165
+ const images = ((await response.json()).data ?? []).flatMap((item) => {
166
+ if (item.b64_json) return [{ b64Json: item.b64_json }];
167
+ if (item.url) return [{ url: item.url }];
168
+ return [];
169
+ });
170
+ if (images.length === 0) throw new Error("grok: image edit response contained no images");
171
+ return {
172
+ id: generateId(this.name),
173
+ model,
174
+ images
175
+ };
176
+ } catch (error) {
177
+ logger.errors(`${this.name}.generateImages fatal`, {
178
+ error: toRunErrorPayload(error, `${this.name}.generateImages failed`),
179
+ source: `${this.name}.generateImages`
180
+ });
181
+ throw error;
182
+ }
183
+ }
184
+ };
185
+ /**
186
+ * Creates a Grok image adapter with explicit API key.
187
+ * Type resolution happens here at the call site.
188
+ *
189
+ * @param model - The model name (e.g., 'grok-2-image-1212')
190
+ * @param apiKey - Your xAI API key
191
+ * @param config - Optional additional configuration
192
+ * @returns Configured Grok image adapter instance with resolved types
193
+ *
194
+ * @example
195
+ * ```typescript
196
+ * const adapter = createGrokImage('grok-2-image-1212', "xai-...");
197
+ *
198
+ * const result = await generateImage({
199
+ * adapter,
200
+ * prompt: 'A cute baby sea otter'
201
+ * });
202
+ * ```
203
+ */
193
204
  function createGrokImage(model, apiKey, config) {
194
- return new GrokImageAdapter({ apiKey, ...config }, model);
205
+ return new GrokImageAdapter({
206
+ apiKey,
207
+ ...config
208
+ }, model);
195
209
  }
210
+ /**
211
+ * Creates a Grok image adapter with automatic API key detection from environment variables.
212
+ * Type resolution happens here at the call site.
213
+ *
214
+ * Looks for `XAI_API_KEY` in:
215
+ * - `process.env` (Node.js)
216
+ * - `window.env` (Browser with injected env)
217
+ *
218
+ * @param model - The model name (e.g., 'grok-2-image-1212')
219
+ * @param config - Optional configuration (excluding apiKey which is auto-detected)
220
+ * @returns Configured Grok image adapter instance with resolved types
221
+ * @throws Error if XAI_API_KEY is not found in environment
222
+ *
223
+ * @example
224
+ * ```typescript
225
+ * // Automatically uses XAI_API_KEY from environment
226
+ * const adapter = grokImage('grok-2-image-1212');
227
+ *
228
+ * const result = await generateImage({
229
+ * adapter,
230
+ * prompt: 'A beautiful sunset over mountains'
231
+ * });
232
+ * ```
233
+ */
196
234
  function grokImage(model, config) {
197
- const apiKey = getGrokApiKeyFromEnv();
198
- return createGrokImage(model, apiKey, config);
235
+ return createGrokImage(model, getGrokApiKeyFromEnv(), config);
199
236
  }
200
- export {
201
- GrokImageAdapter,
202
- createGrokImage,
203
- grokImage
204
- };
205
- //# sourceMappingURL=image.js.map
237
+ //#endregion
238
+ export { GrokImageAdapter, createGrokImage, grokImage };
239
+
240
+ //# sourceMappingURL=image.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"image.js","sources":["../../../src/adapters/image.ts"],"sourcesContent":["import OpenAI from 'openai'\nimport { resolveMediaPrompt } from '@tanstack/ai'\nimport { BaseImageAdapter } from '@tanstack/ai/adapters'\nimport { toRunErrorPayload } from '@tanstack/ai/adapter-internals'\nimport { buildImagesUsage } from '@tanstack/openai-base'\nimport { generateId } from '@tanstack/ai-utils'\nimport { getGrokApiKeyFromEnv, withGrokDefaults } from '../utils/client'\nimport {\n isGrokImagineImageModel,\n parseGrokImagineSize,\n validateImageSize,\n validateNumberOfImages,\n validatePrompt,\n} from '../image/image-provider-options'\nimport type {\n GeneratedImage,\n ImageGenerationOptions,\n ImageGenerationResult,\n ImagePart,\n MediaInputMetadata,\n ResolvedMediaPrompt,\n} from '@tanstack/ai'\nimport type OpenAI_SDK from 'openai'\nimport type { GrokImageModel } from '../model-meta'\nimport type {\n GrokImageModelInputModalitiesByName,\n GrokImageModelProviderOptionsByName,\n GrokImageModelSizeByName,\n GrokImageProviderOptions,\n} from '../image/image-provider-options'\nimport type { GrokClientConfig } from '../utils/client'\n\n/**\n * Configuration for Grok image adapter\n */\nexport interface GrokImageConfig extends GrokClientConfig {}\n\n/** Maximum source images accepted by xAI's image edit endpoint. */\nconst MAX_EDIT_IMAGES = 3\n\n/**\n * Maps the generic `size` option onto Imagine API parameters: the\n * \"aspectRatio_resolution\" template (\"16:9_2k\") splits into `aspect_ratio`\n * and optional `resolution` request fields.\n */\nfunction imagineSizeParams(size: string | undefined): {\n aspect_ratio?: string\n resolution?: string\n} {\n if (!size) return {}\n const parsed = parseGrokImagineSize(size)\n if (!parsed) return {}\n return {\n aspect_ratio: parsed.aspectRatio,\n ...(parsed.resolution !== undefined && { resolution: parsed.resolution }),\n }\n}\n\n/**\n * Convert a TanStack ImagePart to the URL string accepted by xAI's edit\n * endpoint: public URLs pass through (fetched by xAI's servers), data\n * sources become base64 data URIs.\n */\nfunction imagePartToUrl(part: ImagePart<MediaInputMetadata>): string {\n if (part.source.type === 'url') return part.source.value\n return `data:${part.source.mimeType};base64,${part.source.value}`\n}\n\n/** Response shape of xAI's `/v1/images/edits` endpoint. */\ninterface GrokImageEditResponse {\n data?: Array<{\n url?: string | null\n b64_json?: string | null\n mime_type?: string\n }>\n}\n\n/**\n * Grok Image Generation Adapter\n *\n * Tree-shakeable adapter for Grok image generation functionality.\n * Supports the legacy grok-2-image-1212 model (text-to-image via the\n * OpenAI-compat endpoint) and the grok-imagine image models, which also\n * accept image prompt parts for image-conditioned generation via xAI's\n * `/v1/images/edits` endpoint (up to 3 source images).\n *\n * Features:\n * - Model-specific type-safe provider options\n * - Size / aspect-ratio validation per model\n * - Number of images validation\n */\nexport class GrokImageAdapter<\n TModel extends GrokImageModel,\n> extends BaseImageAdapter<\n TModel,\n GrokImageProviderOptions,\n GrokImageModelProviderOptionsByName,\n GrokImageModelSizeByName,\n GrokImageModelInputModalitiesByName\n> {\n override readonly kind = 'image' as const\n readonly name = 'grok' as const\n\n protected client: OpenAI\n private readonly clientConfig: GrokImageConfig\n\n constructor(config: GrokImageConfig, model: TModel) {\n super(model, {})\n this.clientConfig = withGrokDefaults(config)\n this.client = new OpenAI(this.clientConfig)\n }\n\n async generateImages(\n options: ImageGenerationOptions<GrokImageProviderOptions>,\n ): Promise<ImageGenerationResult> {\n const { model, numberOfImages, size, modelOptions } = options\n\n const resolved = resolveMediaPrompt(options.prompt)\n const prompt = resolved.text\n\n if (resolved.videos.length > 0 || resolved.audios.length > 0) {\n throw new Error(\n `grok.generateImages does not support video / audio prompt parts on model ${model}.`,\n )\n }\n\n if (resolved.images.length > 0) {\n if (!isGrokImagineImageModel(model)) {\n throw new Error(\n `grok: model \"${model}\" does not support image prompt parts. ` +\n `Image-conditioned generation requires an Imagine API model ` +\n `('grok-imagine-image' or 'grok-imagine-image-quality').`,\n )\n }\n return await this.editImages(options, resolved)\n }\n\n validatePrompt({ prompt, model })\n validateImageSize(model, size)\n validateNumberOfImages(model, numberOfImages)\n\n // grok-imagine models are aspect-ratio sized: the generic `size` option\n // carries an \"aspectRatio_resolution\" template (e.g. '16:9_2k', like\n // Gemini native image models) and maps to the Imagine API's\n // `aspect_ratio` / `resolution` parameters instead of OpenAI-style `size`.\n const isImagine = isGrokImagineImageModel(model)\n const request = {\n model,\n prompt,\n n: numberOfImages ?? 1,\n ...(isImagine\n ? imagineSizeParams(size)\n : size !== undefined && {\n size: size,\n }),\n stream: false,\n ...modelOptions,\n } as OpenAI_SDK.Images.ImageGenerateParamsNonStreaming\n\n try {\n options.logger.request(\n `activity=image provider=${this.name} model=${model} n=${request.n ?? 1} size=${request.size ?? 'default'}`,\n { provider: this.name, model },\n )\n const response = await this.client.images.generate(request)\n\n const images: Array<GeneratedImage> = (response.data ?? []).flatMap(\n (item): Array<GeneratedImage> => {\n const revisedPrompt = item.revised_prompt\n if (item.b64_json) {\n return [\n {\n b64Json: item.b64_json,\n ...(revisedPrompt !== undefined && { revisedPrompt }),\n },\n ]\n }\n if (item.url) {\n return [\n {\n url: item.url,\n ...(revisedPrompt !== undefined && { revisedPrompt }),\n },\n ]\n }\n return []\n },\n )\n\n const usage = buildImagesUsage(response.usage)\n\n return {\n id: generateId(this.name),\n model,\n images,\n ...(usage ? { usage } : {}),\n }\n } catch (error: unknown) {\n options.logger.errors(`${this.name}.generateImages fatal`, {\n error: toRunErrorPayload(error, `${this.name}.generateImages failed`),\n source: `${this.name}.generateImages`,\n })\n throw error\n }\n }\n\n /**\n * Image-conditioned generation via xAI's Imagine API.\n *\n * The `/v1/images/edits` endpoint takes `application/json` (the OpenAI\n * SDK's `images.edit()` sends `multipart/form-data`, which xAI rejects),\n * so this path issues the request directly. One input is sent as\n * `image: { url }`; multiple inputs (up to 3) as `images: [{ url }, ...]`,\n * addressed by xAI in the order they are sent. The prompt text is sent\n * verbatim — no referencing markers are injected.\n */\n private async editImages(\n options: ImageGenerationOptions<GrokImageProviderOptions>,\n resolved: ResolvedMediaPrompt,\n ): Promise<ImageGenerationResult> {\n const { model, numberOfImages, size, modelOptions, logger } = options\n const prompt = resolved.text\n const imageInputs = resolved.images\n\n const unsupportedRole = imageInputs.find(\n (part) =>\n part.metadata?.role === 'mask' || part.metadata?.role === 'control',\n )\n if (unsupportedRole) {\n throw new Error(\n `grok: the Imagine API has no ${unsupportedRole.metadata?.role} input; ` +\n `only source/reference images are supported.`,\n )\n }\n if (imageInputs.length > MAX_EDIT_IMAGES) {\n throw new Error(\n `grok: model \"${model}\" accepts at most ${MAX_EDIT_IMAGES} source images; received ${imageInputs.length}.`,\n )\n }\n\n validatePrompt({ prompt, model })\n validateImageSize(model, size)\n validateNumberOfImages(model, numberOfImages)\n\n const urls = imageInputs.map((part) => imagePartToUrl(part))\n const request: Record<string, unknown> = {\n model,\n prompt,\n ...(urls.length === 1\n ? { image: { url: urls[0] } }\n : { images: urls.map((url) => ({ url })) }),\n ...(numberOfImages !== undefined && { n: numberOfImages }),\n ...imagineSizeParams(size),\n ...modelOptions,\n }\n\n try {\n logger.request(\n `activity=image provider=${this.name} model=${model} edit images=${urls.length}`,\n { provider: this.name, model },\n )\n\n const response = await fetch(\n `${this.clientConfig.baseURL}/images/edits`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.clientConfig.apiKey}`,\n },\n body: JSON.stringify(request),\n },\n )\n if (!response.ok) {\n const body = await response.text()\n throw new Error(\n `grok: image edit request failed (${response.status} ${response.statusText}): ${body}`,\n )\n }\n\n const result = (await response.json()) as GrokImageEditResponse\n const images: Array<GeneratedImage> = (result.data ?? []).flatMap(\n (item): Array<GeneratedImage> => {\n if (item.b64_json) return [{ b64Json: item.b64_json }]\n if (item.url) return [{ url: item.url }]\n return []\n },\n )\n if (images.length === 0) {\n throw new Error('grok: image edit response contained no images')\n }\n\n return {\n id: generateId(this.name),\n model,\n images,\n }\n } catch (error: unknown) {\n logger.errors(`${this.name}.generateImages fatal`, {\n error: toRunErrorPayload(error, `${this.name}.generateImages failed`),\n source: `${this.name}.generateImages`,\n })\n throw error\n }\n }\n}\n\n/**\n * Creates a Grok image adapter with explicit API key.\n * Type resolution happens here at the call site.\n *\n * @param model - The model name (e.g., 'grok-2-image-1212')\n * @param apiKey - Your xAI API key\n * @param config - Optional additional configuration\n * @returns Configured Grok image adapter instance with resolved types\n *\n * @example\n * ```typescript\n * const adapter = createGrokImage('grok-2-image-1212', \"xai-...\");\n *\n * const result = await generateImage({\n * adapter,\n * prompt: 'A cute baby sea otter'\n * });\n * ```\n */\nexport function createGrokImage<TModel extends GrokImageModel>(\n model: TModel,\n apiKey: string,\n config?: Omit<GrokImageConfig, 'apiKey'>,\n): GrokImageAdapter<TModel> {\n return new GrokImageAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates a Grok image adapter with automatic API key detection from environment variables.\n * Type resolution happens here at the call site.\n *\n * Looks for `XAI_API_KEY` in:\n * - `process.env` (Node.js)\n * - `window.env` (Browser with injected env)\n *\n * @param model - The model name (e.g., 'grok-2-image-1212')\n * @param config - Optional configuration (excluding apiKey which is auto-detected)\n * @returns Configured Grok image adapter instance with resolved types\n * @throws Error if XAI_API_KEY is not found in environment\n *\n * @example\n * ```typescript\n * // Automatically uses XAI_API_KEY from environment\n * const adapter = grokImage('grok-2-image-1212');\n *\n * const result = await generateImage({\n * adapter,\n * prompt: 'A beautiful sunset over mountains'\n * });\n * ```\n */\nexport function grokImage<TModel extends GrokImageModel>(\n model: TModel,\n config?: Omit<GrokImageConfig, 'apiKey'>,\n): GrokImageAdapter<TModel> {\n const apiKey = getGrokApiKeyFromEnv()\n return createGrokImage(model, apiKey, config)\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"image.js","names":[],"sources":["../../../src/adapters/image.ts"],"sourcesContent":["import OpenAI from 'openai'\nimport { resolveMediaPrompt } from '@tanstack/ai'\nimport { BaseImageAdapter } from '@tanstack/ai/adapters'\nimport { toRunErrorPayload } from '@tanstack/ai/adapter-internals'\nimport { buildImagesUsage } from '@tanstack/openai-base'\nimport { generateId } from '@tanstack/ai-utils'\nimport { getGrokApiKeyFromEnv, withGrokDefaults } from '../utils/client'\nimport {\n isGrokImagineImageModel,\n parseGrokImagineSize,\n validateImageSize,\n validateNumberOfImages,\n validatePrompt,\n} from '../image/image-provider-options'\nimport type {\n GeneratedImage,\n ImageGenerationOptions,\n ImageGenerationResult,\n ImagePart,\n MediaInputMetadata,\n ResolvedMediaPrompt,\n} from '@tanstack/ai'\nimport type OpenAI_SDK from 'openai'\nimport type { GrokImageModel } from '../model-meta'\nimport type {\n GrokImageModelInputModalitiesByName,\n GrokImageModelProviderOptionsByName,\n GrokImageModelSizeByName,\n GrokImageProviderOptions,\n} from '../image/image-provider-options'\nimport type { GrokClientConfig } from '../utils/client'\n\n/**\n * Configuration for Grok image adapter\n */\nexport interface GrokImageConfig extends GrokClientConfig {}\n\n/** Maximum source images accepted by xAI's image edit endpoint. */\nconst MAX_EDIT_IMAGES = 3\n\n/**\n * Maps the generic `size` option onto Imagine API parameters: the\n * \"aspectRatio_resolution\" template (\"16:9_2k\") splits into `aspect_ratio`\n * and optional `resolution` request fields.\n */\nfunction imagineSizeParams(size: string | undefined): {\n aspect_ratio?: string\n resolution?: string\n} {\n if (!size) return {}\n const parsed = parseGrokImagineSize(size)\n if (!parsed) return {}\n return {\n aspect_ratio: parsed.aspectRatio,\n ...(parsed.resolution !== undefined && { resolution: parsed.resolution }),\n }\n}\n\n/**\n * Convert a TanStack ImagePart to the URL string accepted by xAI's edit\n * endpoint: public URLs pass through (fetched by xAI's servers), data\n * sources become base64 data URIs.\n */\nfunction imagePartToUrl(part: ImagePart<MediaInputMetadata>): string {\n if (part.source.type === 'url') return part.source.value\n return `data:${part.source.mimeType};base64,${part.source.value}`\n}\n\n/** Response shape of xAI's `/v1/images/edits` endpoint. */\ninterface GrokImageEditResponse {\n data?: Array<{\n url?: string | null\n b64_json?: string | null\n mime_type?: string\n }>\n}\n\n/**\n * Grok Image Generation Adapter\n *\n * Tree-shakeable adapter for Grok image generation functionality.\n * Supports the legacy grok-2-image-1212 model (text-to-image via the\n * OpenAI-compat endpoint) and the grok-imagine image models, which also\n * accept image prompt parts for image-conditioned generation via xAI's\n * `/v1/images/edits` endpoint (up to 3 source images).\n *\n * Features:\n * - Model-specific type-safe provider options\n * - Size / aspect-ratio validation per model\n * - Number of images validation\n */\nexport class GrokImageAdapter<\n TModel extends GrokImageModel,\n> extends BaseImageAdapter<\n TModel,\n GrokImageProviderOptions,\n GrokImageModelProviderOptionsByName,\n GrokImageModelSizeByName,\n GrokImageModelInputModalitiesByName\n> {\n override readonly kind = 'image' as const\n readonly name = 'grok' as const\n\n protected client: OpenAI\n private readonly clientConfig: GrokImageConfig\n\n constructor(config: GrokImageConfig, model: TModel) {\n super(model, {})\n this.clientConfig = withGrokDefaults(config)\n this.client = new OpenAI(this.clientConfig)\n }\n\n async generateImages(\n options: ImageGenerationOptions<GrokImageProviderOptions>,\n ): Promise<ImageGenerationResult> {\n const { model, numberOfImages, size, modelOptions } = options\n\n const resolved = resolveMediaPrompt(options.prompt)\n const prompt = resolved.text\n\n if (resolved.videos.length > 0 || resolved.audios.length > 0) {\n throw new Error(\n `grok.generateImages does not support video / audio prompt parts on model ${model}.`,\n )\n }\n\n if (resolved.images.length > 0) {\n if (!isGrokImagineImageModel(model)) {\n throw new Error(\n `grok: model \"${model}\" does not support image prompt parts. ` +\n `Image-conditioned generation requires an Imagine API model ` +\n `('grok-imagine-image' or 'grok-imagine-image-quality').`,\n )\n }\n return await this.editImages(options, resolved)\n }\n\n validatePrompt({ prompt, model })\n validateImageSize(model, size)\n validateNumberOfImages(model, numberOfImages)\n\n // grok-imagine models are aspect-ratio sized: the generic `size` option\n // carries an \"aspectRatio_resolution\" template (e.g. '16:9_2k', like\n // Gemini native image models) and maps to the Imagine API's\n // `aspect_ratio` / `resolution` parameters instead of OpenAI-style `size`.\n const isImagine = isGrokImagineImageModel(model)\n const request = {\n model,\n prompt,\n n: numberOfImages ?? 1,\n ...(isImagine\n ? imagineSizeParams(size)\n : size !== undefined && {\n size: size,\n }),\n stream: false,\n ...modelOptions,\n } as OpenAI_SDK.Images.ImageGenerateParamsNonStreaming\n\n try {\n options.logger.request(\n `activity=image provider=${this.name} model=${model} n=${request.n ?? 1} size=${request.size ?? 'default'}`,\n { provider: this.name, model },\n )\n const response = await this.client.images.generate(request)\n\n const images: Array<GeneratedImage> = (response.data ?? []).flatMap(\n (item): Array<GeneratedImage> => {\n const revisedPrompt = item.revised_prompt\n if (item.b64_json) {\n return [\n {\n b64Json: item.b64_json,\n ...(revisedPrompt !== undefined && { revisedPrompt }),\n },\n ]\n }\n if (item.url) {\n return [\n {\n url: item.url,\n ...(revisedPrompt !== undefined && { revisedPrompt }),\n },\n ]\n }\n return []\n },\n )\n\n const usage = buildImagesUsage(response.usage)\n\n return {\n id: generateId(this.name),\n model,\n images,\n ...(usage ? { usage } : {}),\n }\n } catch (error: unknown) {\n options.logger.errors(`${this.name}.generateImages fatal`, {\n error: toRunErrorPayload(error, `${this.name}.generateImages failed`),\n source: `${this.name}.generateImages`,\n })\n throw error\n }\n }\n\n /**\n * Image-conditioned generation via xAI's Imagine API.\n *\n * The `/v1/images/edits` endpoint takes `application/json` (the OpenAI\n * SDK's `images.edit()` sends `multipart/form-data`, which xAI rejects),\n * so this path issues the request directly. One input is sent as\n * `image: { url }`; multiple inputs (up to 3) as `images: [{ url }, ...]`,\n * addressed by xAI in the order they are sent. The prompt text is sent\n * verbatim — no referencing markers are injected.\n */\n private async editImages(\n options: ImageGenerationOptions<GrokImageProviderOptions>,\n resolved: ResolvedMediaPrompt,\n ): Promise<ImageGenerationResult> {\n const { model, numberOfImages, size, modelOptions, logger } = options\n const prompt = resolved.text\n const imageInputs = resolved.images\n\n const unsupportedRole = imageInputs.find(\n (part) =>\n part.metadata?.role === 'mask' || part.metadata?.role === 'control',\n )\n if (unsupportedRole) {\n throw new Error(\n `grok: the Imagine API has no ${unsupportedRole.metadata?.role} input; ` +\n `only source/reference images are supported.`,\n )\n }\n if (imageInputs.length > MAX_EDIT_IMAGES) {\n throw new Error(\n `grok: model \"${model}\" accepts at most ${MAX_EDIT_IMAGES} source images; received ${imageInputs.length}.`,\n )\n }\n\n validatePrompt({ prompt, model })\n validateImageSize(model, size)\n validateNumberOfImages(model, numberOfImages)\n\n const urls = imageInputs.map((part) => imagePartToUrl(part))\n const request: Record<string, unknown> = {\n model,\n prompt,\n ...(urls.length === 1\n ? { image: { url: urls[0] } }\n : { images: urls.map((url) => ({ url })) }),\n ...(numberOfImages !== undefined && { n: numberOfImages }),\n ...imagineSizeParams(size),\n ...modelOptions,\n }\n\n try {\n logger.request(\n `activity=image provider=${this.name} model=${model} edit images=${urls.length}`,\n { provider: this.name, model },\n )\n\n const response = await fetch(\n `${this.clientConfig.baseURL}/images/edits`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.clientConfig.apiKey}`,\n },\n body: JSON.stringify(request),\n },\n )\n if (!response.ok) {\n const body = await response.text()\n throw new Error(\n `grok: image edit request failed (${response.status} ${response.statusText}): ${body}`,\n )\n }\n\n const result = (await response.json()) as GrokImageEditResponse\n const images: Array<GeneratedImage> = (result.data ?? []).flatMap(\n (item): Array<GeneratedImage> => {\n if (item.b64_json) return [{ b64Json: item.b64_json }]\n if (item.url) return [{ url: item.url }]\n return []\n },\n )\n if (images.length === 0) {\n throw new Error('grok: image edit response contained no images')\n }\n\n return {\n id: generateId(this.name),\n model,\n images,\n }\n } catch (error: unknown) {\n logger.errors(`${this.name}.generateImages fatal`, {\n error: toRunErrorPayload(error, `${this.name}.generateImages failed`),\n source: `${this.name}.generateImages`,\n })\n throw error\n }\n }\n}\n\n/**\n * Creates a Grok image adapter with explicit API key.\n * Type resolution happens here at the call site.\n *\n * @param model - The model name (e.g., 'grok-2-image-1212')\n * @param apiKey - Your xAI API key\n * @param config - Optional additional configuration\n * @returns Configured Grok image adapter instance with resolved types\n *\n * @example\n * ```typescript\n * const adapter = createGrokImage('grok-2-image-1212', \"xai-...\");\n *\n * const result = await generateImage({\n * adapter,\n * prompt: 'A cute baby sea otter'\n * });\n * ```\n */\nexport function createGrokImage<TModel extends GrokImageModel>(\n model: TModel,\n apiKey: string,\n config?: Omit<GrokImageConfig, 'apiKey'>,\n): GrokImageAdapter<TModel> {\n return new GrokImageAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates a Grok image adapter with automatic API key detection from environment variables.\n * Type resolution happens here at the call site.\n *\n * Looks for `XAI_API_KEY` in:\n * - `process.env` (Node.js)\n * - `window.env` (Browser with injected env)\n *\n * @param model - The model name (e.g., 'grok-2-image-1212')\n * @param config - Optional configuration (excluding apiKey which is auto-detected)\n * @returns Configured Grok image adapter instance with resolved types\n * @throws Error if XAI_API_KEY is not found in environment\n *\n * @example\n * ```typescript\n * // Automatically uses XAI_API_KEY from environment\n * const adapter = grokImage('grok-2-image-1212');\n *\n * const result = await generateImage({\n * adapter,\n * prompt: 'A beautiful sunset over mountains'\n * });\n * ```\n */\nexport function grokImage<TModel extends GrokImageModel>(\n model: TModel,\n config?: Omit<GrokImageConfig, 'apiKey'>,\n): GrokImageAdapter<TModel> {\n const apiKey = getGrokApiKeyFromEnv()\n return createGrokImage(model, apiKey, config)\n}\n"],"mappings":"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"}
@@ -1,18 +1,55 @@
1
- import { ChatStreamSummarizeAdapter } from "@tanstack/ai/adapters";
2
1
  import { getGrokApiKeyFromEnv } from "../utils/client.js";
3
2
  import { GrokTextAdapter } from "./text.js";
3
+ import { ChatStreamSummarizeAdapter } from "@tanstack/ai/adapters";
4
+ //#region src/adapters/summarize.ts
5
+ /**
6
+ * Creates a Grok summarize adapter with explicit API key.
7
+ * Type resolution happens here at the call site.
8
+ *
9
+ * @param model - The model name (e.g., 'grok-build-0.1')
10
+ * @param apiKey - Your xAI API key
11
+ * @param config - Optional additional configuration
12
+ * @returns Configured Grok summarize adapter instance with resolved types
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * const adapter = createGrokSummarize('grok-build-0.1', "xai-...");
17
+ * ```
18
+ */
4
19
  function createGrokSummarize(model, apiKey, config) {
5
- return new ChatStreamSummarizeAdapter(
6
- new GrokTextAdapter({ apiKey, ...config }, model),
7
- model,
8
- "grok"
9
- );
20
+ return new ChatStreamSummarizeAdapter(new GrokTextAdapter({
21
+ apiKey,
22
+ ...config
23
+ }, model), model, "grok");
10
24
  }
25
+ /**
26
+ * Creates a Grok summarize adapter with automatic API key detection from environment variables.
27
+ * Type resolution happens here at the call site.
28
+ *
29
+ * Looks for `XAI_API_KEY` in:
30
+ * - `process.env` (Node.js)
31
+ * - `window.env` (Browser with injected env)
32
+ *
33
+ * @param model - The model name (e.g., 'grok-build-0.1')
34
+ * @param config - Optional configuration (excluding apiKey which is auto-detected)
35
+ * @returns Configured Grok summarize adapter instance with resolved types
36
+ * @throws Error if XAI_API_KEY is not found in environment
37
+ *
38
+ * @example
39
+ * ```typescript
40
+ * // Automatically uses XAI_API_KEY from environment
41
+ * const adapter = grokSummarize('grok-build-0.1');
42
+ *
43
+ * await summarize({
44
+ * adapter,
45
+ * text: "Long article text..."
46
+ * });
47
+ * ```
48
+ */
11
49
  function grokSummarize(model, config) {
12
- return createGrokSummarize(model, getGrokApiKeyFromEnv(), config);
50
+ return createGrokSummarize(model, getGrokApiKeyFromEnv(), config);
13
51
  }
14
- export {
15
- createGrokSummarize,
16
- grokSummarize
17
- };
18
- //# sourceMappingURL=summarize.js.map
52
+ //#endregion
53
+ export { createGrokSummarize, grokSummarize };
54
+
55
+ //# sourceMappingURL=summarize.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"summarize.js","sources":["../../../src/adapters/summarize.ts"],"sourcesContent":["import { ChatStreamSummarizeAdapter } from '@tanstack/ai/adapters'\nimport { getGrokApiKeyFromEnv } from '../utils/client'\nimport { GrokTextAdapter } from './text'\nimport type { InferTextProviderOptions } from '@tanstack/ai/adapters'\nimport type { GROK_CHAT_MODELS } from '../model-meta'\nimport type { GrokClientConfig } from '../utils/client'\n\n/**\n * Configuration for Grok summarize adapter\n */\nexport interface GrokSummarizeConfig extends GrokClientConfig {}\n\n/** Model type for Grok summarization */\nexport type GrokSummarizeModel = (typeof GROK_CHAT_MODELS)[number]\n\n/**\n * Creates a Grok summarize adapter with explicit API key.\n * Type resolution happens here at the call site.\n *\n * @param model - The model name (e.g., 'grok-build-0.1')\n * @param apiKey - Your xAI API key\n * @param config - Optional additional configuration\n * @returns Configured Grok summarize adapter instance with resolved types\n *\n * @example\n * ```typescript\n * const adapter = createGrokSummarize('grok-build-0.1', \"xai-...\");\n * ```\n */\nexport function createGrokSummarize<TModel extends GrokSummarizeModel>(\n model: TModel,\n apiKey: string,\n config?: Omit<GrokSummarizeConfig, 'apiKey'>,\n): ChatStreamSummarizeAdapter<\n TModel,\n InferTextProviderOptions<GrokTextAdapter<TModel>>\n> {\n return new ChatStreamSummarizeAdapter(\n new GrokTextAdapter({ apiKey, ...config }, model),\n model,\n 'grok',\n )\n}\n\n/**\n * Creates a Grok summarize adapter with automatic API key detection from environment variables.\n * Type resolution happens here at the call site.\n *\n * Looks for `XAI_API_KEY` in:\n * - `process.env` (Node.js)\n * - `window.env` (Browser with injected env)\n *\n * @param model - The model name (e.g., 'grok-build-0.1')\n * @param config - Optional configuration (excluding apiKey which is auto-detected)\n * @returns Configured Grok summarize adapter instance with resolved types\n * @throws Error if XAI_API_KEY is not found in environment\n *\n * @example\n * ```typescript\n * // Automatically uses XAI_API_KEY from environment\n * const adapter = grokSummarize('grok-build-0.1');\n *\n * await summarize({\n * adapter,\n * text: \"Long article text...\"\n * });\n * ```\n */\nexport function grokSummarize<TModel extends GrokSummarizeModel>(\n model: TModel,\n config?: Omit<GrokSummarizeConfig, 'apiKey'>,\n): ChatStreamSummarizeAdapter<\n TModel,\n InferTextProviderOptions<GrokTextAdapter<TModel>>\n> {\n return createGrokSummarize(model, getGrokApiKeyFromEnv(), config)\n}\n"],"names":[],"mappings":";;;AA6BO,SAAS,oBACd,OACA,QACA,QAIA;AACA,SAAO,IAAI;AAAA,IACT,IAAI,gBAAgB,EAAE,QAAQ,GAAG,OAAA,GAAU,KAAK;AAAA,IAChD;AAAA,IACA;AAAA,EAAA;AAEJ;AA0BO,SAAS,cACd,OACA,QAIA;AACA,SAAO,oBAAoB,OAAO,qBAAA,GAAwB,MAAM;AAClE;"}
1
+ {"version":3,"file":"summarize.js","names":[],"sources":["../../../src/adapters/summarize.ts"],"sourcesContent":["import { ChatStreamSummarizeAdapter } from '@tanstack/ai/adapters'\nimport { getGrokApiKeyFromEnv } from '../utils/client'\nimport { GrokTextAdapter } from './text'\nimport type { InferTextProviderOptions } from '@tanstack/ai/adapters'\nimport type { GROK_CHAT_MODELS } from '../model-meta'\nimport type { GrokClientConfig } from '../utils/client'\n\n/**\n * Configuration for Grok summarize adapter\n */\nexport interface GrokSummarizeConfig extends GrokClientConfig {}\n\n/** Model type for Grok summarization */\nexport type GrokSummarizeModel = (typeof GROK_CHAT_MODELS)[number]\n\n/**\n * Creates a Grok summarize adapter with explicit API key.\n * Type resolution happens here at the call site.\n *\n * @param model - The model name (e.g., 'grok-build-0.1')\n * @param apiKey - Your xAI API key\n * @param config - Optional additional configuration\n * @returns Configured Grok summarize adapter instance with resolved types\n *\n * @example\n * ```typescript\n * const adapter = createGrokSummarize('grok-build-0.1', \"xai-...\");\n * ```\n */\nexport function createGrokSummarize<TModel extends GrokSummarizeModel>(\n model: TModel,\n apiKey: string,\n config?: Omit<GrokSummarizeConfig, 'apiKey'>,\n): ChatStreamSummarizeAdapter<\n TModel,\n InferTextProviderOptions<GrokTextAdapter<TModel>>\n> {\n return new ChatStreamSummarizeAdapter(\n new GrokTextAdapter({ apiKey, ...config }, model),\n model,\n 'grok',\n )\n}\n\n/**\n * Creates a Grok summarize adapter with automatic API key detection from environment variables.\n * Type resolution happens here at the call site.\n *\n * Looks for `XAI_API_KEY` in:\n * - `process.env` (Node.js)\n * - `window.env` (Browser with injected env)\n *\n * @param model - The model name (e.g., 'grok-build-0.1')\n * @param config - Optional configuration (excluding apiKey which is auto-detected)\n * @returns Configured Grok summarize adapter instance with resolved types\n * @throws Error if XAI_API_KEY is not found in environment\n *\n * @example\n * ```typescript\n * // Automatically uses XAI_API_KEY from environment\n * const adapter = grokSummarize('grok-build-0.1');\n *\n * await summarize({\n * adapter,\n * text: \"Long article text...\"\n * });\n * ```\n */\nexport function grokSummarize<TModel extends GrokSummarizeModel>(\n model: TModel,\n config?: Omit<GrokSummarizeConfig, 'apiKey'>,\n): ChatStreamSummarizeAdapter<\n TModel,\n InferTextProviderOptions<GrokTextAdapter<TModel>>\n> {\n return createGrokSummarize(model, getGrokApiKeyFromEnv(), config)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AA6BA,SAAgB,oBACd,OACA,QACA,QAIA;CACA,OAAO,IAAI,2BACT,IAAI,gBAAgB;EAAE;EAAQ,GAAG;CAAO,GAAG,KAAK,GAChD,OACA,MACF;AACF;;;;;;;;;;;;;;;;;;;;;;;;;AA0BA,SAAgB,cACd,OACA,QAIA;CACA,OAAO,oBAAoB,OAAO,qBAAqB,GAAG,MAAM;AAClE"}