@tanstack/ai-groq 0.1.4 → 0.1.5

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.
@@ -3,6 +3,7 @@ import { validateTextProviderOptions } from "../text/text-provider-options.js";
3
3
  import { makeGroqStructuredOutputCompatible, transformNullsToUndefined } from "../utils/schema-converter.js";
4
4
  import { convertToolsToProviderFormat } from "../tools/tool-converter.js";
5
5
  import { createGroqClient, generateId, getGroqApiKeyFromEnv } from "../utils/client.js";
6
+ const asChunk = (chunk) => chunk;
6
7
  class GroqTextAdapter extends BaseTextAdapter {
7
8
  constructor(config, model) {
8
9
  super({}, model);
@@ -14,7 +15,8 @@ class GroqTextAdapter extends BaseTextAdapter {
14
15
  const requestParams = this.mapTextOptionsToGroq(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 GroqTextAdapter 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);
@@ -120,12 +125,13 @@ class GroqTextAdapter extends BaseTextAdapter {
120
125
  if (!choice) continue;
121
126
  if (!aguiState.hasEmittedRunStarted) {
122
127
  aguiState.hasEmittedRunStarted = true;
123
- yield {
128
+ yield asChunk({
124
129
  type: "RUN_STARTED",
125
130
  runId: aguiState.runId,
131
+ threadId: aguiState.threadId,
126
132
  model: chunk.model || options.model,
127
133
  timestamp
128
- };
134
+ });
129
135
  }
130
136
  const delta = choice.delta;
131
137
  const deltaContent = delta.content;
@@ -133,23 +139,23 @@ class GroqTextAdapter extends BaseTextAdapter {
133
139
  if (deltaContent) {
134
140
  if (!hasEmittedTextMessageStart) {
135
141
  hasEmittedTextMessageStart = true;
136
- yield {
142
+ yield asChunk({
137
143
  type: "TEXT_MESSAGE_START",
138
144
  messageId: aguiState.messageId,
139
145
  model: chunk.model || options.model,
140
146
  timestamp,
141
147
  role: "assistant"
142
- };
148
+ });
143
149
  }
144
150
  accumulatedContent += deltaContent;
145
- yield {
151
+ yield asChunk({
146
152
  type: "TEXT_MESSAGE_CONTENT",
147
153
  messageId: aguiState.messageId,
148
154
  model: chunk.model || options.model,
149
155
  timestamp,
150
156
  delta: deltaContent,
151
157
  content: accumulatedContent
152
- };
158
+ });
153
159
  }
154
160
  if (deltaToolCalls) {
155
161
  for (const toolCallDelta of deltaToolCalls) {
@@ -174,23 +180,24 @@ class GroqTextAdapter extends BaseTextAdapter {
174
180
  }
175
181
  if (toolCall.id && toolCall.name && !toolCall.started) {
176
182
  toolCall.started = true;
177
- yield {
183
+ yield asChunk({
178
184
  type: "TOOL_CALL_START",
179
185
  toolCallId: toolCall.id,
186
+ toolCallName: toolCall.name,
180
187
  toolName: toolCall.name,
181
188
  model: chunk.model || options.model,
182
189
  timestamp,
183
190
  index
184
- };
191
+ });
185
192
  }
186
193
  if (toolCallDelta.function?.arguments && toolCall.started) {
187
- yield {
194
+ yield asChunk({
188
195
  type: "TOOL_CALL_ARGS",
189
196
  toolCallId: toolCall.id,
190
197
  model: chunk.model || options.model,
191
198
  timestamp,
192
199
  delta: toolCallDelta.function.arguments
193
- };
200
+ });
194
201
  }
195
202
  }
196
203
  }
@@ -206,29 +213,31 @@ class GroqTextAdapter extends BaseTextAdapter {
206
213
  } catch {
207
214
  parsedInput = {};
208
215
  }
209
- yield {
216
+ yield asChunk({
210
217
  type: "TOOL_CALL_END",
211
218
  toolCallId: toolCall.id,
219
+ toolCallName: toolCall.name,
212
220
  toolName: toolCall.name,
213
221
  model: chunk.model || options.model,
214
222
  timestamp,
215
223
  input: parsedInput
216
- };
224
+ });
217
225
  }
218
226
  }
219
227
  const computedFinishReason = choice.finish_reason === "tool_calls" || toolCallsInProgress.size > 0 ? "tool_calls" : choice.finish_reason === "length" ? "length" : "stop";
220
228
  if (hasEmittedTextMessageStart) {
221
- yield {
229
+ yield asChunk({
222
230
  type: "TEXT_MESSAGE_END",
223
231
  messageId: aguiState.messageId,
224
232
  model: chunk.model || options.model,
225
233
  timestamp
226
- };
234
+ });
227
235
  }
228
236
  const groqUsage = chunk.x_groq?.usage;
229
- yield {
237
+ yield asChunk({
230
238
  type: "RUN_FINISHED",
231
239
  runId: aguiState.runId,
240
+ threadId: aguiState.threadId,
232
241
  model: chunk.model || options.model,
233
242
  timestamp,
234
243
  usage: groqUsage ? {
@@ -237,22 +246,24 @@ class GroqTextAdapter extends BaseTextAdapter {
237
246
  totalTokens: groqUsage.total_tokens || 0
238
247
  } : void 0,
239
248
  finishReason: computedFinishReason
240
- };
249
+ });
241
250
  }
242
251
  }
243
252
  } catch (error) {
244
253
  const err = error;
245
254
  console.log("[Groq Adapter] Stream ended with error:", err.message);
246
- yield {
255
+ yield asChunk({
247
256
  type: "RUN_ERROR",
248
257
  runId: aguiState.runId,
249
258
  model: options.model,
250
259
  timestamp,
260
+ message: err.message || "Unknown error occurred",
261
+ code: err.code,
251
262
  error: {
252
263
  message: err.message || "Unknown error occurred",
253
264
  code: err.code
254
265
  }
255
- };
266
+ });
256
267
  }
257
268
  }
258
269
  /**
@@ -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 createGroqClient,\n generateId,\n getGroqApiKeyFromEnv,\n makeGroqStructuredOutputCompatible,\n transformNullsToUndefined,\n} from '../utils'\nimport type {\n GROQ_CHAT_MODELS,\n ResolveInputModalities,\n ResolveProviderOptions,\n} from '../model-meta'\nimport type {\n StructuredOutputOptions,\n StructuredOutputResult,\n} from '@tanstack/ai/adapters'\nimport type GROQ_SDK from 'groq-sdk'\nimport type { ChatCompletionCreateParamsStreaming } from 'groq-sdk/resources/chat/completions'\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 ChatCompletionContentPart,\n ChatCompletionMessageParam,\n GroqImageMetadata,\n GroqMessageMetadataByModality,\n} from '../message-types'\nimport type { GroqClientConfig } from '../utils'\n\n/**\n * Configuration for Groq text adapter\n */\nexport interface GroqTextConfig extends GroqClientConfig {}\n\n/**\n * Alias for TextProviderOptions for external use\n */\nexport type { ExternalTextProviderOptions as GroqTextProviderOptions } from '../text/text-provider-options'\n\n/**\n * Groq Text (Chat) Adapter\n *\n * Tree-shakeable adapter for Groq chat/text completion functionality.\n * Uses the Groq SDK which provides an OpenAI-compatible Chat Completions API.\n */\nexport class GroqTextAdapter<\n TModel extends (typeof GROQ_CHAT_MODELS)[number],\n> extends BaseTextAdapter<\n TModel,\n ResolveProviderOptions<TModel>,\n ResolveInputModalities<TModel>,\n GroqMessageMetadataByModality\n> {\n readonly kind = 'text' as const\n readonly name = 'groq' as const\n\n private client: GROQ_SDK\n\n constructor(config: GroqTextConfig, model: TModel) {\n super({}, model)\n this.client = createGroqClient(config)\n }\n\n async *chatStream(\n options: TextOptions<ResolveProviderOptions<TModel>>,\n ): AsyncIterable<StreamChunk> {\n const requestParams = this.mapTextOptionsToGroq(options)\n const timestamp = Date.now()\n\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.processGroqStreamChunks(stream, options, aguiState)\n } catch (error: unknown) {\n const err = error as Error & { code?: string }\n\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 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 Groq's JSON Schema response format.\n * Uses stream: false to get the complete response in one call.\n *\n * Groq has strict requirements for structured output:\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 Groq-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.mapTextOptionsToGroq(chatOptions)\n\n const jsonSchema = makeGroqStructuredOutputCompatible(\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 const rawText = response.choices[0]?.message.content || ''\n\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 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 /**\n * Processes streaming chunks from the Groq API and yields AG-UI stream events.\n * Handles text content deltas, tool call assembly, and lifecycle events.\n */\n private async *processGroqStreamChunks(\n stream: AsyncIterable<GROQ_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 const toolCallsInProgress = new Map<\n number,\n {\n id: string\n name: string\n arguments: string\n started: boolean\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 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 if (deltaContent) {\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 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 if (deltaToolCalls) {\n for (const toolCallDelta of deltaToolCalls) {\n const index = toolCallDelta.index\n\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 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 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 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 if (choice.finish_reason) {\n if (\n choice.finish_reason === 'tool_calls' ||\n toolCallsInProgress.size > 0\n ) {\n for (const [, toolCall] of toolCallsInProgress) {\n if (!toolCall.started || !toolCall.id || !toolCall.name) {\n continue\n }\n\n let parsedInput: unknown = {}\n try {\n parsedInput = toolCall.arguments\n ? JSON.parse(toolCall.arguments)\n : {}\n } catch {\n parsedInput = {}\n }\n\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 : choice.finish_reason === 'length'\n ? 'length'\n : 'stop'\n\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 const groqUsage = chunk.x_groq?.usage\n\n yield {\n type: 'RUN_FINISHED',\n runId: aguiState.runId,\n model: chunk.model || options.model,\n timestamp,\n usage: groqUsage\n ? {\n promptTokens: groqUsage.prompt_tokens || 0,\n completionTokens: groqUsage.completion_tokens || 0,\n totalTokens: groqUsage.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('[Groq Adapter] Stream ended with error:', err.message)\n\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 TextOptions to Groq-specific Chat Completions request parameters.\n */\n private mapTextOptionsToGroq(\n options: TextOptions,\n ): 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 const messages: Array<ChatCompletionMessageParam> = []\n\n if (options.systemPrompts && options.systemPrompts.length > 0) {\n messages.push({\n role: 'system',\n content: options.systemPrompts.join('\\n'),\n })\n }\n\n for (const message of options.messages) {\n messages.push(this.convertMessageToGroq(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,\n stream: true,\n }\n }\n\n /**\n * Converts a TanStack AI ModelMessage to a Groq ChatCompletionMessageParam.\n * Handles tool, assistant, and user messages including multimodal content.\n */\n private convertMessageToGroq(\n message: ModelMessage,\n ): ChatCompletionMessageParam {\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 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 const contentParts = this.normalizeContent(message.content)\n\n if (contentParts.length === 1 && contentParts[0]?.type === 'text') {\n return {\n role: 'user',\n content: contentParts[0].content,\n }\n }\n\n const parts: Array<ChatCompletionContentPart> = []\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 GroqImageMetadata | undefined\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 return content\n .filter((p) => p.type === 'text')\n .map((p) => p.content)\n .join('')\n }\n}\n\n/**\n * Creates a Groq text adapter with explicit API key.\n * Type resolution happens here at the call site.\n *\n * @param model - The model name (e.g., 'llama-3.3-70b-versatile', 'openai/gpt-oss-120b')\n * @param apiKey - Your Groq API key\n * @param config - Optional additional configuration\n * @returns Configured Groq text adapter instance with resolved types\n *\n * @example\n * ```typescript\n * const adapter = createGroqText('llama-3.3-70b-versatile', \"gsk_...\");\n * // adapter has type-safe providerOptions for llama-3.3-70b-versatile\n * ```\n */\nexport function createGroqText<\n TModel extends (typeof GROQ_CHAT_MODELS)[number],\n>(\n model: TModel,\n apiKey: string,\n config?: Omit<GroqTextConfig, 'apiKey'>,\n): GroqTextAdapter<TModel> {\n return new GroqTextAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates a Groq text adapter with automatic API key detection from environment variables.\n * Type resolution happens here at the call site.\n *\n * Looks for `GROQ_API_KEY` in:\n * - `process.env` (Node.js)\n * - `window.env` (Browser with injected env)\n *\n * @param model - The model name (e.g., 'llama-3.3-70b-versatile', 'openai/gpt-oss-120b')\n * @param config - Optional configuration (excluding apiKey which is auto-detected)\n * @returns Configured Groq text adapter instance with resolved types\n * @throws Error if GROQ_API_KEY is not found in environment\n *\n * @example\n * ```typescript\n * // Automatically uses GROQ_API_KEY from environment\n * const adapter = groqText('llama-3.3-70b-versatile');\n *\n * const stream = chat({\n * adapter,\n * messages: [{ role: \"user\", content: \"Hello!\" }]\n * });\n * ```\n */\nexport function groqText<TModel extends (typeof GROQ_CHAT_MODELS)[number]>(\n model: TModel,\n config?: Omit<GroqTextConfig, 'apiKey'>,\n): GroqTextAdapter<TModel> {\n const apiKey = getGroqApiKeyFromEnv()\n return createGroqText(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 createGroqClient,\n generateId,\n getGroqApiKeyFromEnv,\n makeGroqStructuredOutputCompatible,\n transformNullsToUndefined,\n} from '../utils'\nimport type {\n GROQ_CHAT_MODELS,\n ResolveInputModalities,\n ResolveProviderOptions,\n} from '../model-meta'\nimport type {\n StructuredOutputOptions,\n StructuredOutputResult,\n} from '@tanstack/ai/adapters'\nimport type GROQ_SDK from 'groq-sdk'\nimport type { ChatCompletionCreateParamsStreaming } from 'groq-sdk/resources/chat/completions'\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 ChatCompletionContentPart,\n ChatCompletionMessageParam,\n GroqImageMetadata,\n GroqMessageMetadataByModality,\n} from '../message-types'\nimport type { GroqClientConfig } from '../utils'\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 Groq text adapter\n */\nexport interface GroqTextConfig extends GroqClientConfig {}\n\n/**\n * Alias for TextProviderOptions for external use\n */\nexport type { ExternalTextProviderOptions as GroqTextProviderOptions } from '../text/text-provider-options'\n\n/**\n * Groq Text (Chat) Adapter\n *\n * Tree-shakeable adapter for Groq chat/text completion functionality.\n * Uses the Groq SDK which provides an OpenAI-compatible Chat Completions API.\n */\nexport class GroqTextAdapter<\n TModel extends (typeof GROQ_CHAT_MODELS)[number],\n> extends BaseTextAdapter<\n TModel,\n ResolveProviderOptions<TModel>,\n ResolveInputModalities<TModel>,\n GroqMessageMetadataByModality\n> {\n readonly kind = 'text' as const\n readonly name = 'groq' as const\n\n private client: GROQ_SDK\n\n constructor(config: GroqTextConfig, model: TModel) {\n super({}, model)\n this.client = createGroqClient(config)\n }\n\n async *chatStream(\n options: TextOptions<ResolveProviderOptions<TModel>>,\n ): AsyncIterable<StreamChunk> {\n const requestParams = this.mapTextOptionsToGroq(options)\n const timestamp = Date.now()\n\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.processGroqStreamChunks(stream, options, aguiState)\n } catch (error: unknown) {\n const err = error as Error & { code?: string }\n\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 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 Groq's JSON Schema response format.\n * Uses stream: false to get the complete response in one call.\n *\n * Groq has strict requirements for structured output:\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 Groq-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.mapTextOptionsToGroq(chatOptions)\n\n const jsonSchema = makeGroqStructuredOutputCompatible(\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 const rawText = response.choices[0]?.message.content || ''\n\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 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 /**\n * Processes streaming chunks from the Groq API and yields AG-UI stream events.\n * Handles text content deltas, tool call assembly, and lifecycle events.\n */\n private async *processGroqStreamChunks(\n stream: AsyncIterable<GROQ_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 const toolCallsInProgress = new Map<\n number,\n {\n id: string\n name: string\n arguments: string\n started: boolean\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 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 if (deltaContent) {\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 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 if (deltaToolCalls) {\n for (const toolCallDelta of deltaToolCalls) {\n const index = toolCallDelta.index\n\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 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 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 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 if (choice.finish_reason) {\n if (\n choice.finish_reason === 'tool_calls' ||\n toolCallsInProgress.size > 0\n ) {\n for (const [, toolCall] of toolCallsInProgress) {\n if (!toolCall.started || !toolCall.id || !toolCall.name) {\n continue\n }\n\n let parsedInput: unknown = {}\n try {\n parsedInput = toolCall.arguments\n ? JSON.parse(toolCall.arguments)\n : {}\n } catch {\n parsedInput = {}\n }\n\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 : choice.finish_reason === 'length'\n ? 'length'\n : 'stop'\n\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 const groqUsage = chunk.x_groq?.usage\n\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: groqUsage\n ? {\n promptTokens: groqUsage.prompt_tokens || 0,\n completionTokens: groqUsage.completion_tokens || 0,\n totalTokens: groqUsage.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('[Groq Adapter] Stream ended with error:', err.message)\n\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 TextOptions to Groq-specific Chat Completions request parameters.\n */\n private mapTextOptionsToGroq(\n options: TextOptions,\n ): 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 const messages: Array<ChatCompletionMessageParam> = []\n\n if (options.systemPrompts && options.systemPrompts.length > 0) {\n messages.push({\n role: 'system',\n content: options.systemPrompts.join('\\n'),\n })\n }\n\n for (const message of options.messages) {\n messages.push(this.convertMessageToGroq(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,\n stream: true,\n }\n }\n\n /**\n * Converts a TanStack AI ModelMessage to a Groq ChatCompletionMessageParam.\n * Handles tool, assistant, and user messages including multimodal content.\n */\n private convertMessageToGroq(\n message: ModelMessage,\n ): ChatCompletionMessageParam {\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 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 const contentParts = this.normalizeContent(message.content)\n\n if (contentParts.length === 1 && contentParts[0]?.type === 'text') {\n return {\n role: 'user',\n content: contentParts[0].content,\n }\n }\n\n const parts: Array<ChatCompletionContentPart> = []\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 GroqImageMetadata | undefined\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 return content\n .filter((p) => p.type === 'text')\n .map((p) => p.content)\n .join('')\n }\n}\n\n/**\n * Creates a Groq text adapter with explicit API key.\n * Type resolution happens here at the call site.\n *\n * @param model - The model name (e.g., 'llama-3.3-70b-versatile', 'openai/gpt-oss-120b')\n * @param apiKey - Your Groq API key\n * @param config - Optional additional configuration\n * @returns Configured Groq text adapter instance with resolved types\n *\n * @example\n * ```typescript\n * const adapter = createGroqText('llama-3.3-70b-versatile', \"gsk_...\");\n * // adapter has type-safe providerOptions for llama-3.3-70b-versatile\n * ```\n */\nexport function createGroqText<\n TModel extends (typeof GROQ_CHAT_MODELS)[number],\n>(\n model: TModel,\n apiKey: string,\n config?: Omit<GroqTextConfig, 'apiKey'>,\n): GroqTextAdapter<TModel> {\n return new GroqTextAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates a Groq text adapter with automatic API key detection from environment variables.\n * Type resolution happens here at the call site.\n *\n * Looks for `GROQ_API_KEY` in:\n * - `process.env` (Node.js)\n * - `window.env` (Browser with injected env)\n *\n * @param model - The model name (e.g., 'llama-3.3-70b-versatile', 'openai/gpt-oss-120b')\n * @param config - Optional configuration (excluding apiKey which is auto-detected)\n * @returns Configured Groq text adapter instance with resolved types\n * @throws Error if GROQ_API_KEY is not found in environment\n *\n * @example\n * ```typescript\n * // Automatically uses GROQ_API_KEY from environment\n * const adapter = groqText('llama-3.3-70b-versatile');\n *\n * const stream = chat({\n * adapter,\n * messages: [{ role: \"user\", content: \"Hello!\" }]\n * });\n * ```\n */\nexport function groqText<TModel extends (typeof GROQ_CHAT_MODELS)[number]>(\n model: TModel,\n config?: Omit<GroqTextConfig, 'apiKey'>,\n): GroqTextAdapter<TModel> {\n const apiKey = getGroqApiKeyFromEnv()\n return createGroqText(model, apiKey, config)\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/ai-groq",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "type": "module",
5
5
  "description": "Groq adapter for TanStack AI",
6
6
  "author": "",
@@ -34,7 +34,7 @@
34
34
  },
35
35
  "peerDependencies": {
36
36
  "zod": "^4.0.0",
37
- "@tanstack/ai": "^0.10.0"
37
+ "@tanstack/ai": "^0.11.0"
38
38
  },
39
39
  "dependencies": {
40
40
  "groq-sdk": "^0.37.0"
@@ -34,6 +34,11 @@ import type {
34
34
  } from '../message-types'
35
35
  import type { GroqClientConfig } from '../utils'
36
36
 
37
+ /** Cast an event object to StreamChunk. Adapters construct events with string
38
+ * literal types which are structurally compatible with the EventType enum. */
39
+ const asChunk = (chunk: Record<string, unknown>) =>
40
+ chunk as unknown as StreamChunk
41
+
37
42
  /**
38
43
  * Configuration for Groq text adapter
39
44
  */
@@ -75,7 +80,8 @@ export class GroqTextAdapter<
75
80
  const timestamp = Date.now()
76
81
 
77
82
  const aguiState = {
78
- runId: generateId(this.name),
83
+ runId: options.runId ?? generateId(this.name),
84
+ threadId: options.threadId ?? generateId(this.name),
79
85
  messageId: generateId(this.name),
80
86
  timestamp,
81
87
  hasEmittedRunStarted: false,
@@ -93,24 +99,27 @@ export class GroqTextAdapter<
93
99
 
94
100
  if (!aguiState.hasEmittedRunStarted) {
95
101
  aguiState.hasEmittedRunStarted = true
96
- yield {
102
+ yield asChunk({
97
103
  type: 'RUN_STARTED',
98
104
  runId: aguiState.runId,
105
+ threadId: aguiState.threadId,
99
106
  model: options.model,
100
107
  timestamp,
101
- }
108
+ })
102
109
  }
103
110
 
104
- yield {
111
+ yield asChunk({
105
112
  type: 'RUN_ERROR',
106
113
  runId: aguiState.runId,
107
114
  model: options.model,
108
115
  timestamp,
116
+ message: err.message || 'Unknown error',
117
+ code: err.code,
109
118
  error: {
110
119
  message: err.message || 'Unknown error',
111
120
  code: err.code,
112
121
  },
113
- }
122
+ })
114
123
 
115
124
  console.error('>>> chatStream: Fatal error during response creation <<<')
116
125
  console.error('>>> Error message:', err.message)
@@ -190,6 +199,7 @@ export class GroqTextAdapter<
190
199
  options: TextOptions,
191
200
  aguiState: {
192
201
  runId: string
202
+ threadId: string
193
203
  messageId: string
194
204
  timestamp: number
195
205
  hasEmittedRunStarted: boolean
@@ -217,12 +227,13 @@ export class GroqTextAdapter<
217
227
 
218
228
  if (!aguiState.hasEmittedRunStarted) {
219
229
  aguiState.hasEmittedRunStarted = true
220
- yield {
230
+ yield asChunk({
221
231
  type: 'RUN_STARTED',
222
232
  runId: aguiState.runId,
233
+ threadId: aguiState.threadId,
223
234
  model: chunk.model || options.model,
224
235
  timestamp,
225
- }
236
+ })
226
237
  }
227
238
 
228
239
  const delta = choice.delta
@@ -232,25 +243,25 @@ export class GroqTextAdapter<
232
243
  if (deltaContent) {
233
244
  if (!hasEmittedTextMessageStart) {
234
245
  hasEmittedTextMessageStart = true
235
- yield {
246
+ yield asChunk({
236
247
  type: 'TEXT_MESSAGE_START',
237
248
  messageId: aguiState.messageId,
238
249
  model: chunk.model || options.model,
239
250
  timestamp,
240
251
  role: 'assistant',
241
- }
252
+ })
242
253
  }
243
254
 
244
255
  accumulatedContent += deltaContent
245
256
 
246
- yield {
257
+ yield asChunk({
247
258
  type: 'TEXT_MESSAGE_CONTENT',
248
259
  messageId: aguiState.messageId,
249
260
  model: chunk.model || options.model,
250
261
  timestamp,
251
262
  delta: deltaContent,
252
263
  content: accumulatedContent,
253
- }
264
+ })
254
265
  }
255
266
 
256
267
  if (deltaToolCalls) {
@@ -280,24 +291,25 @@ export class GroqTextAdapter<
280
291
 
281
292
  if (toolCall.id && toolCall.name && !toolCall.started) {
282
293
  toolCall.started = true
283
- yield {
294
+ yield asChunk({
284
295
  type: 'TOOL_CALL_START',
285
296
  toolCallId: toolCall.id,
297
+ toolCallName: toolCall.name,
286
298
  toolName: toolCall.name,
287
299
  model: chunk.model || options.model,
288
300
  timestamp,
289
301
  index,
290
- }
302
+ })
291
303
  }
292
304
 
293
305
  if (toolCallDelta.function?.arguments && toolCall.started) {
294
- yield {
306
+ yield asChunk({
295
307
  type: 'TOOL_CALL_ARGS',
296
308
  toolCallId: toolCall.id,
297
309
  model: chunk.model || options.model,
298
310
  timestamp,
299
311
  delta: toolCallDelta.function.arguments,
300
- }
312
+ })
301
313
  }
302
314
  }
303
315
  }
@@ -321,14 +333,15 @@ export class GroqTextAdapter<
321
333
  parsedInput = {}
322
334
  }
323
335
 
324
- yield {
336
+ yield asChunk({
325
337
  type: 'TOOL_CALL_END',
326
338
  toolCallId: toolCall.id,
339
+ toolCallName: toolCall.name,
327
340
  toolName: toolCall.name,
328
341
  model: chunk.model || options.model,
329
342
  timestamp,
330
343
  input: parsedInput,
331
- }
344
+ })
332
345
  }
333
346
  }
334
347
 
@@ -341,19 +354,20 @@ export class GroqTextAdapter<
341
354
  : 'stop'
342
355
 
343
356
  if (hasEmittedTextMessageStart) {
344
- yield {
357
+ yield asChunk({
345
358
  type: 'TEXT_MESSAGE_END',
346
359
  messageId: aguiState.messageId,
347
360
  model: chunk.model || options.model,
348
361
  timestamp,
349
- }
362
+ })
350
363
  }
351
364
 
352
365
  const groqUsage = chunk.x_groq?.usage
353
366
 
354
- yield {
367
+ yield asChunk({
355
368
  type: 'RUN_FINISHED',
356
369
  runId: aguiState.runId,
370
+ threadId: aguiState.threadId,
357
371
  model: chunk.model || options.model,
358
372
  timestamp,
359
373
  usage: groqUsage
@@ -364,23 +378,25 @@ export class GroqTextAdapter<
364
378
  }
365
379
  : undefined,
366
380
  finishReason: computedFinishReason,
367
- }
381
+ })
368
382
  }
369
383
  }
370
384
  } catch (error: unknown) {
371
385
  const err = error as Error & { code?: string }
372
386
  console.log('[Groq Adapter] Stream ended with error:', err.message)
373
387
 
374
- yield {
388
+ yield asChunk({
375
389
  type: 'RUN_ERROR',
376
390
  runId: aguiState.runId,
377
391
  model: options.model,
378
392
  timestamp,
393
+ message: err.message || 'Unknown error occurred',
394
+ code: err.code,
379
395
  error: {
380
396
  message: err.message || 'Unknown error occurred',
381
397
  code: err.code,
382
398
  },
383
- }
399
+ })
384
400
  }
385
401
  }
386
402