@tanstack/ai-groq 0.5.1 → 0.5.2

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,7 +3,7 @@ import { OpenAIBaseChatCompletionsTextAdapter } from '@tanstack/openai-base';
3
3
  import { Modality, TextOptions } from '@tanstack/ai';
4
4
  import { GROQ_CHAT_MODELS, GroqChatModelToolCapabilitiesByName, ResolveInputModalities, ResolveProviderOptions } from '../model-meta.js';
5
5
  import { GroqMessageMetadataByModality } from '../message-types.js';
6
- import { GroqClientConfig } from '../utils.js';
6
+ import { GroqClientConfig } from '../utils/client.js';
7
7
  type ResolveToolCapabilities<TModel extends string> = TModel extends keyof GroqChatModelToolCapabilitiesByName ? NonNullable<GroqChatModelToolCapabilitiesByName[TModel]> : readonly [];
8
8
  /**
9
9
  * Configuration for Groq text adapter
@@ -1 +1 @@
1
- {"version":3,"file":"text.js","sources":["../../../src/adapters/text.ts"],"sourcesContent":["import OpenAI from 'openai'\nimport { OpenAIBaseChatCompletionsTextAdapter } from '@tanstack/openai-base'\nimport { getGroqApiKeyFromEnv, withGroqDefaults } from '../utils/client'\nimport { makeGroqStructuredOutputCompatible } from '../utils/schema-converter'\nimport type { Modality, TextOptions } from '@tanstack/ai'\nimport type {\n GROQ_CHAT_MODELS,\n GroqChatModelToolCapabilitiesByName,\n ResolveInputModalities,\n ResolveProviderOptions,\n} from '../model-meta'\nimport type { GroqMessageMetadataByModality } from '../message-types'\nimport type { GroqClientConfig } from '../utils'\n\ntype ResolveToolCapabilities<TModel extends string> =\n TModel extends keyof GroqChatModelToolCapabilitiesByName\n ? NonNullable<GroqChatModelToolCapabilitiesByName[TModel]>\n : readonly []\n\n/**\n * Configuration for Groq text adapter\n */\nexport interface GroqTextConfig extends GroqClientConfig {}\n\n/**\n * Re-export of the public provider options type\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. Groq exposes an\n * OpenAI-compatible Chat Completions endpoint at `/openai/v1`, so we drive\n * it with the OpenAI SDK via a `baseURL` override (the same pattern as\n * `ai-grok`).\n *\n * Quirk: when usage is present on a stream, Groq historically delivered it\n * under `chunk.x_groq.usage` rather than `chunk.usage`. The override below\n * promotes it to the standard location so the base's RUN_FINISHED usage\n * accounting works unchanged.\n */\nexport class GroqTextAdapter<\n TModel extends (typeof GROQ_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 OpenAIBaseChatCompletionsTextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n GroqMessageMetadataByModality,\n TToolCapabilities\n> {\n override readonly kind = 'text' as const\n override readonly name = 'groq' as const\n\n constructor(config: GroqTextConfig, model: TModel) {\n super(model, 'groq', new OpenAI(withGroqDefaults(config)))\n }\n\n protected override makeStructuredOutputCompatible(\n schema: Record<string, any>,\n originalRequired?: Array<string>,\n ): Record<string, any> {\n return makeGroqStructuredOutputCompatible(schema, originalRequired)\n }\n\n protected override async *processStreamChunks(\n stream: AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk>,\n options: TextOptions,\n aguiState: {\n runId: string\n threadId: string\n messageId: string\n hasEmittedRunStarted: boolean\n },\n ) {\n yield* super.processStreamChunks(\n promoteGroqUsage(stream),\n options,\n aguiState,\n )\n }\n\n /**\n * Surfaces Groq's reasoning deltas during streaming structured output.\n * Groq emits `delta.reasoning` (or legacy `delta.reasoning_content`) on\n * reasoning models when the caller sets `reasoning_format: 'parsed'` in\n * modelOptions. The base's chatStream and structuredOutputStream both\n * route reasoning through this hook.\n */\n protected override extractReasoning(\n chunk: OpenAI.Chat.Completions.ChatCompletionChunk,\n ): { text: string } | undefined {\n const delta = chunk.choices[0]?.delta as\n | { reasoning?: unknown; reasoning_content?: unknown }\n | undefined\n const raw = delta?.reasoning ?? delta?.reasoning_content\n if (typeof raw === 'string' && raw.length > 0) {\n return { text: raw }\n }\n return undefined\n }\n\n /**\n * Groq's API rejects `response_format: json_schema` together with `tools`\n * + `stream` (returns 400 — see Groq Structured Outputs docs:\n * \"Streaming and tool use are not currently supported with Structured\n * Outputs.\"). Force the engine onto the legacy finalization path even\n * though the OpenAI Chat Completions base would otherwise opt in.\n */\n override supportsCombinedToolsAndSchema(): boolean {\n return false\n }\n}\n\n/**\n * Promotes Groq's non-standard `x_groq.usage` to the standard `chunk.usage`\n * slot the base reads. Pass-through for chunks that already carry usage at\n * the documented location.\n */\nasync function* promoteGroqUsage(\n stream: AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk>,\n): AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk> {\n for await (const chunk of stream) {\n const groqChunk = chunk as typeof chunk & {\n x_groq?: { usage?: OpenAI.Chat.Completions.ChatCompletionChunk['usage'] }\n }\n if (!chunk.usage && groqChunk.x_groq?.usage) {\n yield { ...chunk, usage: groqChunk.x_groq.usage }\n } else {\n yield chunk\n }\n }\n}\n\n/**\n * Creates a Groq text adapter with explicit API key.\n *\n * @example\n * ```typescript\n * const adapter = createGroqText('llama-3.3-70b-versatile', \"gsk_...\");\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 API key from `GROQ_API_KEY`.\n *\n * @example\n * ```typescript\n * const adapter = groqText('llama-3.3-70b-versatile');\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":";;;;AA0CO,MAAM,wBAOH,qCAMR;AAAA,EACkB,OAAO;AAAA,EACP,OAAO;AAAA,EAEzB,YAAY,QAAwB,OAAe;AACjD,UAAM,OAAO,QAAQ,IAAI,OAAO,iBAAiB,MAAM,CAAC,CAAC;AAAA,EAC3D;AAAA,EAEmB,+BACjB,QACA,kBACqB;AACrB,WAAO,mCAAmC,QAAQ,gBAAgB;AAAA,EACpE;AAAA,EAEA,OAA0B,oBACxB,QACA,SACA,WAMA;AACA,WAAO,MAAM;AAAA,MACX,iBAAiB,MAAM;AAAA,MACvB;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASmB,iBACjB,OAC8B;AAC9B,UAAM,QAAQ,MAAM,QAAQ,CAAC,GAAG;AAGhC,UAAM,MAAM,OAAO,aAAa,OAAO;AACvC,QAAI,OAAO,QAAQ,YAAY,IAAI,SAAS,GAAG;AAC7C,aAAO,EAAE,MAAM,IAAA;AAAA,IACjB;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASS,iCAA0C;AACjD,WAAO;AAAA,EACT;AACF;AAOA,gBAAgB,iBACd,QAC4D;AAC5D,mBAAiB,SAAS,QAAQ;AAChC,UAAM,YAAY;AAGlB,QAAI,CAAC,MAAM,SAAS,UAAU,QAAQ,OAAO;AAC3C,YAAM,EAAE,GAAG,OAAO,OAAO,UAAU,OAAO,MAAA;AAAA,IAC5C,OAAO;AACL,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAUO,SAAS,eAGd,OACA,QACA,QACyB;AACzB,SAAO,IAAI,gBAAgB,EAAE,QAAQ,GAAG,OAAA,GAAU,KAAK;AACzD;AAUO,SAAS,SACd,OACA,QACyB;AACzB,QAAM,SAAS,qBAAA;AACf,SAAO,eAAe,OAAO,QAAQ,MAAM;AAC7C;"}
1
+ {"version":3,"file":"text.js","sources":["../../../src/adapters/text.ts"],"sourcesContent":["import OpenAI from 'openai'\nimport { OpenAIBaseChatCompletionsTextAdapter } from '@tanstack/openai-base'\nimport { getGroqApiKeyFromEnv, withGroqDefaults } from '../utils/client'\nimport { makeGroqStructuredOutputCompatible } from '../utils/schema-converter'\nimport type { Modality, TextOptions } from '@tanstack/ai'\nimport type {\n GROQ_CHAT_MODELS,\n GroqChatModelToolCapabilitiesByName,\n ResolveInputModalities,\n ResolveProviderOptions,\n} from '../model-meta'\nimport type { GroqMessageMetadataByModality } from '../message-types'\nimport type { GroqClientConfig } from '../utils/client'\n\ntype ResolveToolCapabilities<TModel extends string> =\n TModel extends keyof GroqChatModelToolCapabilitiesByName\n ? NonNullable<GroqChatModelToolCapabilitiesByName[TModel]>\n : readonly []\n\n/**\n * Configuration for Groq text adapter\n */\nexport interface GroqTextConfig extends GroqClientConfig {}\n\n/**\n * Re-export of the public provider options type\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. Groq exposes an\n * OpenAI-compatible Chat Completions endpoint at `/openai/v1`, so we drive\n * it with the OpenAI SDK via a `baseURL` override (the same pattern as\n * `ai-grok`).\n *\n * Quirk: when usage is present on a stream, Groq historically delivered it\n * under `chunk.x_groq.usage` rather than `chunk.usage`. The override below\n * promotes it to the standard location so the base's RUN_FINISHED usage\n * accounting works unchanged.\n */\nexport class GroqTextAdapter<\n TModel extends (typeof GROQ_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 OpenAIBaseChatCompletionsTextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n GroqMessageMetadataByModality,\n TToolCapabilities\n> {\n override readonly kind = 'text' as const\n override readonly name = 'groq' as const\n\n constructor(config: GroqTextConfig, model: TModel) {\n super(model, 'groq', new OpenAI(withGroqDefaults(config)))\n }\n\n protected override makeStructuredOutputCompatible(\n schema: Record<string, any>,\n originalRequired?: Array<string>,\n ): Record<string, any> {\n return makeGroqStructuredOutputCompatible(schema, originalRequired)\n }\n\n protected override async *processStreamChunks(\n stream: AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk>,\n options: TextOptions,\n aguiState: {\n runId: string\n threadId: string\n messageId: string\n hasEmittedRunStarted: boolean\n },\n ) {\n yield* super.processStreamChunks(\n promoteGroqUsage(stream),\n options,\n aguiState,\n )\n }\n\n /**\n * Surfaces Groq's reasoning deltas during streaming structured output.\n * Groq emits `delta.reasoning` (or legacy `delta.reasoning_content`) on\n * reasoning models when the caller sets `reasoning_format: 'parsed'` in\n * modelOptions. The base's chatStream and structuredOutputStream both\n * route reasoning through this hook.\n */\n protected override extractReasoning(\n chunk: OpenAI.Chat.Completions.ChatCompletionChunk,\n ): { text: string } | undefined {\n const delta = chunk.choices[0]?.delta as\n | { reasoning?: unknown; reasoning_content?: unknown }\n | undefined\n const raw = delta?.reasoning ?? delta?.reasoning_content\n if (typeof raw === 'string' && raw.length > 0) {\n return { text: raw }\n }\n return undefined\n }\n\n /**\n * Groq's API rejects `response_format: json_schema` together with `tools`\n * + `stream` (returns 400 — see Groq Structured Outputs docs:\n * \"Streaming and tool use are not currently supported with Structured\n * Outputs.\"). Force the engine onto the legacy finalization path even\n * though the OpenAI Chat Completions base would otherwise opt in.\n */\n override supportsCombinedToolsAndSchema(): boolean {\n return false\n }\n}\n\n/**\n * Promotes Groq's non-standard `x_groq.usage` to the standard `chunk.usage`\n * slot the base reads. Pass-through for chunks that already carry usage at\n * the documented location.\n */\nasync function* promoteGroqUsage(\n stream: AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk>,\n): AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk> {\n for await (const chunk of stream) {\n const groqChunk = chunk as typeof chunk & {\n x_groq?: { usage?: OpenAI.Chat.Completions.ChatCompletionChunk['usage'] }\n }\n if (!chunk.usage && groqChunk.x_groq?.usage) {\n yield { ...chunk, usage: groqChunk.x_groq.usage }\n } else {\n yield chunk\n }\n }\n}\n\n/**\n * Creates a Groq text adapter with explicit API key.\n *\n * @example\n * ```typescript\n * const adapter = createGroqText('llama-3.3-70b-versatile', \"gsk_...\");\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 API key from `GROQ_API_KEY`.\n *\n * @example\n * ```typescript\n * const adapter = groqText('llama-3.3-70b-versatile');\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":";;;;AA0CO,MAAM,wBAOH,qCAMR;AAAA,EACkB,OAAO;AAAA,EACP,OAAO;AAAA,EAEzB,YAAY,QAAwB,OAAe;AACjD,UAAM,OAAO,QAAQ,IAAI,OAAO,iBAAiB,MAAM,CAAC,CAAC;AAAA,EAC3D;AAAA,EAEmB,+BACjB,QACA,kBACqB;AACrB,WAAO,mCAAmC,QAAQ,gBAAgB;AAAA,EACpE;AAAA,EAEA,OAA0B,oBACxB,QACA,SACA,WAMA;AACA,WAAO,MAAM;AAAA,MACX,iBAAiB,MAAM;AAAA,MACvB;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASmB,iBACjB,OAC8B;AAC9B,UAAM,QAAQ,MAAM,QAAQ,CAAC,GAAG;AAGhC,UAAM,MAAM,OAAO,aAAa,OAAO;AACvC,QAAI,OAAO,QAAQ,YAAY,IAAI,SAAS,GAAG;AAC7C,aAAO,EAAE,MAAM,IAAA;AAAA,IACjB;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASS,iCAA0C;AACjD,WAAO;AAAA,EACT;AACF;AAOA,gBAAgB,iBACd,QAC4D;AAC5D,mBAAiB,SAAS,QAAQ;AAChC,UAAM,YAAY;AAGlB,QAAI,CAAC,MAAM,SAAS,UAAU,QAAQ,OAAO;AAC3C,YAAM,EAAE,GAAG,OAAO,OAAO,UAAU,OAAO,MAAA;AAAA,IAC5C,OAAO;AACL,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAUO,SAAS,eAGd,OACA,QACA,QACyB;AACzB,SAAO,IAAI,gBAAgB,EAAE,QAAQ,GAAG,OAAA,GAAU,KAAK;AACzD;AAUO,SAAS,SACd,OACA,QACyB;AACzB,QAAM,SAAS,qBAAA;AACf,SAAO,eAAe,OAAO,QAAQ,MAAM;AAC7C;"}
@@ -3,7 +3,7 @@ import { BaseTTSAdapter } from '@tanstack/ai/adapters';
3
3
  import { TTSOptions, TTSResult } from '@tanstack/ai';
4
4
  import { GroqTTSModel } from '../model-meta.js';
5
5
  import { GroqTTSProviderOptions } from '../audio/tts-provider-options.js';
6
- import { GroqClientConfig } from '../utils.js';
6
+ import { GroqClientConfig } from '../utils/client.js';
7
7
  /**
8
8
  * Configuration for Groq TTS adapter
9
9
  */
@@ -1 +1 @@
1
- {"version":3,"file":"tts.js","sources":["../../../src/adapters/tts.ts"],"sourcesContent":["import OpenAI from 'openai'\nimport { BaseTTSAdapter } from '@tanstack/ai/adapters'\nimport { toRunErrorPayload } from '@tanstack/ai/adapter-internals'\nimport { arrayBufferToBase64, generateId } from '@tanstack/ai-utils'\nimport { getGroqApiKeyFromEnv, withGroqDefaults } from '../utils/client'\nimport { validateAudioInput } from '../audio/audio-provider-options'\nimport type { TTSOptions, TTSResult } from '@tanstack/ai'\nimport type OpenAI_SDK from 'openai'\nimport type { GroqTTSModel } from '../model-meta'\nimport type { GroqTTSProviderOptions } from '../audio/tts-provider-options'\nimport type { GroqClientConfig } from '../utils'\n\n/**\n * Configuration for Groq TTS adapter\n */\nexport interface GroqTTSConfig extends GroqClientConfig {}\n\n/**\n * Groq Text-to-Speech Adapter\n *\n * Tree-shakeable adapter for Groq TTS functionality. Groq exposes an\n * OpenAI-compatible `/audio/speech` endpoint, so the adapter drives it with\n * the OpenAI SDK via a `baseURL` override (the same pattern as the Groq text\n * adapter).\n *\n * Supports `canopylabs/orpheus-v1-english` and\n * `canopylabs/orpheus-arabic-saudi`.\n *\n * Features:\n * - English voices: autumn(f), diana(f), hannah(f), austin(m), daniel(m), troy(m)\n * - Arabic voices: fahad(m), sultan(m), lulwa(f), noura(f)\n * - Output formats: flac, mp3, mulaw, ogg, wav (default wav)\n * - Speed control\n * - Configurable sample rate via `modelOptions`\n */\nexport class GroqTTSAdapter<TModel extends GroqTTSModel> extends BaseTTSAdapter<\n TModel,\n GroqTTSProviderOptions\n> {\n readonly name = 'groq' as const\n\n protected client: OpenAI\n\n constructor(config: GroqTTSConfig, model: TModel) {\n super(model, {})\n this.client = new OpenAI(withGroqDefaults(config))\n }\n\n async generateSpeech(\n options: TTSOptions<GroqTTSProviderOptions>,\n ): Promise<TTSResult> {\n const { model, text, voice, format, speed, modelOptions } = options\n\n validateAudioInput({ input: text, model: this.model })\n\n // Spreading optional inputs conditionally keeps the request compatible\n // with the vendor SDK shape under exactOptionalPropertyTypes. `sample_rate`\n // is a Groq-only body field carried via modelOptions.\n const request: OpenAI_SDK.Audio.SpeechCreateParams = {\n model,\n input: text,\n voice: voice ?? 'autumn',\n response_format: format ?? 'wav',\n ...(speed !== undefined && { speed }),\n ...(modelOptions ?? {}),\n }\n\n try {\n options.logger.request(\n `activity=tts provider=${this.name} model=${model} format=${request.response_format ?? 'default'} voice=${request.voice}`,\n { provider: this.name, model },\n )\n const response = await this.client.audio.speech.create(request)\n\n const arrayBuffer = await response.arrayBuffer()\n const base64 = arrayBufferToBase64(arrayBuffer)\n\n const outputFormat = request.response_format ?? 'wav'\n const contentType = this.getContentType(outputFormat)\n\n return {\n id: generateId(this.name),\n model,\n audio: base64,\n format: outputFormat,\n contentType,\n }\n } catch (error: unknown) {\n // Narrow before logging: raw SDK errors can carry request metadata\n // (including auth headers) which we must never surface to user loggers.\n options.logger.errors(`${this.name}.generateSpeech fatal`, {\n error: toRunErrorPayload(error, `${this.name}.generateSpeech failed`),\n source: `${this.name}.generateSpeech`,\n })\n throw error\n }\n }\n\n private getContentType(format: string): string {\n const contentTypes: Record<string, string> = {\n flac: 'audio/flac',\n mp3: 'audio/mpeg',\n mulaw: 'audio/basic',\n ogg: 'audio/ogg',\n wav: 'audio/wav',\n }\n return contentTypes[format] || 'audio/wav'\n }\n}\n\n/**\n * Creates a Groq speech adapter with explicit API key.\n * Type resolution happens here at the call site.\n *\n * @param model - The model name (e.g., 'canopylabs/orpheus-v1-english')\n * @param apiKey - Your Groq API key\n * @param config - Optional additional configuration\n * @returns Configured Groq speech adapter instance with resolved types\n *\n * @example\n * ```typescript\n * const adapter = createGroqSpeech('canopylabs/orpheus-v1-english', 'gsk_...')\n *\n * const result = await generateSpeech({\n * adapter,\n * text: 'Hello, world!',\n * voice: 'autumn',\n * })\n * ```\n */\nexport function createGroqSpeech<TModel extends GroqTTSModel>(\n model: TModel,\n apiKey: string,\n config?: Omit<GroqTTSConfig, 'apiKey'>,\n): GroqTTSAdapter<TModel> {\n return new GroqTTSAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates a Groq speech adapter with automatic API key detection from\n * environment variables.\n *\n * Looks for `GROQ_API_KEY` in the environment.\n *\n * @param model - The model name (e.g., 'canopylabs/orpheus-v1-english')\n * @param config - Optional configuration (excluding apiKey which is auto-detected)\n * @returns Configured Groq speech adapter instance with resolved types\n * @throws Error if GROQ_API_KEY is not found in environment\n *\n * @example\n * ```typescript\n * const adapter = groqSpeech('canopylabs/orpheus-v1-english')\n *\n * const result = await generateSpeech({\n * adapter,\n * text: 'Welcome to TanStack AI!',\n * voice: 'autumn',\n * format: 'wav',\n * })\n * ```\n */\nexport function groqSpeech<TModel extends GroqTTSModel>(\n model: TModel,\n config?: Omit<GroqTTSConfig, 'apiKey'>,\n): GroqTTSAdapter<TModel> {\n const apiKey = getGroqApiKeyFromEnv()\n return createGroqSpeech(model, apiKey, config)\n}\n"],"names":[],"mappings":";;;;;;AAmCO,MAAM,uBAAoD,eAG/D;AAAA,EACS,OAAO;AAAA,EAEN;AAAA,EAEV,YAAY,QAAuB,OAAe;AAChD,UAAM,OAAO,EAAE;AACf,SAAK,SAAS,IAAI,OAAO,iBAAiB,MAAM,CAAC;AAAA,EACnD;AAAA,EAEA,MAAM,eACJ,SACoB;AACpB,UAAM,EAAE,OAAO,MAAM,OAAO,QAAQ,OAAO,iBAAiB;AAE5D,uBAAmB,EAAE,OAAO,MAAM,OAAO,KAAK,OAAO;AAKrD,UAAM,UAA+C;AAAA,MACnD;AAAA,MACA,OAAO;AAAA,MACP,OAAO,SAAS;AAAA,MAChB,iBAAiB,UAAU;AAAA,MAC3B,GAAI,UAAU,UAAa,EAAE,MAAA;AAAA,MAC7B,GAAI,gBAAgB,CAAA;AAAA,IAAC;AAGvB,QAAI;AACF,cAAQ,OAAO;AAAA,QACb,yBAAyB,KAAK,IAAI,UAAU,KAAK,WAAW,QAAQ,mBAAmB,SAAS,UAAU,QAAQ,KAAK;AAAA,QACvH,EAAE,UAAU,KAAK,MAAM,MAAA;AAAA,MAAM;AAE/B,YAAM,WAAW,MAAM,KAAK,OAAO,MAAM,OAAO,OAAO,OAAO;AAE9D,YAAM,cAAc,MAAM,SAAS,YAAA;AACnC,YAAM,SAAS,oBAAoB,WAAW;AAE9C,YAAM,eAAe,QAAQ,mBAAmB;AAChD,YAAM,cAAc,KAAK,eAAe,YAAY;AAEpD,aAAO;AAAA,QACL,IAAI,WAAW,KAAK,IAAI;AAAA,QACxB;AAAA,QACA,OAAO;AAAA,QACP,QAAQ;AAAA,QACR;AAAA,MAAA;AAAA,IAEJ,SAAS,OAAgB;AAGvB,cAAQ,OAAO,OAAO,GAAG,KAAK,IAAI,yBAAyB;AAAA,QACzD,OAAO,kBAAkB,OAAO,GAAG,KAAK,IAAI,wBAAwB;AAAA,QACpE,QAAQ,GAAG,KAAK,IAAI;AAAA,MAAA,CACrB;AACD,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEQ,eAAe,QAAwB;AAC7C,UAAM,eAAuC;AAAA,MAC3C,MAAM;AAAA,MACN,KAAK;AAAA,MACL,OAAO;AAAA,MACP,KAAK;AAAA,MACL,KAAK;AAAA,IAAA;AAEP,WAAO,aAAa,MAAM,KAAK;AAAA,EACjC;AACF;AAsBO,SAAS,iBACd,OACA,QACA,QACwB;AACxB,SAAO,IAAI,eAAe,EAAE,QAAQ,GAAG,OAAA,GAAU,KAAK;AACxD;AAyBO,SAAS,WACd,OACA,QACwB;AACxB,QAAM,SAAS,qBAAA;AACf,SAAO,iBAAiB,OAAO,QAAQ,MAAM;AAC/C;"}
1
+ {"version":3,"file":"tts.js","sources":["../../../src/adapters/tts.ts"],"sourcesContent":["import OpenAI from 'openai'\nimport { BaseTTSAdapter } from '@tanstack/ai/adapters'\nimport { toRunErrorPayload } from '@tanstack/ai/adapter-internals'\nimport { arrayBufferToBase64, generateId } from '@tanstack/ai-utils'\nimport { getGroqApiKeyFromEnv, withGroqDefaults } from '../utils/client'\nimport { validateAudioInput } from '../audio/audio-provider-options'\nimport type { TTSOptions, TTSResult } from '@tanstack/ai'\nimport type OpenAI_SDK from 'openai'\nimport type { GroqTTSModel } from '../model-meta'\nimport type { GroqTTSProviderOptions } from '../audio/tts-provider-options'\nimport type { GroqClientConfig } from '../utils/client'\n\n/**\n * Configuration for Groq TTS adapter\n */\nexport interface GroqTTSConfig extends GroqClientConfig {}\n\n/**\n * Groq Text-to-Speech Adapter\n *\n * Tree-shakeable adapter for Groq TTS functionality. Groq exposes an\n * OpenAI-compatible `/audio/speech` endpoint, so the adapter drives it with\n * the OpenAI SDK via a `baseURL` override (the same pattern as the Groq text\n * adapter).\n *\n * Supports `canopylabs/orpheus-v1-english` and\n * `canopylabs/orpheus-arabic-saudi`.\n *\n * Features:\n * - English voices: autumn(f), diana(f), hannah(f), austin(m), daniel(m), troy(m)\n * - Arabic voices: fahad(m), sultan(m), lulwa(f), noura(f)\n * - Output formats: flac, mp3, mulaw, ogg, wav (default wav)\n * - Speed control\n * - Configurable sample rate via `modelOptions`\n */\nexport class GroqTTSAdapter<TModel extends GroqTTSModel> extends BaseTTSAdapter<\n TModel,\n GroqTTSProviderOptions\n> {\n readonly name = 'groq' as const\n\n protected client: OpenAI\n\n constructor(config: GroqTTSConfig, model: TModel) {\n super(model, {})\n this.client = new OpenAI(withGroqDefaults(config))\n }\n\n async generateSpeech(\n options: TTSOptions<GroqTTSProviderOptions>,\n ): Promise<TTSResult> {\n const { model, text, voice, format, speed, modelOptions } = options\n\n validateAudioInput({ input: text, model: this.model })\n\n // Spreading optional inputs conditionally keeps the request compatible\n // with the vendor SDK shape under exactOptionalPropertyTypes. `sample_rate`\n // is a Groq-only body field carried via modelOptions.\n const request: OpenAI_SDK.Audio.SpeechCreateParams = {\n model,\n input: text,\n voice: voice ?? 'autumn',\n response_format: format ?? 'wav',\n ...(speed !== undefined && { speed }),\n ...(modelOptions ?? {}),\n }\n\n try {\n options.logger.request(\n `activity=tts provider=${this.name} model=${model} format=${request.response_format ?? 'default'} voice=${request.voice}`,\n { provider: this.name, model },\n )\n const response = await this.client.audio.speech.create(request)\n\n const arrayBuffer = await response.arrayBuffer()\n const base64 = arrayBufferToBase64(arrayBuffer)\n\n const outputFormat = request.response_format ?? 'wav'\n const contentType = this.getContentType(outputFormat)\n\n return {\n id: generateId(this.name),\n model,\n audio: base64,\n format: outputFormat,\n contentType,\n }\n } catch (error: unknown) {\n // Narrow before logging: raw SDK errors can carry request metadata\n // (including auth headers) which we must never surface to user loggers.\n options.logger.errors(`${this.name}.generateSpeech fatal`, {\n error: toRunErrorPayload(error, `${this.name}.generateSpeech failed`),\n source: `${this.name}.generateSpeech`,\n })\n throw error\n }\n }\n\n private getContentType(format: string): string {\n const contentTypes: Record<string, string> = {\n flac: 'audio/flac',\n mp3: 'audio/mpeg',\n mulaw: 'audio/basic',\n ogg: 'audio/ogg',\n wav: 'audio/wav',\n }\n return contentTypes[format] || 'audio/wav'\n }\n}\n\n/**\n * Creates a Groq speech adapter with explicit API key.\n * Type resolution happens here at the call site.\n *\n * @param model - The model name (e.g., 'canopylabs/orpheus-v1-english')\n * @param apiKey - Your Groq API key\n * @param config - Optional additional configuration\n * @returns Configured Groq speech adapter instance with resolved types\n *\n * @example\n * ```typescript\n * const adapter = createGroqSpeech('canopylabs/orpheus-v1-english', 'gsk_...')\n *\n * const result = await generateSpeech({\n * adapter,\n * text: 'Hello, world!',\n * voice: 'autumn',\n * })\n * ```\n */\nexport function createGroqSpeech<TModel extends GroqTTSModel>(\n model: TModel,\n apiKey: string,\n config?: Omit<GroqTTSConfig, 'apiKey'>,\n): GroqTTSAdapter<TModel> {\n return new GroqTTSAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates a Groq speech adapter with automatic API key detection from\n * environment variables.\n *\n * Looks for `GROQ_API_KEY` in the environment.\n *\n * @param model - The model name (e.g., 'canopylabs/orpheus-v1-english')\n * @param config - Optional configuration (excluding apiKey which is auto-detected)\n * @returns Configured Groq speech adapter instance with resolved types\n * @throws Error if GROQ_API_KEY is not found in environment\n *\n * @example\n * ```typescript\n * const adapter = groqSpeech('canopylabs/orpheus-v1-english')\n *\n * const result = await generateSpeech({\n * adapter,\n * text: 'Welcome to TanStack AI!',\n * voice: 'autumn',\n * format: 'wav',\n * })\n * ```\n */\nexport function groqSpeech<TModel extends GroqTTSModel>(\n model: TModel,\n config?: Omit<GroqTTSConfig, 'apiKey'>,\n): GroqTTSAdapter<TModel> {\n const apiKey = getGroqApiKeyFromEnv()\n return createGroqSpeech(model, apiKey, config)\n}\n"],"names":[],"mappings":";;;;;;AAmCO,MAAM,uBAAoD,eAG/D;AAAA,EACS,OAAO;AAAA,EAEN;AAAA,EAEV,YAAY,QAAuB,OAAe;AAChD,UAAM,OAAO,EAAE;AACf,SAAK,SAAS,IAAI,OAAO,iBAAiB,MAAM,CAAC;AAAA,EACnD;AAAA,EAEA,MAAM,eACJ,SACoB;AACpB,UAAM,EAAE,OAAO,MAAM,OAAO,QAAQ,OAAO,iBAAiB;AAE5D,uBAAmB,EAAE,OAAO,MAAM,OAAO,KAAK,OAAO;AAKrD,UAAM,UAA+C;AAAA,MACnD;AAAA,MACA,OAAO;AAAA,MACP,OAAO,SAAS;AAAA,MAChB,iBAAiB,UAAU;AAAA,MAC3B,GAAI,UAAU,UAAa,EAAE,MAAA;AAAA,MAC7B,GAAI,gBAAgB,CAAA;AAAA,IAAC;AAGvB,QAAI;AACF,cAAQ,OAAO;AAAA,QACb,yBAAyB,KAAK,IAAI,UAAU,KAAK,WAAW,QAAQ,mBAAmB,SAAS,UAAU,QAAQ,KAAK;AAAA,QACvH,EAAE,UAAU,KAAK,MAAM,MAAA;AAAA,MAAM;AAE/B,YAAM,WAAW,MAAM,KAAK,OAAO,MAAM,OAAO,OAAO,OAAO;AAE9D,YAAM,cAAc,MAAM,SAAS,YAAA;AACnC,YAAM,SAAS,oBAAoB,WAAW;AAE9C,YAAM,eAAe,QAAQ,mBAAmB;AAChD,YAAM,cAAc,KAAK,eAAe,YAAY;AAEpD,aAAO;AAAA,QACL,IAAI,WAAW,KAAK,IAAI;AAAA,QACxB;AAAA,QACA,OAAO;AAAA,QACP,QAAQ;AAAA,QACR;AAAA,MAAA;AAAA,IAEJ,SAAS,OAAgB;AAGvB,cAAQ,OAAO,OAAO,GAAG,KAAK,IAAI,yBAAyB;AAAA,QACzD,OAAO,kBAAkB,OAAO,GAAG,KAAK,IAAI,wBAAwB;AAAA,QACpE,QAAQ,GAAG,KAAK,IAAI;AAAA,MAAA,CACrB;AACD,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEQ,eAAe,QAAwB;AAC7C,UAAM,eAAuC;AAAA,MAC3C,MAAM;AAAA,MACN,KAAK;AAAA,MACL,OAAO;AAAA,MACP,KAAK;AAAA,MACL,KAAK;AAAA,IAAA;AAEP,WAAO,aAAa,MAAM,KAAK;AAAA,EACjC;AACF;AAsBO,SAAS,iBACd,OACA,QACA,QACwB;AACxB,SAAO,IAAI,eAAe,EAAE,QAAQ,GAAG,OAAA,GAAU,KAAK;AACxD;AAyBO,SAAS,WACd,OACA,QACwB;AACxB,QAAM,SAAS,qBAAA;AACf,SAAO,iBAAiB,OAAO,QAAQ,MAAM;AAC/C;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/ai-groq",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "Groq adapter for TanStack AI low-latency chat, tool calling, and structured outputs.",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -53,12 +53,12 @@
53
53
  },
54
54
  "peerDependencies": {
55
55
  "zod": "^4.0.0",
56
- "@tanstack/ai": "^0.40.0"
56
+ "@tanstack/ai": "^0.41.0"
57
57
  },
58
58
  "dependencies": {
59
59
  "openai": "^6.41.0",
60
60
  "@tanstack/ai-utils": "0.3.1",
61
- "@tanstack/openai-base": "0.9.7"
61
+ "@tanstack/openai-base": "0.9.8"
62
62
  },
63
63
  "scripts": {
64
64
  "build": "vite build",
@@ -10,7 +10,7 @@ import type {
10
10
  ResolveProviderOptions,
11
11
  } from '../model-meta'
12
12
  import type { GroqMessageMetadataByModality } from '../message-types'
13
- import type { GroqClientConfig } from '../utils'
13
+ import type { GroqClientConfig } from '../utils/client'
14
14
 
15
15
  type ResolveToolCapabilities<TModel extends string> =
16
16
  TModel extends keyof GroqChatModelToolCapabilitiesByName
@@ -8,7 +8,7 @@ import type { TTSOptions, TTSResult } from '@tanstack/ai'
8
8
  import type OpenAI_SDK from 'openai'
9
9
  import type { GroqTTSModel } from '../model-meta'
10
10
  import type { GroqTTSProviderOptions } from '../audio/tts-provider-options'
11
- import type { GroqClientConfig } from '../utils'
11
+ import type { GroqClientConfig } from '../utils/client'
12
12
 
13
13
  /**
14
14
  * Configuration for Groq TTS adapter
@@ -1,3 +0,0 @@
1
- export { getGroqApiKeyFromEnv, withGroqDefaults, type GroqClientConfig, } from './client.js';
2
- export { generateId } from '@tanstack/ai-utils';
3
- export { makeGroqStructuredOutputCompatible, transformNullsToUndefined, } from './schema-converter.js';
@@ -1,10 +0,0 @@
1
- export {
2
- getGroqApiKeyFromEnv,
3
- withGroqDefaults,
4
- type GroqClientConfig,
5
- } from './client'
6
- export { generateId } from '@tanstack/ai-utils'
7
- export {
8
- makeGroqStructuredOutputCompatible,
9
- transformNullsToUndefined,
10
- } from './schema-converter'