@tanstack/ai-grok 0.6.5 → 0.6.7

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.
@@ -1,8 +1,10 @@
1
1
  import { BaseTextAdapter, StructuredOutputOptions, StructuredOutputResult } from '@tanstack/ai/adapters';
2
- import { GROK_CHAT_MODELS, ResolveInputModalities, ResolveProviderOptions } from '../model-meta.js';
3
- import { StreamChunk, TextOptions } from '@tanstack/ai';
2
+ import { GROK_CHAT_MODELS, GrokChatModelToolCapabilitiesByName, ResolveInputModalities, ResolveProviderOptions } from '../model-meta.js';
3
+ import { Modality, StreamChunk, TextOptions } from '@tanstack/ai';
4
+ import { ExternalTextProviderOptions as GrokTextProviderOptions } from '../text/text-provider-options.js';
4
5
  import { GrokMessageMetadataByModality } from '../message-types.js';
5
6
  import { GrokClientConfig } from '../utils.js';
7
+ type ResolveToolCapabilities<TModel extends string> = TModel extends keyof GrokChatModelToolCapabilitiesByName ? NonNullable<GrokChatModelToolCapabilitiesByName[TModel]> : readonly [];
6
8
  /**
7
9
  * Configuration for Grok text adapter
8
10
  */
@@ -18,12 +20,12 @@ export type { ExternalTextProviderOptions as GrokTextProviderOptions } from '../
18
20
  * Tree-shakeable adapter for Grok chat/text completion functionality.
19
21
  * Uses OpenAI-compatible Chat Completions API (not Responses API).
20
22
  */
21
- export declare class GrokTextAdapter<TModel extends (typeof GROK_CHAT_MODELS)[number]> extends BaseTextAdapter<TModel, ResolveProviderOptions<TModel>, ResolveInputModalities<TModel>, GrokMessageMetadataByModality> {
23
+ export declare class GrokTextAdapter<TModel extends (typeof GROK_CHAT_MODELS)[number], TProviderOptions extends Record<string, any> = ResolveProviderOptions<TModel>, TInputModalities extends ReadonlyArray<Modality> = ResolveInputModalities<TModel>, TToolCapabilities extends ReadonlyArray<string> = ResolveToolCapabilities<TModel>> extends BaseTextAdapter<TModel, TProviderOptions, TInputModalities, GrokMessageMetadataByModality, TToolCapabilities> {
22
24
  readonly kind: "text";
23
25
  readonly name: "grok";
24
26
  private client;
25
27
  constructor(config: GrokTextConfig, model: TModel);
26
- chatStream(options: TextOptions<ResolveProviderOptions<TModel>>): AsyncIterable<StreamChunk>;
28
+ chatStream(options: TextOptions<GrokTextProviderOptions>): AsyncIterable<StreamChunk>;
27
29
  /**
28
30
  * Generate structured output using Grok's JSON Schema response format.
29
31
  * Uses stream: false to get the complete response in one call.
@@ -36,7 +38,7 @@ export declare class GrokTextAdapter<TModel extends (typeof GROK_CHAT_MODELS)[nu
36
38
  * The outputSchema is already JSON Schema (converted in the ai layer).
37
39
  * We apply Grok-specific transformations for structured output compatibility.
38
40
  */
39
- structuredOutput(options: StructuredOutputOptions<ResolveProviderOptions<TModel>>): Promise<StructuredOutputResult<unknown>>;
41
+ structuredOutput(options: StructuredOutputOptions<GrokTextProviderOptions>): Promise<StructuredOutputResult<unknown>>;
40
42
  private processGrokStreamChunks;
41
43
  /**
42
44
  * Maps common options to Grok-specific Chat Completions format
@@ -3,6 +3,7 @@ import { validateTextProviderOptions } from "../text/text-provider-options.js";
3
3
  import { makeGrokStructuredOutputCompatible, transformNullsToUndefined } from "../utils/schema-converter.js";
4
4
  import { convertToolsToProviderFormat } from "../tools/tool-converter.js";
5
5
  import { createGrokClient, generateId, getGrokApiKeyFromEnv } from "../utils/client.js";
6
+ const asChunk = (chunk) => chunk;
6
7
  class GrokTextAdapter extends BaseTextAdapter {
7
8
  constructor(config, model) {
8
9
  super({}, model);
@@ -14,7 +15,8 @@ class GrokTextAdapter extends BaseTextAdapter {
14
15
  const requestParams = this.mapTextOptionsToGrok(options);
15
16
  const timestamp = Date.now();
16
17
  const aguiState = {
17
- runId: generateId(this.name),
18
+ runId: options.runId ?? generateId(this.name),
19
+ threadId: options.threadId ?? generateId(this.name),
18
20
  messageId: generateId(this.name),
19
21
  timestamp,
20
22
  hasEmittedRunStarted: false
@@ -29,23 +31,26 @@ class GrokTextAdapter extends BaseTextAdapter {
29
31
  const err = error;
30
32
  if (!aguiState.hasEmittedRunStarted) {
31
33
  aguiState.hasEmittedRunStarted = true;
32
- yield {
34
+ yield asChunk({
33
35
  type: "RUN_STARTED",
34
36
  runId: aguiState.runId,
37
+ threadId: aguiState.threadId,
35
38
  model: options.model,
36
39
  timestamp
37
- };
40
+ });
38
41
  }
39
- yield {
42
+ yield asChunk({
40
43
  type: "RUN_ERROR",
41
44
  runId: aguiState.runId,
42
45
  model: options.model,
43
46
  timestamp,
47
+ message: err.message || "Unknown error",
48
+ code: err.code,
44
49
  error: {
45
50
  message: err.message || "Unknown error",
46
51
  code: err.code
47
52
  }
48
- };
53
+ });
49
54
  console.error(">>> chatStream: Fatal error during response creation <<<");
50
55
  console.error(">>> Error message:", err.message);
51
56
  console.error(">>> Error stack:", err.stack);
@@ -116,12 +121,13 @@ class GrokTextAdapter extends BaseTextAdapter {
116
121
  if (!choice) continue;
117
122
  if (!aguiState.hasEmittedRunStarted) {
118
123
  aguiState.hasEmittedRunStarted = true;
119
- yield {
124
+ yield asChunk({
120
125
  type: "RUN_STARTED",
121
126
  runId: aguiState.runId,
127
+ threadId: aguiState.threadId,
122
128
  model: chunk.model || options.model,
123
129
  timestamp
124
- };
130
+ });
125
131
  }
126
132
  const delta = choice.delta;
127
133
  const deltaContent = delta.content;
@@ -129,23 +135,23 @@ class GrokTextAdapter extends BaseTextAdapter {
129
135
  if (deltaContent) {
130
136
  if (!hasEmittedTextMessageStart) {
131
137
  hasEmittedTextMessageStart = true;
132
- yield {
138
+ yield asChunk({
133
139
  type: "TEXT_MESSAGE_START",
134
140
  messageId: aguiState.messageId,
135
141
  model: chunk.model || options.model,
136
142
  timestamp,
137
143
  role: "assistant"
138
- };
144
+ });
139
145
  }
140
146
  accumulatedContent += deltaContent;
141
- yield {
147
+ yield asChunk({
142
148
  type: "TEXT_MESSAGE_CONTENT",
143
149
  messageId: aguiState.messageId,
144
150
  model: chunk.model || options.model,
145
151
  timestamp,
146
152
  delta: deltaContent,
147
153
  content: accumulatedContent
148
- };
154
+ });
149
155
  }
150
156
  if (deltaToolCalls) {
151
157
  for (const toolCallDelta of deltaToolCalls) {
@@ -170,23 +176,24 @@ class GrokTextAdapter extends BaseTextAdapter {
170
176
  }
171
177
  if (toolCall.id && toolCall.name && !toolCall.started) {
172
178
  toolCall.started = true;
173
- yield {
179
+ yield asChunk({
174
180
  type: "TOOL_CALL_START",
175
181
  toolCallId: toolCall.id,
182
+ toolCallName: toolCall.name,
176
183
  toolName: toolCall.name,
177
184
  model: chunk.model || options.model,
178
185
  timestamp,
179
186
  index
180
- };
187
+ });
181
188
  }
182
189
  if (toolCallDelta.function?.arguments && toolCall.started) {
183
- yield {
190
+ yield asChunk({
184
191
  type: "TOOL_CALL_ARGS",
185
192
  toolCallId: toolCall.id,
186
193
  model: chunk.model || options.model,
187
194
  timestamp,
188
195
  delta: toolCallDelta.function.arguments
189
- };
196
+ });
190
197
  }
191
198
  }
192
199
  }
@@ -199,28 +206,30 @@ class GrokTextAdapter extends BaseTextAdapter {
199
206
  } catch {
200
207
  parsedInput = {};
201
208
  }
202
- yield {
209
+ yield asChunk({
203
210
  type: "TOOL_CALL_END",
204
211
  toolCallId: toolCall.id,
212
+ toolCallName: toolCall.name,
205
213
  toolName: toolCall.name,
206
214
  model: chunk.model || options.model,
207
215
  timestamp,
208
216
  input: parsedInput
209
- };
217
+ });
210
218
  }
211
219
  }
212
220
  const computedFinishReason = choice.finish_reason === "tool_calls" || toolCallsInProgress.size > 0 ? "tool_calls" : "stop";
213
221
  if (hasEmittedTextMessageStart) {
214
- yield {
222
+ yield asChunk({
215
223
  type: "TEXT_MESSAGE_END",
216
224
  messageId: aguiState.messageId,
217
225
  model: chunk.model || options.model,
218
226
  timestamp
219
- };
227
+ });
220
228
  }
221
- yield {
229
+ yield asChunk({
222
230
  type: "RUN_FINISHED",
223
231
  runId: aguiState.runId,
232
+ threadId: aguiState.threadId,
224
233
  model: chunk.model || options.model,
225
234
  timestamp,
226
235
  usage: chunk.usage ? {
@@ -229,22 +238,24 @@ class GrokTextAdapter extends BaseTextAdapter {
229
238
  totalTokens: chunk.usage.total_tokens || 0
230
239
  } : void 0,
231
240
  finishReason: computedFinishReason
232
- };
241
+ });
233
242
  }
234
243
  }
235
244
  } catch (error) {
236
245
  const err = error;
237
246
  console.log("[Grok Adapter] Stream ended with error:", err.message);
238
- yield {
247
+ yield asChunk({
239
248
  type: "RUN_ERROR",
240
249
  runId: aguiState.runId,
241
250
  model: options.model,
242
251
  timestamp,
252
+ message: err.message || "Unknown error occurred",
253
+ code: err.code,
243
254
  error: {
244
255
  message: err.message || "Unknown error occurred",
245
256
  code: err.code
246
257
  }
247
- };
258
+ });
248
259
  }
249
260
  }
250
261
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"text.js","sources":["../../../src/adapters/text.ts"],"sourcesContent":["import { BaseTextAdapter } from '@tanstack/ai/adapters'\nimport { validateTextProviderOptions } from '../text/text-provider-options'\nimport { convertToolsToProviderFormat } from '../tools'\nimport {\n createGrokClient,\n generateId,\n getGrokApiKeyFromEnv,\n makeGrokStructuredOutputCompatible,\n transformNullsToUndefined,\n} from '../utils'\nimport type {\n GROK_CHAT_MODELS,\n ResolveInputModalities,\n ResolveProviderOptions,\n} from '../model-meta'\nimport type {\n StructuredOutputOptions,\n StructuredOutputResult,\n} from '@tanstack/ai/adapters'\nimport type OpenAI_SDK from 'openai'\nimport type {\n ContentPart,\n ModelMessage,\n StreamChunk,\n TextOptions,\n} from '@tanstack/ai'\nimport type { InternalTextProviderOptions } from '../text/text-provider-options'\nimport type {\n GrokImageMetadata,\n GrokMessageMetadataByModality,\n} from '../message-types'\nimport type { GrokClientConfig } from '../utils'\n\n/**\n * Configuration for Grok text adapter\n */\nexport interface GrokTextConfig extends GrokClientConfig {}\n\n/**\n * Alias for TextProviderOptions for external use\n */\nexport type { ExternalTextProviderOptions as GrokTextProviderOptions } from '../text/text-provider-options'\n\n/**\n * Grok Text (Chat) Adapter\n *\n * Tree-shakeable adapter for Grok chat/text completion functionality.\n * Uses OpenAI-compatible Chat Completions API (not Responses API).\n */\nexport class GrokTextAdapter<\n TModel extends (typeof GROK_CHAT_MODELS)[number],\n> extends BaseTextAdapter<\n TModel,\n ResolveProviderOptions<TModel>,\n ResolveInputModalities<TModel>,\n GrokMessageMetadataByModality\n> {\n readonly kind = 'text' as const\n readonly name = 'grok' as const\n\n private client: OpenAI_SDK\n\n constructor(config: GrokTextConfig, model: TModel) {\n super({}, model)\n this.client = createGrokClient(config)\n }\n\n async *chatStream(\n options: TextOptions<ResolveProviderOptions<TModel>>,\n ): AsyncIterable<StreamChunk> {\n const requestParams = this.mapTextOptionsToGrok(options)\n const timestamp = Date.now()\n\n // AG-UI lifecycle tracking (mutable state object for ESLint compatibility)\n const aguiState = {\n runId: generateId(this.name),\n messageId: generateId(this.name),\n timestamp,\n hasEmittedRunStarted: false,\n }\n\n try {\n const stream = await this.client.chat.completions.create({\n ...requestParams,\n stream: true,\n })\n\n yield* this.processGrokStreamChunks(stream, options, aguiState)\n } catch (error: unknown) {\n const err = error as Error & { code?: string }\n\n // Emit RUN_STARTED if not yet emitted\n if (!aguiState.hasEmittedRunStarted) {\n aguiState.hasEmittedRunStarted = true\n yield {\n type: 'RUN_STARTED',\n runId: aguiState.runId,\n model: options.model,\n timestamp,\n }\n }\n\n // Emit AG-UI RUN_ERROR\n yield {\n type: 'RUN_ERROR',\n runId: aguiState.runId,\n model: options.model,\n timestamp,\n error: {\n message: err.message || 'Unknown error',\n code: err.code,\n },\n }\n\n console.error('>>> chatStream: Fatal error during response creation <<<')\n console.error('>>> Error message:', err.message)\n console.error('>>> Error stack:', err.stack)\n console.error('>>> Full error:', err)\n }\n }\n\n /**\n * Generate structured output using Grok's JSON Schema response format.\n * Uses stream: false to get the complete response in one call.\n *\n * Grok has strict requirements for structured output (via OpenAI-compatible API):\n * - All properties must be in the `required` array\n * - Optional fields should have null added to their type union\n * - additionalProperties must be false for all objects\n *\n * The outputSchema is already JSON Schema (converted in the ai layer).\n * We apply Grok-specific transformations for structured output compatibility.\n */\n async structuredOutput(\n options: StructuredOutputOptions<ResolveProviderOptions<TModel>>,\n ): Promise<StructuredOutputResult<unknown>> {\n const { chatOptions, outputSchema } = options\n const requestParams = this.mapTextOptionsToGrok(chatOptions)\n\n // Apply Grok-specific transformations for structured output compatibility\n const jsonSchema = makeGrokStructuredOutputCompatible(\n outputSchema,\n outputSchema.required || [],\n )\n\n try {\n const response = await this.client.chat.completions.create({\n ...requestParams,\n stream: false,\n response_format: {\n type: 'json_schema',\n json_schema: {\n name: 'structured_output',\n schema: jsonSchema,\n strict: true,\n },\n },\n })\n\n // Extract text content from the response\n const rawText = response.choices[0]?.message.content || ''\n\n // Parse the JSON response\n let parsed: unknown\n try {\n parsed = JSON.parse(rawText)\n } catch {\n throw new Error(\n `Failed to parse structured output as JSON. Content: ${rawText.slice(0, 200)}${rawText.length > 200 ? '...' : ''}`,\n )\n }\n\n // Transform null values to undefined to match original Zod schema expectations\n // Grok returns null for optional fields we made nullable in the schema\n const transformed = transformNullsToUndefined(parsed)\n\n return {\n data: transformed,\n rawText,\n }\n } catch (error: unknown) {\n const err = error as Error\n console.error('>>> structuredOutput: Error during response creation <<<')\n console.error('>>> Error message:', err.message)\n throw error\n }\n }\n\n private async *processGrokStreamChunks(\n stream: AsyncIterable<OpenAI_SDK.Chat.Completions.ChatCompletionChunk>,\n options: TextOptions,\n aguiState: {\n runId: string\n messageId: string\n timestamp: number\n hasEmittedRunStarted: boolean\n },\n ): AsyncIterable<StreamChunk> {\n let accumulatedContent = ''\n const timestamp = aguiState.timestamp\n let hasEmittedTextMessageStart = false\n\n // Track tool calls being streamed (arguments come in chunks)\n const toolCallsInProgress = new Map<\n number,\n {\n id: string\n name: string\n arguments: string\n started: boolean // Track if TOOL_CALL_START has been emitted\n }\n >()\n\n try {\n for await (const chunk of stream) {\n const choice = chunk.choices[0]\n\n if (!choice) continue\n\n // Emit RUN_STARTED on first chunk\n if (!aguiState.hasEmittedRunStarted) {\n aguiState.hasEmittedRunStarted = true\n yield {\n type: 'RUN_STARTED',\n runId: aguiState.runId,\n model: chunk.model || options.model,\n timestamp,\n }\n }\n\n const delta = choice.delta\n const deltaContent = delta.content\n const deltaToolCalls = delta.tool_calls\n\n // Handle content delta\n if (deltaContent) {\n // Emit TEXT_MESSAGE_START on first text content\n if (!hasEmittedTextMessageStart) {\n hasEmittedTextMessageStart = true\n yield {\n type: 'TEXT_MESSAGE_START',\n messageId: aguiState.messageId,\n model: chunk.model || options.model,\n timestamp,\n role: 'assistant',\n }\n }\n\n accumulatedContent += deltaContent\n\n // Emit AG-UI TEXT_MESSAGE_CONTENT\n yield {\n type: 'TEXT_MESSAGE_CONTENT',\n messageId: aguiState.messageId,\n model: chunk.model || options.model,\n timestamp,\n delta: deltaContent,\n content: accumulatedContent,\n }\n }\n\n // Handle tool calls - they come in as deltas\n if (deltaToolCalls) {\n for (const toolCallDelta of deltaToolCalls) {\n const index = toolCallDelta.index\n\n // Initialize or update the tool call in progress\n if (!toolCallsInProgress.has(index)) {\n toolCallsInProgress.set(index, {\n id: toolCallDelta.id || '',\n name: toolCallDelta.function?.name || '',\n arguments: '',\n started: false,\n })\n }\n\n const toolCall = toolCallsInProgress.get(index)!\n\n // Update with any new data from the delta\n if (toolCallDelta.id) {\n toolCall.id = toolCallDelta.id\n }\n if (toolCallDelta.function?.name) {\n toolCall.name = toolCallDelta.function.name\n }\n if (toolCallDelta.function?.arguments) {\n toolCall.arguments += toolCallDelta.function.arguments\n }\n\n // Emit TOOL_CALL_START when we have id and name\n if (toolCall.id && toolCall.name && !toolCall.started) {\n toolCall.started = true\n yield {\n type: 'TOOL_CALL_START',\n toolCallId: toolCall.id,\n toolName: toolCall.name,\n model: chunk.model || options.model,\n timestamp,\n index,\n }\n }\n\n // Emit TOOL_CALL_ARGS for argument deltas\n if (toolCallDelta.function?.arguments && toolCall.started) {\n yield {\n type: 'TOOL_CALL_ARGS',\n toolCallId: toolCall.id,\n model: chunk.model || options.model,\n timestamp,\n delta: toolCallDelta.function.arguments,\n }\n }\n }\n }\n\n // Handle finish reason\n if (choice.finish_reason) {\n // Emit all completed tool calls\n if (\n choice.finish_reason === 'tool_calls' ||\n toolCallsInProgress.size > 0\n ) {\n for (const [, toolCall] of toolCallsInProgress) {\n // Parse arguments for TOOL_CALL_END\n let parsedInput: unknown = {}\n try {\n parsedInput = toolCall.arguments\n ? JSON.parse(toolCall.arguments)\n : {}\n } catch {\n parsedInput = {}\n }\n\n // Emit AG-UI TOOL_CALL_END\n yield {\n type: 'TOOL_CALL_END',\n toolCallId: toolCall.id,\n toolName: toolCall.name,\n model: chunk.model || options.model,\n timestamp,\n input: parsedInput,\n }\n }\n }\n\n const computedFinishReason =\n choice.finish_reason === 'tool_calls' ||\n toolCallsInProgress.size > 0\n ? 'tool_calls'\n : 'stop'\n\n // Emit TEXT_MESSAGE_END if we had text content\n if (hasEmittedTextMessageStart) {\n yield {\n type: 'TEXT_MESSAGE_END',\n messageId: aguiState.messageId,\n model: chunk.model || options.model,\n timestamp,\n }\n }\n\n // Emit AG-UI RUN_FINISHED\n yield {\n type: 'RUN_FINISHED',\n runId: aguiState.runId,\n model: chunk.model || options.model,\n timestamp,\n usage: chunk.usage\n ? {\n promptTokens: chunk.usage.prompt_tokens || 0,\n completionTokens: chunk.usage.completion_tokens || 0,\n totalTokens: chunk.usage.total_tokens || 0,\n }\n : undefined,\n finishReason: computedFinishReason,\n }\n }\n }\n } catch (error: unknown) {\n const err = error as Error & { code?: string }\n console.log('[Grok Adapter] Stream ended with error:', err.message)\n\n // Emit AG-UI RUN_ERROR\n yield {\n type: 'RUN_ERROR',\n runId: aguiState.runId,\n model: options.model,\n timestamp,\n error: {\n message: err.message || 'Unknown error occurred',\n code: err.code,\n },\n }\n }\n }\n\n /**\n * Maps common options to Grok-specific Chat Completions format\n */\n private mapTextOptionsToGrok(\n options: TextOptions,\n ): OpenAI_SDK.Chat.Completions.ChatCompletionCreateParamsStreaming {\n const modelOptions = options.modelOptions as\n | Omit<\n InternalTextProviderOptions,\n 'max_tokens' | 'tools' | 'temperature' | 'input' | 'top_p'\n >\n | undefined\n\n if (modelOptions) {\n validateTextProviderOptions({\n ...modelOptions,\n model: options.model,\n })\n }\n\n const tools = options.tools\n ? convertToolsToProviderFormat(options.tools)\n : undefined\n\n // Build messages array with system prompts\n const messages: Array<OpenAI_SDK.Chat.Completions.ChatCompletionMessageParam> =\n []\n\n // Add system prompts first\n if (options.systemPrompts && options.systemPrompts.length > 0) {\n messages.push({\n role: 'system',\n content: options.systemPrompts.join('\\n'),\n })\n }\n\n // Convert messages\n for (const message of options.messages) {\n messages.push(this.convertMessageToGrok(message))\n }\n\n return {\n model: options.model,\n messages,\n temperature: options.temperature,\n max_tokens: options.maxTokens,\n top_p: options.topP,\n tools: tools as Array<OpenAI_SDK.Chat.Completions.ChatCompletionTool>,\n stream: true,\n stream_options: { include_usage: true },\n }\n }\n\n private convertMessageToGrok(\n message: ModelMessage,\n ): OpenAI_SDK.Chat.Completions.ChatCompletionMessageParam {\n // Handle tool messages\n if (message.role === 'tool') {\n return {\n role: 'tool',\n tool_call_id: message.toolCallId || '',\n content:\n typeof message.content === 'string'\n ? message.content\n : JSON.stringify(message.content),\n }\n }\n\n // Handle assistant messages\n if (message.role === 'assistant') {\n const toolCalls = message.toolCalls?.map((tc) => ({\n id: tc.id,\n type: 'function' as const,\n function: {\n name: tc.function.name,\n arguments:\n typeof tc.function.arguments === 'string'\n ? tc.function.arguments\n : JSON.stringify(tc.function.arguments),\n },\n }))\n\n return {\n role: 'assistant',\n content: this.extractTextContent(message.content),\n ...(toolCalls && toolCalls.length > 0 ? { tool_calls: toolCalls } : {}),\n }\n }\n\n // Handle user messages - support multimodal content\n const contentParts = this.normalizeContent(message.content)\n\n // If only text, use simple string format\n if (contentParts.length === 1 && contentParts[0]?.type === 'text') {\n return {\n role: 'user',\n content: contentParts[0].content,\n }\n }\n\n // Otherwise, use array format for multimodal\n const parts: Array<OpenAI_SDK.Chat.Completions.ChatCompletionContentPart> =\n []\n for (const part of contentParts) {\n if (part.type === 'text') {\n parts.push({ type: 'text', text: part.content })\n } else if (part.type === 'image') {\n const imageMetadata = part.metadata as GrokImageMetadata | undefined\n // For base64 data, construct a data URI using the mimeType from source\n const imageValue = part.source.value\n const imageUrl =\n part.source.type === 'data' && !imageValue.startsWith('data:')\n ? `data:${part.source.mimeType};base64,${imageValue}`\n : imageValue\n parts.push({\n type: 'image_url',\n image_url: {\n url: imageUrl,\n detail: imageMetadata?.detail || 'auto',\n },\n })\n }\n }\n\n return {\n role: 'user',\n content: parts.length > 0 ? parts : '',\n }\n }\n\n /**\n * Normalizes message content to an array of ContentPart.\n * Handles backward compatibility with string content.\n */\n private normalizeContent(\n content: string | null | Array<ContentPart>,\n ): Array<ContentPart> {\n if (content === null) {\n return []\n }\n if (typeof content === 'string') {\n return [{ type: 'text', content: content }]\n }\n return content\n }\n\n /**\n * Extracts text content from a content value that may be string, null, or ContentPart array.\n */\n private extractTextContent(\n content: string | null | Array<ContentPart>,\n ): string {\n if (content === null) {\n return ''\n }\n if (typeof content === 'string') {\n return content\n }\n // It's an array of ContentPart\n return content\n .filter((p) => p.type === 'text')\n .map((p) => p.content)\n .join('')\n }\n}\n\n/**\n * Creates a Grok text adapter with explicit API key.\n * Type resolution happens here at the call site.\n *\n * @param model - The model name (e.g., 'grok-3', 'grok-4')\n * @param apiKey - Your xAI API key\n * @param config - Optional additional configuration\n * @returns Configured Grok text adapter instance with resolved types\n *\n * @example\n * ```typescript\n * const adapter = createGrokText('grok-3', \"xai-...\");\n * // adapter has type-safe providerOptions for grok-3\n * ```\n */\nexport function createGrokText<\n TModel extends (typeof GROK_CHAT_MODELS)[number],\n>(\n model: TModel,\n apiKey: string,\n config?: Omit<GrokTextConfig, 'apiKey'>,\n): GrokTextAdapter<TModel> {\n return new GrokTextAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates a Grok text 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-3', 'grok-4')\n * @param config - Optional configuration (excluding apiKey which is auto-detected)\n * @returns Configured Grok text 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 = grokText('grok-3');\n *\n * const stream = chat({\n * adapter,\n * messages: [{ role: \"user\", content: \"Hello!\" }]\n * });\n * ```\n */\nexport function grokText<TModel extends (typeof GROK_CHAT_MODELS)[number]>(\n model: TModel,\n config?: Omit<GrokTextConfig, 'apiKey'>,\n): GrokTextAdapter<TModel> {\n const apiKey = getGrokApiKeyFromEnv()\n return createGrokText(model, apiKey, config)\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"text.js","sources":["../../../src/adapters/text.ts"],"sourcesContent":["import { BaseTextAdapter } from '@tanstack/ai/adapters'\nimport { validateTextProviderOptions } from '../text/text-provider-options'\nimport { convertToolsToProviderFormat } from '../tools'\nimport {\n createGrokClient,\n generateId,\n getGrokApiKeyFromEnv,\n makeGrokStructuredOutputCompatible,\n transformNullsToUndefined,\n} from '../utils'\nimport type {\n GROK_CHAT_MODELS,\n GrokChatModelToolCapabilitiesByName,\n ResolveInputModalities,\n ResolveProviderOptions,\n} from '../model-meta'\nimport type {\n StructuredOutputOptions,\n StructuredOutputResult,\n} from '@tanstack/ai/adapters'\nimport type OpenAI_SDK from 'openai'\nimport type {\n ContentPart,\n Modality,\n ModelMessage,\n StreamChunk,\n TextOptions,\n} from '@tanstack/ai'\nimport type {\n ExternalTextProviderOptions as GrokTextProviderOptions,\n InternalTextProviderOptions,\n} from '../text/text-provider-options'\nimport type {\n GrokImageMetadata,\n GrokMessageMetadataByModality,\n} from '../message-types'\nimport type { GrokClientConfig } from '../utils'\n\ntype ResolveToolCapabilities<TModel extends string> =\n TModel extends keyof GrokChatModelToolCapabilitiesByName\n ? NonNullable<GrokChatModelToolCapabilitiesByName[TModel]>\n : readonly []\n\n/** Cast an event object to StreamChunk. Adapters construct events with string\n * literal types which are structurally compatible with the EventType enum. */\nconst asChunk = (chunk: Record<string, unknown>) =>\n chunk as unknown as StreamChunk\n\n/**\n * Configuration for Grok text adapter\n */\nexport interface GrokTextConfig extends GrokClientConfig {}\n\n/**\n * Alias for TextProviderOptions for external use\n */\nexport type { ExternalTextProviderOptions as GrokTextProviderOptions } from '../text/text-provider-options'\n\n/**\n * Grok Text (Chat) Adapter\n *\n * Tree-shakeable adapter for Grok chat/text completion functionality.\n * Uses OpenAI-compatible Chat Completions API (not Responses API).\n */\nexport class GrokTextAdapter<\n TModel extends (typeof GROK_CHAT_MODELS)[number],\n TProviderOptions extends Record<string, any> = ResolveProviderOptions<TModel>,\n TInputModalities extends ReadonlyArray<Modality> =\n ResolveInputModalities<TModel>,\n TToolCapabilities extends ReadonlyArray<string> =\n ResolveToolCapabilities<TModel>,\n> extends BaseTextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n GrokMessageMetadataByModality,\n TToolCapabilities\n> {\n readonly kind = 'text' as const\n readonly name = 'grok' as const\n\n private client: OpenAI_SDK\n\n constructor(config: GrokTextConfig, model: TModel) {\n super({}, model)\n this.client = createGrokClient(config)\n }\n\n async *chatStream(\n options: TextOptions<GrokTextProviderOptions>,\n ): AsyncIterable<StreamChunk> {\n const requestParams = this.mapTextOptionsToGrok(options)\n const timestamp = Date.now()\n\n // AG-UI lifecycle tracking (mutable state object for ESLint compatibility)\n const aguiState = {\n runId: options.runId ?? generateId(this.name),\n threadId: options.threadId ?? generateId(this.name),\n messageId: generateId(this.name),\n timestamp,\n hasEmittedRunStarted: false,\n }\n\n try {\n const stream = await this.client.chat.completions.create({\n ...requestParams,\n stream: true,\n })\n\n yield* this.processGrokStreamChunks(stream, options, aguiState)\n } catch (error: unknown) {\n const err = error as Error & { code?: string }\n\n // Emit RUN_STARTED if not yet emitted\n if (!aguiState.hasEmittedRunStarted) {\n aguiState.hasEmittedRunStarted = true\n yield asChunk({\n type: 'RUN_STARTED',\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model: options.model,\n timestamp,\n })\n }\n\n // Emit AG-UI RUN_ERROR\n yield asChunk({\n type: 'RUN_ERROR',\n runId: aguiState.runId,\n model: options.model,\n timestamp,\n message: err.message || 'Unknown error',\n code: err.code,\n error: {\n message: err.message || 'Unknown error',\n code: err.code,\n },\n })\n\n console.error('>>> chatStream: Fatal error during response creation <<<')\n console.error('>>> Error message:', err.message)\n console.error('>>> Error stack:', err.stack)\n console.error('>>> Full error:', err)\n }\n }\n\n /**\n * Generate structured output using Grok's JSON Schema response format.\n * Uses stream: false to get the complete response in one call.\n *\n * Grok has strict requirements for structured output (via OpenAI-compatible API):\n * - All properties must be in the `required` array\n * - Optional fields should have null added to their type union\n * - additionalProperties must be false for all objects\n *\n * The outputSchema is already JSON Schema (converted in the ai layer).\n * We apply Grok-specific transformations for structured output compatibility.\n */\n async structuredOutput(\n options: StructuredOutputOptions<GrokTextProviderOptions>,\n ): Promise<StructuredOutputResult<unknown>> {\n const { chatOptions, outputSchema } = options\n const requestParams = this.mapTextOptionsToGrok(chatOptions)\n\n // Apply Grok-specific transformations for structured output compatibility\n const jsonSchema = makeGrokStructuredOutputCompatible(\n outputSchema,\n outputSchema.required || [],\n )\n\n try {\n const response = await this.client.chat.completions.create({\n ...requestParams,\n stream: false,\n response_format: {\n type: 'json_schema',\n json_schema: {\n name: 'structured_output',\n schema: jsonSchema,\n strict: true,\n },\n },\n })\n\n // Extract text content from the response\n const rawText = response.choices[0]?.message.content || ''\n\n // Parse the JSON response\n let parsed: unknown\n try {\n parsed = JSON.parse(rawText)\n } catch {\n throw new Error(\n `Failed to parse structured output as JSON. Content: ${rawText.slice(0, 200)}${rawText.length > 200 ? '...' : ''}`,\n )\n }\n\n // Transform null values to undefined to match original Zod schema expectations\n // Grok returns null for optional fields we made nullable in the schema\n const transformed = transformNullsToUndefined(parsed)\n\n return {\n data: transformed,\n rawText,\n }\n } catch (error: unknown) {\n const err = error as Error\n console.error('>>> structuredOutput: Error during response creation <<<')\n console.error('>>> Error message:', err.message)\n throw error\n }\n }\n\n private async *processGrokStreamChunks(\n stream: AsyncIterable<OpenAI_SDK.Chat.Completions.ChatCompletionChunk>,\n options: TextOptions,\n aguiState: {\n runId: string\n threadId: string\n messageId: string\n timestamp: number\n hasEmittedRunStarted: boolean\n },\n ): AsyncIterable<StreamChunk> {\n let accumulatedContent = ''\n const timestamp = aguiState.timestamp\n let hasEmittedTextMessageStart = false\n\n // Track tool calls being streamed (arguments come in chunks)\n const toolCallsInProgress = new Map<\n number,\n {\n id: string\n name: string\n arguments: string\n started: boolean // Track if TOOL_CALL_START has been emitted\n }\n >()\n\n try {\n for await (const chunk of stream) {\n const choice = chunk.choices[0]\n\n if (!choice) continue\n\n // Emit RUN_STARTED on first chunk\n if (!aguiState.hasEmittedRunStarted) {\n aguiState.hasEmittedRunStarted = true\n yield asChunk({\n type: 'RUN_STARTED',\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model: chunk.model || options.model,\n timestamp,\n })\n }\n\n const delta = choice.delta\n const deltaContent = delta.content\n const deltaToolCalls = delta.tool_calls\n\n // Handle content delta\n if (deltaContent) {\n // Emit TEXT_MESSAGE_START on first text content\n if (!hasEmittedTextMessageStart) {\n hasEmittedTextMessageStart = true\n yield asChunk({\n type: 'TEXT_MESSAGE_START',\n messageId: aguiState.messageId,\n model: chunk.model || options.model,\n timestamp,\n role: 'assistant',\n })\n }\n\n accumulatedContent += deltaContent\n\n // Emit AG-UI TEXT_MESSAGE_CONTENT\n yield asChunk({\n type: 'TEXT_MESSAGE_CONTENT',\n messageId: aguiState.messageId,\n model: chunk.model || options.model,\n timestamp,\n delta: deltaContent,\n content: accumulatedContent,\n })\n }\n\n // Handle tool calls - they come in as deltas\n if (deltaToolCalls) {\n for (const toolCallDelta of deltaToolCalls) {\n const index = toolCallDelta.index\n\n // Initialize or update the tool call in progress\n if (!toolCallsInProgress.has(index)) {\n toolCallsInProgress.set(index, {\n id: toolCallDelta.id || '',\n name: toolCallDelta.function?.name || '',\n arguments: '',\n started: false,\n })\n }\n\n const toolCall = toolCallsInProgress.get(index)!\n\n // Update with any new data from the delta\n if (toolCallDelta.id) {\n toolCall.id = toolCallDelta.id\n }\n if (toolCallDelta.function?.name) {\n toolCall.name = toolCallDelta.function.name\n }\n if (toolCallDelta.function?.arguments) {\n toolCall.arguments += toolCallDelta.function.arguments\n }\n\n // Emit TOOL_CALL_START when we have id and name\n if (toolCall.id && toolCall.name && !toolCall.started) {\n toolCall.started = true\n yield asChunk({\n type: 'TOOL_CALL_START',\n toolCallId: toolCall.id,\n toolCallName: toolCall.name,\n toolName: toolCall.name,\n model: chunk.model || options.model,\n timestamp,\n index,\n })\n }\n\n // Emit TOOL_CALL_ARGS for argument deltas\n if (toolCallDelta.function?.arguments && toolCall.started) {\n yield asChunk({\n type: 'TOOL_CALL_ARGS',\n toolCallId: toolCall.id,\n model: chunk.model || options.model,\n timestamp,\n delta: toolCallDelta.function.arguments,\n })\n }\n }\n }\n\n // Handle finish reason\n if (choice.finish_reason) {\n // Emit all completed tool calls\n if (\n choice.finish_reason === 'tool_calls' ||\n toolCallsInProgress.size > 0\n ) {\n for (const [, toolCall] of toolCallsInProgress) {\n // Parse arguments for TOOL_CALL_END\n let parsedInput: unknown = {}\n try {\n parsedInput = toolCall.arguments\n ? JSON.parse(toolCall.arguments)\n : {}\n } catch {\n parsedInput = {}\n }\n\n // Emit AG-UI TOOL_CALL_END\n yield asChunk({\n type: 'TOOL_CALL_END',\n toolCallId: toolCall.id,\n toolCallName: toolCall.name,\n toolName: toolCall.name,\n model: chunk.model || options.model,\n timestamp,\n input: parsedInput,\n })\n }\n }\n\n const computedFinishReason =\n choice.finish_reason === 'tool_calls' ||\n toolCallsInProgress.size > 0\n ? 'tool_calls'\n : 'stop'\n\n // Emit TEXT_MESSAGE_END if we had text content\n if (hasEmittedTextMessageStart) {\n yield asChunk({\n type: 'TEXT_MESSAGE_END',\n messageId: aguiState.messageId,\n model: chunk.model || options.model,\n timestamp,\n })\n }\n\n // Emit AG-UI RUN_FINISHED\n yield asChunk({\n type: 'RUN_FINISHED',\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model: chunk.model || options.model,\n timestamp,\n usage: chunk.usage\n ? {\n promptTokens: chunk.usage.prompt_tokens || 0,\n completionTokens: chunk.usage.completion_tokens || 0,\n totalTokens: chunk.usage.total_tokens || 0,\n }\n : undefined,\n finishReason: computedFinishReason,\n })\n }\n }\n } catch (error: unknown) {\n const err = error as Error & { code?: string }\n console.log('[Grok Adapter] Stream ended with error:', err.message)\n\n // Emit AG-UI RUN_ERROR\n yield asChunk({\n type: 'RUN_ERROR',\n runId: aguiState.runId,\n model: options.model,\n timestamp,\n message: err.message || 'Unknown error occurred',\n code: err.code,\n error: {\n message: err.message || 'Unknown error occurred',\n code: err.code,\n },\n })\n }\n }\n\n /**\n * Maps common options to Grok-specific Chat Completions format\n */\n private mapTextOptionsToGrok(\n options: TextOptions,\n ): OpenAI_SDK.Chat.Completions.ChatCompletionCreateParamsStreaming {\n const modelOptions = options.modelOptions as\n | Omit<\n InternalTextProviderOptions,\n 'max_tokens' | 'tools' | 'temperature' | 'input' | 'top_p'\n >\n | undefined\n\n if (modelOptions) {\n validateTextProviderOptions({\n ...modelOptions,\n model: options.model,\n })\n }\n\n const tools = options.tools\n ? convertToolsToProviderFormat(options.tools)\n : undefined\n\n // Build messages array with system prompts\n const messages: Array<OpenAI_SDK.Chat.Completions.ChatCompletionMessageParam> =\n []\n\n // Add system prompts first\n if (options.systemPrompts && options.systemPrompts.length > 0) {\n messages.push({\n role: 'system',\n content: options.systemPrompts.join('\\n'),\n })\n }\n\n // Convert messages\n for (const message of options.messages) {\n messages.push(this.convertMessageToGrok(message))\n }\n\n return {\n model: options.model,\n messages,\n temperature: options.temperature,\n max_tokens: options.maxTokens,\n top_p: options.topP,\n tools: tools as Array<OpenAI_SDK.Chat.Completions.ChatCompletionTool>,\n stream: true,\n stream_options: { include_usage: true },\n }\n }\n\n private convertMessageToGrok(\n message: ModelMessage,\n ): OpenAI_SDK.Chat.Completions.ChatCompletionMessageParam {\n // Handle tool messages\n if (message.role === 'tool') {\n return {\n role: 'tool',\n tool_call_id: message.toolCallId || '',\n content:\n typeof message.content === 'string'\n ? message.content\n : JSON.stringify(message.content),\n }\n }\n\n // Handle assistant messages\n if (message.role === 'assistant') {\n const toolCalls = message.toolCalls?.map((tc) => ({\n id: tc.id,\n type: 'function' as const,\n function: {\n name: tc.function.name,\n arguments:\n typeof tc.function.arguments === 'string'\n ? tc.function.arguments\n : JSON.stringify(tc.function.arguments),\n },\n }))\n\n return {\n role: 'assistant',\n content: this.extractTextContent(message.content),\n ...(toolCalls && toolCalls.length > 0 ? { tool_calls: toolCalls } : {}),\n }\n }\n\n // Handle user messages - support multimodal content\n const contentParts = this.normalizeContent(message.content)\n\n // If only text, use simple string format\n if (contentParts.length === 1 && contentParts[0]?.type === 'text') {\n return {\n role: 'user',\n content: contentParts[0].content,\n }\n }\n\n // Otherwise, use array format for multimodal\n const parts: Array<OpenAI_SDK.Chat.Completions.ChatCompletionContentPart> =\n []\n for (const part of contentParts) {\n if (part.type === 'text') {\n parts.push({ type: 'text', text: part.content })\n } else if (part.type === 'image') {\n const imageMetadata = part.metadata as GrokImageMetadata | undefined\n // For base64 data, construct a data URI using the mimeType from source\n const imageValue = part.source.value\n const imageUrl =\n part.source.type === 'data' && !imageValue.startsWith('data:')\n ? `data:${part.source.mimeType};base64,${imageValue}`\n : imageValue\n parts.push({\n type: 'image_url',\n image_url: {\n url: imageUrl,\n detail: imageMetadata?.detail || 'auto',\n },\n })\n }\n }\n\n return {\n role: 'user',\n content: parts.length > 0 ? parts : '',\n }\n }\n\n /**\n * Normalizes message content to an array of ContentPart.\n * Handles backward compatibility with string content.\n */\n private normalizeContent(\n content: string | null | Array<ContentPart>,\n ): Array<ContentPart> {\n if (content === null) {\n return []\n }\n if (typeof content === 'string') {\n return [{ type: 'text', content: content }]\n }\n return content\n }\n\n /**\n * Extracts text content from a content value that may be string, null, or ContentPart array.\n */\n private extractTextContent(\n content: string | null | Array<ContentPart>,\n ): string {\n if (content === null) {\n return ''\n }\n if (typeof content === 'string') {\n return content\n }\n // It's an array of ContentPart\n return content\n .filter((p) => p.type === 'text')\n .map((p) => p.content)\n .join('')\n }\n}\n\n/**\n * Creates a Grok text adapter with explicit API key.\n * Type resolution happens here at the call site.\n *\n * @param model - The model name (e.g., 'grok-3', 'grok-4')\n * @param apiKey - Your xAI API key\n * @param config - Optional additional configuration\n * @returns Configured Grok text adapter instance with resolved types\n *\n * @example\n * ```typescript\n * const adapter = createGrokText('grok-3', \"xai-...\");\n * // adapter has type-safe providerOptions for grok-3\n * ```\n */\nexport function createGrokText<\n TModel extends (typeof GROK_CHAT_MODELS)[number],\n>(\n model: TModel,\n apiKey: string,\n config?: Omit<GrokTextConfig, 'apiKey'>,\n): GrokTextAdapter<TModel> {\n return new GrokTextAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates a Grok text 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-3', 'grok-4')\n * @param config - Optional configuration (excluding apiKey which is auto-detected)\n * @returns Configured Grok text 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 = grokText('grok-3');\n *\n * const stream = chat({\n * adapter,\n * messages: [{ role: \"user\", content: \"Hello!\" }]\n * });\n * ```\n */\nexport function grokText<TModel extends (typeof GROK_CHAT_MODELS)[number]>(\n model: TModel,\n config?: Omit<GrokTextConfig, 'apiKey'>,\n): GrokTextAdapter<TModel> {\n const apiKey = getGrokApiKeyFromEnv()\n return createGrokText(model, apiKey, config)\n}\n"],"names":[],"mappings":"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"}
@@ -2,6 +2,6 @@ export { GrokTextAdapter, createGrokText, grokText, type GrokTextConfig, type Gr
2
2
  export { GrokSummarizeAdapter, createGrokSummarize, grokSummarize, type GrokSummarizeConfig, type GrokSummarizeProviderOptions, type GrokSummarizeModel, } from './adapters/summarize.js';
3
3
  export { GrokImageAdapter, createGrokImage, grokImage, type GrokImageConfig, } from './adapters/image.js';
4
4
  export type { GrokImageProviderOptions, GrokImageModelProviderOptionsByName, } from './image/image-provider-options.js';
5
- export type { GrokChatModelProviderOptionsByName, GrokModelInputModalitiesByName, ResolveProviderOptions, ResolveInputModalities, GrokChatModel, GrokImageModel, } from './model-meta.js';
5
+ export type { GrokChatModelProviderOptionsByName, GrokChatModelToolCapabilitiesByName, GrokModelInputModalitiesByName, ResolveProviderOptions, ResolveInputModalities, GrokChatModel, GrokImageModel, } from './model-meta.js';
6
6
  export { GROK_CHAT_MODELS, GROK_IMAGE_MODELS } from './model-meta.js';
7
7
  export type { GrokTextMetadata, GrokImageMetadata, GrokAudioMetadata, GrokVideoMetadata, GrokDocumentMetadata, GrokMessageMetadataByModality, } from './message-types.js';
@@ -5,6 +5,7 @@ declare const GROK_4_1_FAST_REASONING: {
5
5
  readonly input: ["text", "image"];
6
6
  readonly output: ["text"];
7
7
  readonly capabilities: ["reasoning", "structured_outputs", "tool_calling"];
8
+ readonly tools: readonly [];
8
9
  };
9
10
  readonly pricing: {
10
11
  readonly input: {
@@ -23,6 +24,7 @@ declare const GROK_4_1_FAST_NON_REASONING: {
23
24
  readonly input: ["text", "image"];
24
25
  readonly output: ["text"];
25
26
  readonly capabilities: ["structured_outputs", "tool_calling"];
27
+ readonly tools: readonly [];
26
28
  };
27
29
  readonly pricing: {
28
30
  readonly input: {
@@ -41,6 +43,7 @@ declare const GROK_CODE_FAST_1: {
41
43
  readonly input: ["text"];
42
44
  readonly output: ["text"];
43
45
  readonly capabilities: ["reasoning", "structured_outputs", "tool_calling"];
46
+ readonly tools: readonly [];
44
47
  };
45
48
  readonly pricing: {
46
49
  readonly input: {
@@ -59,6 +62,7 @@ declare const GROK_4_FAST_REASONING: {
59
62
  readonly input: ["text", "image"];
60
63
  readonly output: ["text"];
61
64
  readonly capabilities: ["reasoning", "structured_outputs", "tool_calling"];
65
+ readonly tools: readonly [];
62
66
  };
63
67
  readonly pricing: {
64
68
  readonly input: {
@@ -77,6 +81,7 @@ declare const GROK_4_FAST_NON_REASONING: {
77
81
  readonly input: ["text", "image"];
78
82
  readonly output: ["text"];
79
83
  readonly capabilities: ["structured_outputs", "tool_calling"];
84
+ readonly tools: readonly [];
80
85
  };
81
86
  readonly pricing: {
82
87
  readonly input: {
@@ -95,6 +100,7 @@ declare const GROK_4: {
95
100
  readonly input: ["text", "image"];
96
101
  readonly output: ["text"];
97
102
  readonly capabilities: ["reasoning", "structured_outputs", "tool_calling"];
103
+ readonly tools: readonly [];
98
104
  };
99
105
  readonly pricing: {
100
106
  readonly input: {
@@ -113,6 +119,7 @@ declare const GROK_3_MINI: {
113
119
  readonly input: ["text"];
114
120
  readonly output: ["text"];
115
121
  readonly capabilities: ["reasoning", "structured_outputs", "tool_calling"];
122
+ readonly tools: readonly [];
116
123
  };
117
124
  readonly pricing: {
118
125
  readonly input: {
@@ -131,6 +138,7 @@ declare const GROK_3: {
131
138
  readonly input: ["text"];
132
139
  readonly output: ["text"];
133
140
  readonly capabilities: ["structured_outputs", "tool_calling"];
141
+ readonly tools: readonly [];
134
142
  };
135
143
  readonly pricing: {
136
144
  readonly input: {
@@ -149,6 +157,7 @@ declare const GROK_2_VISION: {
149
157
  readonly input: ["text", "image"];
150
158
  readonly output: ["text"];
151
159
  readonly capabilities: ["structured_outputs", "tool_calling"];
160
+ readonly tools: readonly [];
152
161
  };
153
162
  readonly pricing: {
154
163
  readonly input: {
@@ -170,6 +179,7 @@ declare const GROK_4_20: {
170
179
  readonly input: ["text", "image", "document"];
171
180
  readonly output: ["text"];
172
181
  readonly capabilities: ["reasoning", "structured_outputs", "tool_calling"];
182
+ readonly tools: readonly [];
173
183
  };
174
184
  readonly pricing: {
175
185
  readonly input: {
@@ -188,6 +198,7 @@ declare const GROK_4_20_MULTI_AGENT: {
188
198
  readonly input: ["text", "image", "document"];
189
199
  readonly output: ["text"];
190
200
  readonly capabilities: ["reasoning", "structured_outputs", "tool_calling"];
201
+ readonly tools: readonly [];
191
202
  };
192
203
  readonly pricing: {
193
204
  readonly input: {
@@ -230,6 +241,25 @@ export type GrokModelInputModalitiesByName = {
230
241
  export type GrokChatModelProviderOptionsByName = {
231
242
  [K in (typeof GROK_CHAT_MODELS)[number]]: GrokProviderOptions;
232
243
  };
244
+ /**
245
+ * Type-only map from Grok chat model name to its supported provider tools.
246
+ * Grok exposes no provider-specific tool factories, so every model gets an
247
+ * empty tuple. This ensures that passing an Anthropic/OpenAI ProviderTool to
248
+ * a Grok adapter produces a compile-time type error.
249
+ */
250
+ export type GrokChatModelToolCapabilitiesByName = {
251
+ [GROK_4_1_FAST_REASONING.name]: typeof GROK_4_1_FAST_REASONING.supports.tools;
252
+ [GROK_4_1_FAST_NON_REASONING.name]: typeof GROK_4_1_FAST_NON_REASONING.supports.tools;
253
+ [GROK_CODE_FAST_1.name]: typeof GROK_CODE_FAST_1.supports.tools;
254
+ [GROK_4_FAST_REASONING.name]: typeof GROK_4_FAST_REASONING.supports.tools;
255
+ [GROK_4_FAST_NON_REASONING.name]: typeof GROK_4_FAST_NON_REASONING.supports.tools;
256
+ [GROK_4.name]: typeof GROK_4.supports.tools;
257
+ [GROK_3.name]: typeof GROK_3.supports.tools;
258
+ [GROK_3_MINI.name]: typeof GROK_3_MINI.supports.tools;
259
+ [GROK_2_VISION.name]: typeof GROK_2_VISION.supports.tools;
260
+ [GROK_4_20.name]: typeof GROK_4_20.supports.tools;
261
+ [GROK_4_20_MULTI_AGENT.name]: typeof GROK_4_20_MULTI_AGENT.supports.tools;
262
+ };
233
263
  /**
234
264
  * Grok-specific provider options
235
265
  * Based on OpenAI-compatible API options
@@ -1 +1 @@
1
- {"version":3,"file":"model-meta.js","sources":["../../src/model-meta.ts"],"sourcesContent":["/**\n * Model metadata interface for documentation and type inference\n */\ninterface ModelMeta {\n name: string\n supports: {\n input: Array<'text' | 'image' | 'audio' | 'video' | 'document'>\n output: Array<'text' | 'image' | 'audio' | 'video'>\n capabilities?: Array<'reasoning' | 'tool_calling' | 'structured_outputs'>\n }\n max_input_tokens?: number\n max_output_tokens?: number\n context_window?: number\n knowledge_cutoff?: string\n pricing?: {\n input: {\n normal: number\n cached?: number\n }\n output: {\n normal: number\n }\n }\n}\n\nconst GROK_4_1_FAST_REASONING = {\n name: 'grok-4-1-fast-reasoning',\n context_window: 2_000_000,\n supports: {\n input: ['text', 'image'],\n output: ['text'],\n capabilities: ['reasoning', 'structured_outputs', 'tool_calling'],\n },\n pricing: {\n input: {\n normal: 0.2,\n cached: 0.05,\n },\n output: {\n normal: 0.5,\n },\n },\n} as const satisfies ModelMeta\n\nconst GROK_4_1_FAST_NON_REASONING = {\n name: 'grok-4-1-fast-non-reasoning',\n context_window: 2_000_000,\n supports: {\n input: ['text', 'image'],\n output: ['text'],\n capabilities: ['structured_outputs', 'tool_calling'],\n },\n pricing: {\n input: {\n normal: 0.2,\n cached: 0.05,\n },\n output: {\n normal: 0.5,\n },\n },\n} as const satisfies ModelMeta\n\nconst GROK_CODE_FAST_1 = {\n name: 'grok-code-fast-1',\n context_window: 256_000,\n supports: {\n input: ['text'],\n output: ['text'],\n capabilities: ['reasoning', 'structured_outputs', 'tool_calling'],\n },\n pricing: {\n input: {\n normal: 0.2,\n cached: 0.02,\n },\n output: {\n normal: 1.5,\n },\n },\n} as const satisfies ModelMeta\n\nconst GROK_4_FAST_REASONING = {\n name: 'grok-4-fast-reasoning',\n context_window: 2_000_000,\n supports: {\n input: ['text', 'image'],\n output: ['text'],\n capabilities: ['reasoning', 'structured_outputs', 'tool_calling'],\n },\n pricing: {\n input: {\n normal: 0.2,\n cached: 0.05,\n },\n output: {\n normal: 0.5,\n },\n },\n} as const satisfies ModelMeta\n\nconst GROK_4_FAST_NON_REASONING = {\n name: 'grok-4-fast-non-reasoning',\n context_window: 2_000_000,\n supports: {\n input: ['text', 'image'],\n output: ['text'],\n capabilities: ['structured_outputs', 'tool_calling'],\n },\n pricing: {\n input: {\n normal: 0.2,\n cached: 0.05,\n },\n output: {\n normal: 0.5,\n },\n },\n} as const satisfies ModelMeta\n\nconst GROK_4 = {\n name: 'grok-4',\n context_window: 256_000,\n supports: {\n input: ['text', 'image'],\n output: ['text'],\n capabilities: ['reasoning', 'structured_outputs', 'tool_calling'],\n },\n pricing: {\n input: {\n normal: 3,\n cached: 0.75,\n },\n output: {\n normal: 15,\n },\n },\n} as const satisfies ModelMeta\n\nconst GROK_3_MINI = {\n name: 'grok-3-mini',\n context_window: 131_072,\n supports: {\n input: ['text'],\n output: ['text'],\n capabilities: ['reasoning', 'structured_outputs', 'tool_calling'],\n },\n pricing: {\n input: {\n normal: 0.3,\n cached: 0.075,\n },\n output: {\n normal: 0.5,\n },\n },\n} as const satisfies ModelMeta\n\nconst GROK_3 = {\n name: 'grok-3',\n context_window: 131_072,\n supports: {\n input: ['text'],\n output: ['text'],\n capabilities: ['structured_outputs', 'tool_calling'],\n },\n pricing: {\n input: {\n normal: 3,\n cached: 0.75,\n },\n output: {\n normal: 15,\n },\n },\n} as const satisfies ModelMeta\n\nconst GROK_2_VISION = {\n name: 'grok-2-vision-1212',\n context_window: 32_768,\n supports: {\n input: ['text', 'image'],\n output: ['text'],\n capabilities: ['structured_outputs', 'tool_calling'],\n },\n pricing: {\n input: {\n normal: 2,\n },\n output: {\n normal: 10,\n },\n },\n} as const satisfies ModelMeta\n\nconst GROK_2_IMAGE = {\n name: 'grok-2-image-1212',\n supports: {\n input: ['text'],\n output: ['image'],\n },\n pricing: {\n input: {\n normal: 0.07,\n },\n output: {\n normal: 0.07,\n },\n },\n} as const satisfies ModelMeta\n\n/**\n * Grok Chat Models\n * Based on xAI's available models as of 2025\n */\nconst GROK_4_20 = {\n name: 'grok-4.20',\n context_window: 2_000_000,\n supports: {\n input: ['text', 'image', 'document'],\n output: ['text'],\n capabilities: ['reasoning', 'structured_outputs', 'tool_calling'],\n },\n pricing: {\n input: {\n normal: 2,\n cached: 0.2,\n },\n output: {\n normal: 6,\n },\n },\n} as const satisfies ModelMeta\n\nconst GROK_4_20_MULTI_AGENT = {\n name: 'grok-4.20-multi-agent',\n context_window: 2_000_000,\n supports: {\n input: ['text', 'image', 'document'],\n output: ['text'],\n capabilities: ['reasoning', 'structured_outputs', 'tool_calling'],\n },\n pricing: {\n input: {\n normal: 2,\n cached: 0.2,\n },\n output: {\n normal: 6,\n },\n },\n} as const satisfies ModelMeta\n\nexport const GROK_CHAT_MODELS = [\n GROK_4_1_FAST_REASONING.name,\n GROK_4_1_FAST_NON_REASONING.name,\n GROK_CODE_FAST_1.name,\n GROK_4_FAST_REASONING.name,\n GROK_4_FAST_NON_REASONING.name,\n GROK_4.name,\n GROK_3.name,\n GROK_3_MINI.name,\n GROK_2_VISION.name,\n\n GROK_4_20.name,\n GROK_4_20_MULTI_AGENT.name,\n] as const\n\n/**\n * Grok Image Generation Models\n */\nexport const GROK_IMAGE_MODELS = [GROK_2_IMAGE.name] as const\n\nexport type GrokChatModel = (typeof GROK_CHAT_MODELS)[number]\nexport type GrokImageModel = (typeof GROK_IMAGE_MODELS)[number]\n\n/**\n * Type-only map from Grok chat model name to its supported input modalities.\n * Used for type inference when constructing multimodal messages.\n */\nexport type GrokModelInputModalitiesByName = {\n [GROK_4_1_FAST_REASONING.name]: typeof GROK_4_1_FAST_REASONING.supports.input\n [GROK_4_1_FAST_NON_REASONING.name]: typeof GROK_4_1_FAST_NON_REASONING.supports.input\n [GROK_CODE_FAST_1.name]: typeof GROK_CODE_FAST_1.supports.input\n [GROK_4_FAST_REASONING.name]: typeof GROK_4_FAST_REASONING.supports.input\n [GROK_4_FAST_NON_REASONING.name]: typeof GROK_4_FAST_NON_REASONING.supports.input\n [GROK_4.name]: typeof GROK_4.supports.input\n [GROK_3.name]: typeof GROK_3.supports.input\n [GROK_3_MINI.name]: typeof GROK_3_MINI.supports.input\n [GROK_2_VISION.name]: typeof GROK_2_VISION.supports.input\n [GROK_4_20.name]: typeof GROK_4_20.supports.input\n [GROK_4_20_MULTI_AGENT.name]: typeof GROK_4_20_MULTI_AGENT.supports.input\n}\n\n/**\n * Type-only map from Grok chat model name to its provider options type.\n * Since Grok uses OpenAI-compatible API, we reuse OpenAI provider options.\n */\nexport type GrokChatModelProviderOptionsByName = {\n [K in (typeof GROK_CHAT_MODELS)[number]]: GrokProviderOptions\n}\n\n/**\n * Grok-specific provider options\n * Based on OpenAI-compatible API options\n */\nexport interface GrokProviderOptions {\n /** Temperature for response generation (0-2) */\n temperature?: number\n /** Maximum tokens in the response */\n max_tokens?: number\n /** Top-p sampling parameter */\n top_p?: number\n /** Frequency penalty (-2.0 to 2.0) */\n frequency_penalty?: number\n /** Presence penalty (-2.0 to 2.0) */\n presence_penalty?: number\n /** Stop sequences */\n stop?: string | Array<string>\n /** A unique identifier representing your end-user */\n user?: string\n}\n\n// ===========================\n// Type Resolution Helpers\n// ===========================\n\n/**\n * Resolve provider options for a specific model.\n * If the model has explicit options in the map, use those; otherwise use base options.\n */\nexport type ResolveProviderOptions<TModel extends string> =\n TModel extends keyof GrokChatModelProviderOptionsByName\n ? GrokChatModelProviderOptionsByName[TModel]\n : GrokProviderOptions\n\n/**\n * Resolve input modalities for a specific model.\n * If the model has explicit modalities in the map, use those; otherwise use text only.\n */\nexport type ResolveInputModalities<TModel extends string> =\n TModel extends keyof GrokModelInputModalitiesByName\n ? GrokModelInputModalitiesByName[TModel]\n : readonly ['text']\n"],"names":[],"mappings":"AAyBA,MAAM,0BAA0B;AAAA,EAC9B,MAAM;AAgBR;AAEA,MAAM,8BAA8B;AAAA,EAClC,MAAM;AAgBR;AAEA,MAAM,mBAAmB;AAAA,EACvB,MAAM;AAgBR;AAEA,MAAM,wBAAwB;AAAA,EAC5B,MAAM;AAgBR;AAEA,MAAM,4BAA4B;AAAA,EAChC,MAAM;AAgBR;AAEA,MAAM,SAAS;AAAA,EACb,MAAM;AAgBR;AAEA,MAAM,cAAc;AAAA,EAClB,MAAM;AAgBR;AAEA,MAAM,SAAS;AAAA,EACb,MAAM;AAgBR;AAEA,MAAM,gBAAgB;AAAA,EACpB,MAAM;AAeR;AAEA,MAAM,eAAe;AAAA,EACnB,MAAM;AAaR;AAMA,MAAM,YAAY;AAAA,EAChB,MAAM;AAgBR;AAEA,MAAM,wBAAwB;AAAA,EAC5B,MAAM;AAgBR;AAEO,MAAM,mBAAmB;AAAA,EAC9B,wBAAwB;AAAA,EACxB,4BAA4B;AAAA,EAC5B,iBAAiB;AAAA,EACjB,sBAAsB;AAAA,EACtB,0BAA0B;AAAA,EAC1B,OAAO;AAAA,EACP,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,cAAc;AAAA,EAEd,UAAU;AAAA,EACV,sBAAsB;AACxB;AAKO,MAAM,oBAAoB,CAAC,aAAa,IAAI;"}
1
+ {"version":3,"file":"model-meta.js","sources":["../../src/model-meta.ts"],"sourcesContent":["/**\n * Model metadata interface for documentation and type inference\n */\ninterface ModelMeta {\n name: string\n supports: {\n input: Array<'text' | 'image' | 'audio' | 'video' | 'document'>\n output: Array<'text' | 'image' | 'audio' | 'video'>\n capabilities?: Array<'reasoning' | 'tool_calling' | 'structured_outputs'>\n tools?: ReadonlyArray<never>\n }\n max_input_tokens?: number\n max_output_tokens?: number\n context_window?: number\n knowledge_cutoff?: string\n pricing?: {\n input: {\n normal: number\n cached?: number\n }\n output: {\n normal: number\n }\n }\n}\n\nconst GROK_4_1_FAST_REASONING = {\n name: 'grok-4-1-fast-reasoning',\n context_window: 2_000_000,\n supports: {\n input: ['text', 'image'],\n output: ['text'],\n capabilities: ['reasoning', 'structured_outputs', 'tool_calling'],\n tools: [] as const,\n },\n pricing: {\n input: {\n normal: 0.2,\n cached: 0.05,\n },\n output: {\n normal: 0.5,\n },\n },\n} as const satisfies ModelMeta\n\nconst GROK_4_1_FAST_NON_REASONING = {\n name: 'grok-4-1-fast-non-reasoning',\n context_window: 2_000_000,\n supports: {\n input: ['text', 'image'],\n output: ['text'],\n capabilities: ['structured_outputs', 'tool_calling'],\n tools: [] as const,\n },\n pricing: {\n input: {\n normal: 0.2,\n cached: 0.05,\n },\n output: {\n normal: 0.5,\n },\n },\n} as const satisfies ModelMeta\n\nconst GROK_CODE_FAST_1 = {\n name: 'grok-code-fast-1',\n context_window: 256_000,\n supports: {\n input: ['text'],\n output: ['text'],\n capabilities: ['reasoning', 'structured_outputs', 'tool_calling'],\n tools: [] as const,\n },\n pricing: {\n input: {\n normal: 0.2,\n cached: 0.02,\n },\n output: {\n normal: 1.5,\n },\n },\n} as const satisfies ModelMeta\n\nconst GROK_4_FAST_REASONING = {\n name: 'grok-4-fast-reasoning',\n context_window: 2_000_000,\n supports: {\n input: ['text', 'image'],\n output: ['text'],\n capabilities: ['reasoning', 'structured_outputs', 'tool_calling'],\n tools: [] as const,\n },\n pricing: {\n input: {\n normal: 0.2,\n cached: 0.05,\n },\n output: {\n normal: 0.5,\n },\n },\n} as const satisfies ModelMeta\n\nconst GROK_4_FAST_NON_REASONING = {\n name: 'grok-4-fast-non-reasoning',\n context_window: 2_000_000,\n supports: {\n input: ['text', 'image'],\n output: ['text'],\n capabilities: ['structured_outputs', 'tool_calling'],\n tools: [] as const,\n },\n pricing: {\n input: {\n normal: 0.2,\n cached: 0.05,\n },\n output: {\n normal: 0.5,\n },\n },\n} as const satisfies ModelMeta\n\nconst GROK_4 = {\n name: 'grok-4',\n context_window: 256_000,\n supports: {\n input: ['text', 'image'],\n output: ['text'],\n capabilities: ['reasoning', 'structured_outputs', 'tool_calling'],\n tools: [] as const,\n },\n pricing: {\n input: {\n normal: 3,\n cached: 0.75,\n },\n output: {\n normal: 15,\n },\n },\n} as const satisfies ModelMeta\n\nconst GROK_3_MINI = {\n name: 'grok-3-mini',\n context_window: 131_072,\n supports: {\n input: ['text'],\n output: ['text'],\n capabilities: ['reasoning', 'structured_outputs', 'tool_calling'],\n tools: [] as const,\n },\n pricing: {\n input: {\n normal: 0.3,\n cached: 0.075,\n },\n output: {\n normal: 0.5,\n },\n },\n} as const satisfies ModelMeta\n\nconst GROK_3 = {\n name: 'grok-3',\n context_window: 131_072,\n supports: {\n input: ['text'],\n output: ['text'],\n capabilities: ['structured_outputs', 'tool_calling'],\n tools: [] as const,\n },\n pricing: {\n input: {\n normal: 3,\n cached: 0.75,\n },\n output: {\n normal: 15,\n },\n },\n} as const satisfies ModelMeta\n\nconst GROK_2_VISION = {\n name: 'grok-2-vision-1212',\n context_window: 32_768,\n supports: {\n input: ['text', 'image'],\n output: ['text'],\n capabilities: ['structured_outputs', 'tool_calling'],\n tools: [] as const,\n },\n pricing: {\n input: {\n normal: 2,\n },\n output: {\n normal: 10,\n },\n },\n} as const satisfies ModelMeta\n\nconst GROK_2_IMAGE = {\n name: 'grok-2-image-1212',\n supports: {\n input: ['text'],\n output: ['image'],\n },\n pricing: {\n input: {\n normal: 0.07,\n },\n output: {\n normal: 0.07,\n },\n },\n} as const satisfies ModelMeta\n\n/**\n * Grok Chat Models\n * Based on xAI's available models as of 2025\n */\nconst GROK_4_20 = {\n name: 'grok-4.20',\n context_window: 2_000_000,\n supports: {\n input: ['text', 'image', 'document'],\n output: ['text'],\n capabilities: ['reasoning', 'structured_outputs', 'tool_calling'],\n tools: [] as const,\n },\n pricing: {\n input: {\n normal: 2,\n cached: 0.2,\n },\n output: {\n normal: 6,\n },\n },\n} as const satisfies ModelMeta\n\nconst GROK_4_20_MULTI_AGENT = {\n name: 'grok-4.20-multi-agent',\n context_window: 2_000_000,\n supports: {\n input: ['text', 'image', 'document'],\n output: ['text'],\n capabilities: ['reasoning', 'structured_outputs', 'tool_calling'],\n tools: [] as const,\n },\n pricing: {\n input: {\n normal: 2,\n cached: 0.2,\n },\n output: {\n normal: 6,\n },\n },\n} as const satisfies ModelMeta\n\nexport const GROK_CHAT_MODELS = [\n GROK_4_1_FAST_REASONING.name,\n GROK_4_1_FAST_NON_REASONING.name,\n GROK_CODE_FAST_1.name,\n GROK_4_FAST_REASONING.name,\n GROK_4_FAST_NON_REASONING.name,\n GROK_4.name,\n GROK_3.name,\n GROK_3_MINI.name,\n GROK_2_VISION.name,\n\n GROK_4_20.name,\n GROK_4_20_MULTI_AGENT.name,\n] as const\n\n/**\n * Grok Image Generation Models\n */\nexport const GROK_IMAGE_MODELS = [GROK_2_IMAGE.name] as const\n\nexport type GrokChatModel = (typeof GROK_CHAT_MODELS)[number]\nexport type GrokImageModel = (typeof GROK_IMAGE_MODELS)[number]\n\n/**\n * Type-only map from Grok chat model name to its supported input modalities.\n * Used for type inference when constructing multimodal messages.\n */\nexport type GrokModelInputModalitiesByName = {\n [GROK_4_1_FAST_REASONING.name]: typeof GROK_4_1_FAST_REASONING.supports.input\n [GROK_4_1_FAST_NON_REASONING.name]: typeof GROK_4_1_FAST_NON_REASONING.supports.input\n [GROK_CODE_FAST_1.name]: typeof GROK_CODE_FAST_1.supports.input\n [GROK_4_FAST_REASONING.name]: typeof GROK_4_FAST_REASONING.supports.input\n [GROK_4_FAST_NON_REASONING.name]: typeof GROK_4_FAST_NON_REASONING.supports.input\n [GROK_4.name]: typeof GROK_4.supports.input\n [GROK_3.name]: typeof GROK_3.supports.input\n [GROK_3_MINI.name]: typeof GROK_3_MINI.supports.input\n [GROK_2_VISION.name]: typeof GROK_2_VISION.supports.input\n [GROK_4_20.name]: typeof GROK_4_20.supports.input\n [GROK_4_20_MULTI_AGENT.name]: typeof GROK_4_20_MULTI_AGENT.supports.input\n}\n\n/**\n * Type-only map from Grok chat model name to its provider options type.\n * Since Grok uses OpenAI-compatible API, we reuse OpenAI provider options.\n */\nexport type GrokChatModelProviderOptionsByName = {\n [K in (typeof GROK_CHAT_MODELS)[number]]: GrokProviderOptions\n}\n\n/**\n * Type-only map from Grok chat model name to its supported provider tools.\n * Grok exposes no provider-specific tool factories, so every model gets an\n * empty tuple. This ensures that passing an Anthropic/OpenAI ProviderTool to\n * a Grok adapter produces a compile-time type error.\n */\nexport type GrokChatModelToolCapabilitiesByName = {\n [GROK_4_1_FAST_REASONING.name]: typeof GROK_4_1_FAST_REASONING.supports.tools\n [GROK_4_1_FAST_NON_REASONING.name]: typeof GROK_4_1_FAST_NON_REASONING.supports.tools\n [GROK_CODE_FAST_1.name]: typeof GROK_CODE_FAST_1.supports.tools\n [GROK_4_FAST_REASONING.name]: typeof GROK_4_FAST_REASONING.supports.tools\n [GROK_4_FAST_NON_REASONING.name]: typeof GROK_4_FAST_NON_REASONING.supports.tools\n [GROK_4.name]: typeof GROK_4.supports.tools\n [GROK_3.name]: typeof GROK_3.supports.tools\n [GROK_3_MINI.name]: typeof GROK_3_MINI.supports.tools\n [GROK_2_VISION.name]: typeof GROK_2_VISION.supports.tools\n [GROK_4_20.name]: typeof GROK_4_20.supports.tools\n [GROK_4_20_MULTI_AGENT.name]: typeof GROK_4_20_MULTI_AGENT.supports.tools\n}\n\n/**\n * Grok-specific provider options\n * Based on OpenAI-compatible API options\n */\nexport interface GrokProviderOptions {\n /** Temperature for response generation (0-2) */\n temperature?: number\n /** Maximum tokens in the response */\n max_tokens?: number\n /** Top-p sampling parameter */\n top_p?: number\n /** Frequency penalty (-2.0 to 2.0) */\n frequency_penalty?: number\n /** Presence penalty (-2.0 to 2.0) */\n presence_penalty?: number\n /** Stop sequences */\n stop?: string | Array<string>\n /** A unique identifier representing your end-user */\n user?: string\n}\n\n// ===========================\n// Type Resolution Helpers\n// ===========================\n\n/**\n * Resolve provider options for a specific model.\n * If the model has explicit options in the map, use those; otherwise use base options.\n */\nexport type ResolveProviderOptions<TModel extends string> =\n TModel extends keyof GrokChatModelProviderOptionsByName\n ? GrokChatModelProviderOptionsByName[TModel]\n : GrokProviderOptions\n\n/**\n * Resolve input modalities for a specific model.\n * If the model has explicit modalities in the map, use those; otherwise use text only.\n */\nexport type ResolveInputModalities<TModel extends string> =\n TModel extends keyof GrokModelInputModalitiesByName\n ? GrokModelInputModalitiesByName[TModel]\n : readonly ['text']\n"],"names":[],"mappings":"AA0BA,MAAM,0BAA0B;AAAA,EAC9B,MAAM;AAiBR;AAEA,MAAM,8BAA8B;AAAA,EAClC,MAAM;AAiBR;AAEA,MAAM,mBAAmB;AAAA,EACvB,MAAM;AAiBR;AAEA,MAAM,wBAAwB;AAAA,EAC5B,MAAM;AAiBR;AAEA,MAAM,4BAA4B;AAAA,EAChC,MAAM;AAiBR;AAEA,MAAM,SAAS;AAAA,EACb,MAAM;AAiBR;AAEA,MAAM,cAAc;AAAA,EAClB,MAAM;AAiBR;AAEA,MAAM,SAAS;AAAA,EACb,MAAM;AAiBR;AAEA,MAAM,gBAAgB;AAAA,EACpB,MAAM;AAgBR;AAEA,MAAM,eAAe;AAAA,EACnB,MAAM;AAaR;AAMA,MAAM,YAAY;AAAA,EAChB,MAAM;AAiBR;AAEA,MAAM,wBAAwB;AAAA,EAC5B,MAAM;AAiBR;AAEO,MAAM,mBAAmB;AAAA,EAC9B,wBAAwB;AAAA,EACxB,4BAA4B;AAAA,EAC5B,iBAAiB;AAAA,EACjB,sBAAsB;AAAA,EACtB,0BAA0B;AAAA,EAC1B,OAAO;AAAA,EACP,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,cAAc;AAAA,EAEd,UAAU;AAAA,EACV,sBAAsB;AACxB;AAKO,MAAM,oBAAoB,CAAC,aAAa,IAAI;"}
@@ -0,0 +1,7 @@
1
+ import { convertFunctionToolToAdapterFormat } from "./function-tool.js";
2
+ import { convertToolsToProviderFormat } from "./tool-converter.js";
3
+ export {
4
+ convertFunctionToolToAdapterFormat,
5
+ convertToolsToProviderFormat
6
+ };
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/ai-grok",
3
- "version": "0.6.5",
3
+ "version": "0.6.7",
4
4
  "description": "Grok (xAI) adapter for TanStack AI",
5
5
  "author": "",
6
6
  "license": "MIT",
@@ -16,6 +16,10 @@
16
16
  ".": {
17
17
  "types": "./dist/esm/index.d.ts",
18
18
  "import": "./dist/esm/index.js"
19
+ },
20
+ "./tools": {
21
+ "types": "./dist/esm/tools/index.d.ts",
22
+ "import": "./dist/esm/tools/index.js"
19
23
  }
20
24
  },
21
25
  "files": [
@@ -38,7 +42,7 @@
38
42
  },
39
43
  "peerDependencies": {
40
44
  "zod": "^4.0.0",
41
- "@tanstack/ai": "^0.10.1"
45
+ "@tanstack/ai": "^0.12.0"
42
46
  },
43
47
  "scripts": {
44
48
  "build": "vite build",
@@ -10,6 +10,7 @@ import {
10
10
  } from '../utils'
11
11
  import type {
12
12
  GROK_CHAT_MODELS,
13
+ GrokChatModelToolCapabilitiesByName,
13
14
  ResolveInputModalities,
14
15
  ResolveProviderOptions,
15
16
  } from '../model-meta'
@@ -20,17 +21,31 @@ import type {
20
21
  import type OpenAI_SDK from 'openai'
21
22
  import type {
22
23
  ContentPart,
24
+ Modality,
23
25
  ModelMessage,
24
26
  StreamChunk,
25
27
  TextOptions,
26
28
  } from '@tanstack/ai'
27
- import type { InternalTextProviderOptions } from '../text/text-provider-options'
29
+ import type {
30
+ ExternalTextProviderOptions as GrokTextProviderOptions,
31
+ InternalTextProviderOptions,
32
+ } from '../text/text-provider-options'
28
33
  import type {
29
34
  GrokImageMetadata,
30
35
  GrokMessageMetadataByModality,
31
36
  } from '../message-types'
32
37
  import type { GrokClientConfig } from '../utils'
33
38
 
39
+ type ResolveToolCapabilities<TModel extends string> =
40
+ TModel extends keyof GrokChatModelToolCapabilitiesByName
41
+ ? NonNullable<GrokChatModelToolCapabilitiesByName[TModel]>
42
+ : readonly []
43
+
44
+ /** Cast an event object to StreamChunk. Adapters construct events with string
45
+ * literal types which are structurally compatible with the EventType enum. */
46
+ const asChunk = (chunk: Record<string, unknown>) =>
47
+ chunk as unknown as StreamChunk
48
+
34
49
  /**
35
50
  * Configuration for Grok text adapter
36
51
  */
@@ -49,11 +64,17 @@ export type { ExternalTextProviderOptions as GrokTextProviderOptions } from '../
49
64
  */
50
65
  export class GrokTextAdapter<
51
66
  TModel extends (typeof GROK_CHAT_MODELS)[number],
67
+ TProviderOptions extends Record<string, any> = ResolveProviderOptions<TModel>,
68
+ TInputModalities extends ReadonlyArray<Modality> =
69
+ ResolveInputModalities<TModel>,
70
+ TToolCapabilities extends ReadonlyArray<string> =
71
+ ResolveToolCapabilities<TModel>,
52
72
  > extends BaseTextAdapter<
53
73
  TModel,
54
- ResolveProviderOptions<TModel>,
55
- ResolveInputModalities<TModel>,
56
- GrokMessageMetadataByModality
74
+ TProviderOptions,
75
+ TInputModalities,
76
+ GrokMessageMetadataByModality,
77
+ TToolCapabilities
57
78
  > {
58
79
  readonly kind = 'text' as const
59
80
  readonly name = 'grok' as const
@@ -66,14 +87,15 @@ export class GrokTextAdapter<
66
87
  }
67
88
 
68
89
  async *chatStream(
69
- options: TextOptions<ResolveProviderOptions<TModel>>,
90
+ options: TextOptions<GrokTextProviderOptions>,
70
91
  ): AsyncIterable<StreamChunk> {
71
92
  const requestParams = this.mapTextOptionsToGrok(options)
72
93
  const timestamp = Date.now()
73
94
 
74
95
  // AG-UI lifecycle tracking (mutable state object for ESLint compatibility)
75
96
  const aguiState = {
76
- runId: generateId(this.name),
97
+ runId: options.runId ?? generateId(this.name),
98
+ threadId: options.threadId ?? generateId(this.name),
77
99
  messageId: generateId(this.name),
78
100
  timestamp,
79
101
  hasEmittedRunStarted: false,
@@ -92,25 +114,28 @@ export class GrokTextAdapter<
92
114
  // Emit RUN_STARTED if not yet emitted
93
115
  if (!aguiState.hasEmittedRunStarted) {
94
116
  aguiState.hasEmittedRunStarted = true
95
- yield {
117
+ yield asChunk({
96
118
  type: 'RUN_STARTED',
97
119
  runId: aguiState.runId,
120
+ threadId: aguiState.threadId,
98
121
  model: options.model,
99
122
  timestamp,
100
- }
123
+ })
101
124
  }
102
125
 
103
126
  // Emit AG-UI RUN_ERROR
104
- yield {
127
+ yield asChunk({
105
128
  type: 'RUN_ERROR',
106
129
  runId: aguiState.runId,
107
130
  model: options.model,
108
131
  timestamp,
132
+ message: err.message || 'Unknown error',
133
+ code: err.code,
109
134
  error: {
110
135
  message: err.message || 'Unknown error',
111
136
  code: err.code,
112
137
  },
113
- }
138
+ })
114
139
 
115
140
  console.error('>>> chatStream: Fatal error during response creation <<<')
116
141
  console.error('>>> Error message:', err.message)
@@ -132,7 +157,7 @@ export class GrokTextAdapter<
132
157
  * We apply Grok-specific transformations for structured output compatibility.
133
158
  */
134
159
  async structuredOutput(
135
- options: StructuredOutputOptions<ResolveProviderOptions<TModel>>,
160
+ options: StructuredOutputOptions<GrokTextProviderOptions>,
136
161
  ): Promise<StructuredOutputResult<unknown>> {
137
162
  const { chatOptions, outputSchema } = options
138
163
  const requestParams = this.mapTextOptionsToGrok(chatOptions)
@@ -191,6 +216,7 @@ export class GrokTextAdapter<
191
216
  options: TextOptions,
192
217
  aguiState: {
193
218
  runId: string
219
+ threadId: string
194
220
  messageId: string
195
221
  timestamp: number
196
222
  hasEmittedRunStarted: boolean
@@ -220,12 +246,13 @@ export class GrokTextAdapter<
220
246
  // Emit RUN_STARTED on first chunk
221
247
  if (!aguiState.hasEmittedRunStarted) {
222
248
  aguiState.hasEmittedRunStarted = true
223
- yield {
249
+ yield asChunk({
224
250
  type: 'RUN_STARTED',
225
251
  runId: aguiState.runId,
252
+ threadId: aguiState.threadId,
226
253
  model: chunk.model || options.model,
227
254
  timestamp,
228
- }
255
+ })
229
256
  }
230
257
 
231
258
  const delta = choice.delta
@@ -237,26 +264,26 @@ export class GrokTextAdapter<
237
264
  // Emit TEXT_MESSAGE_START on first text content
238
265
  if (!hasEmittedTextMessageStart) {
239
266
  hasEmittedTextMessageStart = true
240
- yield {
267
+ yield asChunk({
241
268
  type: 'TEXT_MESSAGE_START',
242
269
  messageId: aguiState.messageId,
243
270
  model: chunk.model || options.model,
244
271
  timestamp,
245
272
  role: 'assistant',
246
- }
273
+ })
247
274
  }
248
275
 
249
276
  accumulatedContent += deltaContent
250
277
 
251
278
  // Emit AG-UI TEXT_MESSAGE_CONTENT
252
- yield {
279
+ yield asChunk({
253
280
  type: 'TEXT_MESSAGE_CONTENT',
254
281
  messageId: aguiState.messageId,
255
282
  model: chunk.model || options.model,
256
283
  timestamp,
257
284
  delta: deltaContent,
258
285
  content: accumulatedContent,
259
- }
286
+ })
260
287
  }
261
288
 
262
289
  // Handle tool calls - they come in as deltas
@@ -290,25 +317,26 @@ export class GrokTextAdapter<
290
317
  // Emit TOOL_CALL_START when we have id and name
291
318
  if (toolCall.id && toolCall.name && !toolCall.started) {
292
319
  toolCall.started = true
293
- yield {
320
+ yield asChunk({
294
321
  type: 'TOOL_CALL_START',
295
322
  toolCallId: toolCall.id,
323
+ toolCallName: toolCall.name,
296
324
  toolName: toolCall.name,
297
325
  model: chunk.model || options.model,
298
326
  timestamp,
299
327
  index,
300
- }
328
+ })
301
329
  }
302
330
 
303
331
  // Emit TOOL_CALL_ARGS for argument deltas
304
332
  if (toolCallDelta.function?.arguments && toolCall.started) {
305
- yield {
333
+ yield asChunk({
306
334
  type: 'TOOL_CALL_ARGS',
307
335
  toolCallId: toolCall.id,
308
336
  model: chunk.model || options.model,
309
337
  timestamp,
310
338
  delta: toolCallDelta.function.arguments,
311
- }
339
+ })
312
340
  }
313
341
  }
314
342
  }
@@ -332,14 +360,15 @@ export class GrokTextAdapter<
332
360
  }
333
361
 
334
362
  // Emit AG-UI TOOL_CALL_END
335
- yield {
363
+ yield asChunk({
336
364
  type: 'TOOL_CALL_END',
337
365
  toolCallId: toolCall.id,
366
+ toolCallName: toolCall.name,
338
367
  toolName: toolCall.name,
339
368
  model: chunk.model || options.model,
340
369
  timestamp,
341
370
  input: parsedInput,
342
- }
371
+ })
343
372
  }
344
373
  }
345
374
 
@@ -351,18 +380,19 @@ export class GrokTextAdapter<
351
380
 
352
381
  // Emit TEXT_MESSAGE_END if we had text content
353
382
  if (hasEmittedTextMessageStart) {
354
- yield {
383
+ yield asChunk({
355
384
  type: 'TEXT_MESSAGE_END',
356
385
  messageId: aguiState.messageId,
357
386
  model: chunk.model || options.model,
358
387
  timestamp,
359
- }
388
+ })
360
389
  }
361
390
 
362
391
  // Emit AG-UI RUN_FINISHED
363
- yield {
392
+ yield asChunk({
364
393
  type: 'RUN_FINISHED',
365
394
  runId: aguiState.runId,
395
+ threadId: aguiState.threadId,
366
396
  model: chunk.model || options.model,
367
397
  timestamp,
368
398
  usage: chunk.usage
@@ -373,7 +403,7 @@ export class GrokTextAdapter<
373
403
  }
374
404
  : undefined,
375
405
  finishReason: computedFinishReason,
376
- }
406
+ })
377
407
  }
378
408
  }
379
409
  } catch (error: unknown) {
@@ -381,16 +411,18 @@ export class GrokTextAdapter<
381
411
  console.log('[Grok Adapter] Stream ended with error:', err.message)
382
412
 
383
413
  // Emit AG-UI RUN_ERROR
384
- yield {
414
+ yield asChunk({
385
415
  type: 'RUN_ERROR',
386
416
  runId: aguiState.runId,
387
417
  model: options.model,
388
418
  timestamp,
419
+ message: err.message || 'Unknown error occurred',
420
+ code: err.code,
389
421
  error: {
390
422
  message: err.message || 'Unknown error occurred',
391
423
  code: err.code,
392
424
  },
393
- }
425
+ })
394
426
  }
395
427
  }
396
428
 
package/src/index.ts CHANGED
@@ -39,6 +39,7 @@ export type {
39
39
 
40
40
  export type {
41
41
  GrokChatModelProviderOptionsByName,
42
+ GrokChatModelToolCapabilitiesByName,
42
43
  GrokModelInputModalitiesByName,
43
44
  ResolveProviderOptions,
44
45
  ResolveInputModalities,
package/src/model-meta.ts CHANGED
@@ -7,6 +7,7 @@ interface ModelMeta {
7
7
  input: Array<'text' | 'image' | 'audio' | 'video' | 'document'>
8
8
  output: Array<'text' | 'image' | 'audio' | 'video'>
9
9
  capabilities?: Array<'reasoning' | 'tool_calling' | 'structured_outputs'>
10
+ tools?: ReadonlyArray<never>
10
11
  }
11
12
  max_input_tokens?: number
12
13
  max_output_tokens?: number
@@ -30,6 +31,7 @@ const GROK_4_1_FAST_REASONING = {
30
31
  input: ['text', 'image'],
31
32
  output: ['text'],
32
33
  capabilities: ['reasoning', 'structured_outputs', 'tool_calling'],
34
+ tools: [] as const,
33
35
  },
34
36
  pricing: {
35
37
  input: {
@@ -49,6 +51,7 @@ const GROK_4_1_FAST_NON_REASONING = {
49
51
  input: ['text', 'image'],
50
52
  output: ['text'],
51
53
  capabilities: ['structured_outputs', 'tool_calling'],
54
+ tools: [] as const,
52
55
  },
53
56
  pricing: {
54
57
  input: {
@@ -68,6 +71,7 @@ const GROK_CODE_FAST_1 = {
68
71
  input: ['text'],
69
72
  output: ['text'],
70
73
  capabilities: ['reasoning', 'structured_outputs', 'tool_calling'],
74
+ tools: [] as const,
71
75
  },
72
76
  pricing: {
73
77
  input: {
@@ -87,6 +91,7 @@ const GROK_4_FAST_REASONING = {
87
91
  input: ['text', 'image'],
88
92
  output: ['text'],
89
93
  capabilities: ['reasoning', 'structured_outputs', 'tool_calling'],
94
+ tools: [] as const,
90
95
  },
91
96
  pricing: {
92
97
  input: {
@@ -106,6 +111,7 @@ const GROK_4_FAST_NON_REASONING = {
106
111
  input: ['text', 'image'],
107
112
  output: ['text'],
108
113
  capabilities: ['structured_outputs', 'tool_calling'],
114
+ tools: [] as const,
109
115
  },
110
116
  pricing: {
111
117
  input: {
@@ -125,6 +131,7 @@ const GROK_4 = {
125
131
  input: ['text', 'image'],
126
132
  output: ['text'],
127
133
  capabilities: ['reasoning', 'structured_outputs', 'tool_calling'],
134
+ tools: [] as const,
128
135
  },
129
136
  pricing: {
130
137
  input: {
@@ -144,6 +151,7 @@ const GROK_3_MINI = {
144
151
  input: ['text'],
145
152
  output: ['text'],
146
153
  capabilities: ['reasoning', 'structured_outputs', 'tool_calling'],
154
+ tools: [] as const,
147
155
  },
148
156
  pricing: {
149
157
  input: {
@@ -163,6 +171,7 @@ const GROK_3 = {
163
171
  input: ['text'],
164
172
  output: ['text'],
165
173
  capabilities: ['structured_outputs', 'tool_calling'],
174
+ tools: [] as const,
166
175
  },
167
176
  pricing: {
168
177
  input: {
@@ -182,6 +191,7 @@ const GROK_2_VISION = {
182
191
  input: ['text', 'image'],
183
192
  output: ['text'],
184
193
  capabilities: ['structured_outputs', 'tool_calling'],
194
+ tools: [] as const,
185
195
  },
186
196
  pricing: {
187
197
  input: {
@@ -220,6 +230,7 @@ const GROK_4_20 = {
220
230
  input: ['text', 'image', 'document'],
221
231
  output: ['text'],
222
232
  capabilities: ['reasoning', 'structured_outputs', 'tool_calling'],
233
+ tools: [] as const,
223
234
  },
224
235
  pricing: {
225
236
  input: {
@@ -239,6 +250,7 @@ const GROK_4_20_MULTI_AGENT = {
239
250
  input: ['text', 'image', 'document'],
240
251
  output: ['text'],
241
252
  capabilities: ['reasoning', 'structured_outputs', 'tool_calling'],
253
+ tools: [] as const,
242
254
  },
243
255
  pricing: {
244
256
  input: {
@@ -300,6 +312,26 @@ export type GrokChatModelProviderOptionsByName = {
300
312
  [K in (typeof GROK_CHAT_MODELS)[number]]: GrokProviderOptions
301
313
  }
302
314
 
315
+ /**
316
+ * Type-only map from Grok chat model name to its supported provider tools.
317
+ * Grok exposes no provider-specific tool factories, so every model gets an
318
+ * empty tuple. This ensures that passing an Anthropic/OpenAI ProviderTool to
319
+ * a Grok adapter produces a compile-time type error.
320
+ */
321
+ export type GrokChatModelToolCapabilitiesByName = {
322
+ [GROK_4_1_FAST_REASONING.name]: typeof GROK_4_1_FAST_REASONING.supports.tools
323
+ [GROK_4_1_FAST_NON_REASONING.name]: typeof GROK_4_1_FAST_NON_REASONING.supports.tools
324
+ [GROK_CODE_FAST_1.name]: typeof GROK_CODE_FAST_1.supports.tools
325
+ [GROK_4_FAST_REASONING.name]: typeof GROK_4_FAST_REASONING.supports.tools
326
+ [GROK_4_FAST_NON_REASONING.name]: typeof GROK_4_FAST_NON_REASONING.supports.tools
327
+ [GROK_4.name]: typeof GROK_4.supports.tools
328
+ [GROK_3.name]: typeof GROK_3.supports.tools
329
+ [GROK_3_MINI.name]: typeof GROK_3_MINI.supports.tools
330
+ [GROK_2_VISION.name]: typeof GROK_2_VISION.supports.tools
331
+ [GROK_4_20.name]: typeof GROK_4_20.supports.tools
332
+ [GROK_4_20_MULTI_AGENT.name]: typeof GROK_4_20_MULTI_AGENT.supports.tools
333
+ }
334
+
303
335
  /**
304
336
  * Grok-specific provider options
305
337
  * Based on OpenAI-compatible API options