@tanstack/ai-groq 0.4.16 → 0.5.1

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.
@@ -0,0 +1,80 @@
1
+ import { BaseTranscriptionAdapter } from '@tanstack/ai/adapters';
2
+ import { TranscriptionOptions, TranscriptionResult } from '@tanstack/ai';
3
+ import { GroqTranscriptionModel } from '../model-meta.js';
4
+ import { GroqTranscriptionProviderOptions } from '../audio/transcription-provider-options.js';
5
+ import { GroqClientConfig } from '../utils/client.js';
6
+ /**
7
+ * Configuration for the Groq Transcription adapter.
8
+ */
9
+ export interface GroqTranscriptionConfig extends GroqClientConfig {
10
+ }
11
+ /**
12
+ * Groq Transcription (Speech-to-Text) Adapter
13
+ *
14
+ * Tree-shakeable adapter for Groq audio transcription. Supports
15
+ * whisper-large-v3 and whisper-large-v3-turbo.
16
+ *
17
+ * Features:
18
+ * - Audio file uploads (File, Blob, ArrayBuffer, base64/data URL)
19
+ * - Remote audio URLs passed directly via Groq's `url` field — no upload needed
20
+ * - Verbose JSON response with segment and word timestamps
21
+ * - Language detection or specification (ISO-639-1)
22
+ * - Confidence scores derived from segment avg_logprob
23
+ */
24
+ export declare class GroqTranscriptionAdapter<TModel extends GroqTranscriptionModel> extends BaseTranscriptionAdapter<TModel, GroqTranscriptionProviderOptions> {
25
+ readonly name: "groq";
26
+ private readonly apiKey;
27
+ private readonly baseURL;
28
+ private readonly defaultHeaders;
29
+ constructor(config: GroqTranscriptionConfig, model: TModel);
30
+ transcribe(options: TranscriptionOptions<GroqTranscriptionProviderOptions>): Promise<TranscriptionResult>;
31
+ private prepareAudioFile;
32
+ private ensureFileSupport;
33
+ }
34
+ /**
35
+ * Creates a Groq transcription adapter with an explicit API key.
36
+ * Type resolution happens here at the call site.
37
+ *
38
+ * @param model - The model name (e.g., 'whisper-large-v3-turbo')
39
+ * @param apiKey - Your Groq API key
40
+ * @param config - Optional additional configuration
41
+ * @returns Configured Groq transcription adapter instance
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * const adapter = createGroqTranscription('whisper-large-v3-turbo', 'gsk_...');
46
+ *
47
+ * const result = await generateTranscription({
48
+ * adapter,
49
+ * audio: audioFile,
50
+ * language: 'en',
51
+ * });
52
+ * ```
53
+ */
54
+ export declare function createGroqTranscription<TModel extends GroqTranscriptionModel>(model: TModel, apiKey: string, config?: Omit<GroqTranscriptionConfig, 'apiKey'>): GroqTranscriptionAdapter<TModel>;
55
+ /**
56
+ * Creates a Groq transcription adapter using the `GROQ_API_KEY` environment
57
+ * variable. Type resolution happens here at the call site.
58
+ *
59
+ * Looks for `GROQ_API_KEY` in:
60
+ * - `process.env` (Node.js)
61
+ * - `window.env` (browser with injected env)
62
+ *
63
+ * @param model - The model name (e.g., 'whisper-large-v3-turbo')
64
+ * @param config - Optional configuration (excluding apiKey which is auto-detected)
65
+ * @returns Configured Groq transcription adapter instance
66
+ * @throws Error if GROQ_API_KEY is not found in environment
67
+ *
68
+ * @example
69
+ * ```typescript
70
+ * const adapter = groqTranscription('whisper-large-v3-turbo');
71
+ *
72
+ * const result = await generateTranscription({
73
+ * adapter,
74
+ * audio: 'https://example.com/audio.mp3',
75
+ * });
76
+ *
77
+ * console.log(result.text)
78
+ * ```
79
+ */
80
+ export declare function groqTranscription<TModel extends GroqTranscriptionModel>(model: TModel, config?: Omit<GroqTranscriptionConfig, 'apiKey'>): GroqTranscriptionAdapter<TModel>;
@@ -0,0 +1,179 @@
1
+ import { BaseTranscriptionAdapter } from "@tanstack/ai/adapters";
2
+ import { generateId, base64ToArrayBuffer } from "@tanstack/ai-utils";
3
+ import { withGroqDefaults, getGroqApiKeyFromEnv } from "../utils/client.js";
4
+ function normalizeHeaders(headers) {
5
+ const out = {};
6
+ if (!headers) return out;
7
+ const assign = (key, value) => {
8
+ if (value != null) out[key] = String(value);
9
+ };
10
+ if (headers instanceof Headers) {
11
+ headers.forEach((value, key) => assign(key, value));
12
+ } else if (Array.isArray(headers)) {
13
+ for (const [key, value] of headers) assign(key, value);
14
+ } else {
15
+ for (const [key, value] of Object.entries(headers)) assign(key, value);
16
+ }
17
+ return out;
18
+ }
19
+ class GroqTranscriptionAdapter extends BaseTranscriptionAdapter {
20
+ name = "groq";
21
+ apiKey;
22
+ baseURL;
23
+ defaultHeaders;
24
+ constructor(config, model) {
25
+ super(model, {});
26
+ const resolved = withGroqDefaults(config);
27
+ this.apiKey = resolved.apiKey;
28
+ this.baseURL = resolved.baseURL ?? "https://api.groq.com/openai/v1";
29
+ this.defaultHeaders = normalizeHeaders(resolved.defaultHeaders);
30
+ }
31
+ async transcribe(options) {
32
+ const { model, audio, language, prompt, responseFormat, modelOptions } = options;
33
+ if (responseFormat === "srt" || responseFormat === "vtt") {
34
+ throw new Error(
35
+ `Groq transcription does not support responseFormat='${responseFormat}'. Supported values: 'json', 'text', 'verbose_json'.`
36
+ );
37
+ }
38
+ const effectiveFormat = responseFormat ?? "verbose_json";
39
+ const useVerbose = effectiveFormat === "verbose_json";
40
+ const form = new FormData();
41
+ form.append("model", model);
42
+ form.append("response_format", effectiveFormat);
43
+ if (language !== void 0) form.append("language", language);
44
+ if (prompt !== void 0) form.append("prompt", prompt);
45
+ if (modelOptions?.temperature !== void 0) {
46
+ form.append("temperature", String(modelOptions.temperature));
47
+ }
48
+ if (modelOptions?.timestamp_granularities !== void 0) {
49
+ for (const g of modelOptions.timestamp_granularities) {
50
+ form.append("timestamp_granularities[]", g);
51
+ }
52
+ }
53
+ if (typeof audio === "string" && /^https?:\/\//.test(audio)) {
54
+ form.append("url", audio);
55
+ } else {
56
+ form.append("file", this.prepareAudioFile(audio));
57
+ }
58
+ try {
59
+ options.logger.request(
60
+ `activity=transcription provider=${this.name} model=${model} verbose=${useVerbose}`,
61
+ { provider: this.name, model }
62
+ );
63
+ const response = await fetch(`${this.baseURL}/audio/transcriptions`, {
64
+ method: "POST",
65
+ headers: {
66
+ ...this.defaultHeaders,
67
+ Authorization: `Bearer ${this.apiKey}`
68
+ },
69
+ body: form
70
+ });
71
+ if (!response.ok) {
72
+ const body = await response.json().catch(() => null);
73
+ const message = body?.error?.message ?? `Groq API error ${response.status}`;
74
+ throw new Error(message);
75
+ }
76
+ if (useVerbose) {
77
+ const data = await response.json();
78
+ const requestId = data.x_groq?.id ?? generateId(this.name);
79
+ const segments = data.segments?.map(
80
+ (seg) => ({
81
+ id: seg.id,
82
+ start: seg.start,
83
+ end: seg.end,
84
+ text: seg.text,
85
+ confidence: Math.exp(seg.avg_logprob)
86
+ })
87
+ );
88
+ const words = data.words?.map((w) => ({
89
+ word: w.word,
90
+ start: w.start,
91
+ end: w.end
92
+ }));
93
+ return {
94
+ id: requestId,
95
+ model,
96
+ text: data.text,
97
+ ...data.language !== void 0 && { language: data.language },
98
+ ...data.duration !== void 0 && { duration: data.duration },
99
+ ...segments !== void 0 && { segments },
100
+ ...words !== void 0 && { words }
101
+ };
102
+ } else if (effectiveFormat === "text") {
103
+ const text = await response.text();
104
+ return {
105
+ id: generateId(this.name),
106
+ model,
107
+ text,
108
+ ...language !== void 0 && { language }
109
+ };
110
+ } else {
111
+ const data = await response.json();
112
+ return {
113
+ id: data.x_groq?.id ?? generateId(this.name),
114
+ model,
115
+ text: data.text,
116
+ ...language !== void 0 && { language }
117
+ };
118
+ }
119
+ } catch (error) {
120
+ options.logger.errors(`${this.name}.transcribe fatal`, {
121
+ error,
122
+ source: `${this.name}.transcribe`
123
+ });
124
+ throw error;
125
+ }
126
+ }
127
+ prepareAudioFile(audio) {
128
+ if (typeof File !== "undefined" && audio instanceof File) {
129
+ return audio;
130
+ }
131
+ if (typeof Blob !== "undefined" && audio instanceof Blob) {
132
+ this.ensureFileSupport();
133
+ return new File([audio], "audio.mp3", {
134
+ type: audio.type || "audio/mpeg"
135
+ });
136
+ }
137
+ if (typeof ArrayBuffer !== "undefined" && audio instanceof ArrayBuffer) {
138
+ this.ensureFileSupport();
139
+ return new File([audio], "audio.mp3", { type: "audio/mpeg" });
140
+ }
141
+ if (typeof audio === "string") {
142
+ this.ensureFileSupport();
143
+ if (audio.startsWith("data:")) {
144
+ const parts = audio.split(",");
145
+ const header = parts[0];
146
+ const base64Data = parts[1] || "";
147
+ const mimeMatch = header?.match(/data:([^;]+)/);
148
+ const mimeType = mimeMatch?.[1] || "audio/mpeg";
149
+ const bytes2 = base64ToArrayBuffer(base64Data);
150
+ const extension = mimeType.split("/")[1] || "mp3";
151
+ return new File([bytes2], `audio.${extension}`, { type: mimeType });
152
+ }
153
+ const bytes = base64ToArrayBuffer(audio);
154
+ return new File([bytes], "audio.mp3", { type: "audio/mpeg" });
155
+ }
156
+ throw new Error("Invalid audio input type");
157
+ }
158
+ // Throws on Node < 20 where the global `File` constructor is unavailable.
159
+ ensureFileSupport() {
160
+ if (typeof File === "undefined") {
161
+ throw new Error(
162
+ "`File` is not available in this environment. Use Node.js 20 or newer, or pass a File object directly."
163
+ );
164
+ }
165
+ }
166
+ }
167
+ function createGroqTranscription(model, apiKey, config) {
168
+ return new GroqTranscriptionAdapter({ apiKey, ...config }, model);
169
+ }
170
+ function groqTranscription(model, config) {
171
+ const apiKey = getGroqApiKeyFromEnv();
172
+ return createGroqTranscription(model, apiKey, config);
173
+ }
174
+ export {
175
+ GroqTranscriptionAdapter,
176
+ createGroqTranscription,
177
+ groqTranscription
178
+ };
179
+ //# sourceMappingURL=transcription.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transcription.js","sources":["../../../src/adapters/transcription.ts"],"sourcesContent":["import { BaseTranscriptionAdapter } from '@tanstack/ai/adapters'\nimport { base64ToArrayBuffer, generateId } from '@tanstack/ai-utils'\nimport { getGroqApiKeyFromEnv, withGroqDefaults } from '../utils/client'\nimport type {\n TranscriptionOptions,\n TranscriptionResult,\n TranscriptionSegment,\n} from '@tanstack/ai'\nimport type { GroqTranscriptionModel } from '../model-meta'\nimport type { GroqTranscriptionProviderOptions } from '../audio/transcription-provider-options'\nimport type { GroqClientConfig } from '../utils/client'\n\n/**\n * Configuration for the Groq Transcription adapter.\n */\nexport interface GroqTranscriptionConfig extends GroqClientConfig {}\n\n/**\n * Flattens the `openai` SDK's `HeadersLike` config value into a plain record so\n * it can be merged into the raw `fetch` request this adapter issues. Handles\n * the shapes callers actually pass (`Headers`, an entries array, or a plain\n * object); null/undefined values are dropped.\n *\n * ponytail: doesn't unwrap the SDK's internal `NullableHeaders` class; forward\n * that shape here if the SDK ever hands it to adapter config.\n */\nfunction normalizeHeaders(\n headers: GroqTranscriptionConfig['defaultHeaders'],\n): Record<string, string> {\n const out: Record<string, string> = {}\n if (!headers) return out\n const assign = (key: string, value: unknown) => {\n if (value != null) out[key] = String(value)\n }\n if (headers instanceof Headers) {\n headers.forEach((value, key) => assign(key, value))\n } else if (Array.isArray(headers)) {\n for (const [key, value] of headers) assign(key, value)\n } else {\n for (const [key, value] of Object.entries(headers)) assign(key, value)\n }\n return out\n}\n\n// Shape of Groq's verbose_json transcription response\ninterface GroqVerboseTranscriptionResponse {\n task?: string\n language?: string\n duration?: number\n text: string\n segments?: Array<{\n id: number\n seek?: number\n start: number\n end: number\n text: string\n tokens?: Array<number>\n temperature?: number\n avg_logprob: number\n compression_ratio?: number\n no_speech_prob?: number\n }>\n words?: Array<{ word: string; start: number; end: number }>\n x_groq?: { id?: string }\n}\n\n// Shape of Groq's json transcription response\ninterface GroqJsonTranscriptionResponse {\n text: string\n x_groq?: { id?: string }\n}\n\n/**\n * Groq Transcription (Speech-to-Text) Adapter\n *\n * Tree-shakeable adapter for Groq audio transcription. Supports\n * whisper-large-v3 and whisper-large-v3-turbo.\n *\n * Features:\n * - Audio file uploads (File, Blob, ArrayBuffer, base64/data URL)\n * - Remote audio URLs passed directly via Groq's `url` field — no upload needed\n * - Verbose JSON response with segment and word timestamps\n * - Language detection or specification (ISO-639-1)\n * - Confidence scores derived from segment avg_logprob\n */\nexport class GroqTranscriptionAdapter<\n TModel extends GroqTranscriptionModel,\n> extends BaseTranscriptionAdapter<TModel, GroqTranscriptionProviderOptions> {\n readonly name = 'groq' as const\n\n private readonly apiKey: string\n private readonly baseURL: string\n private readonly defaultHeaders: Record<string, string>\n\n constructor(config: GroqTranscriptionConfig, model: TModel) {\n super(model, {})\n const resolved = withGroqDefaults(config)\n this.apiKey = resolved.apiKey\n this.baseURL = resolved.baseURL ?? 'https://api.groq.com/openai/v1'\n this.defaultHeaders = normalizeHeaders(resolved.defaultHeaders)\n }\n\n async transcribe(\n options: TranscriptionOptions<GroqTranscriptionProviderOptions>,\n ): Promise<TranscriptionResult> {\n const { model, audio, language, prompt, responseFormat, modelOptions } =\n options\n\n // Groq's transcription endpoint only accepts 'json', 'text', and\n // 'verbose_json'. Reject 'srt'/'vtt' up front so callers get a clear\n // message instead of an opaque Groq HTTP error.\n if (responseFormat === 'srt' || responseFormat === 'vtt') {\n throw new Error(\n `Groq transcription does not support responseFormat='${responseFormat}'. ` +\n `Supported values: 'json', 'text', 'verbose_json'.`,\n )\n }\n\n // Default to verbose_json so callers get language, duration, and timestamps\n // without having to opt in explicitly. Both Groq whisper models support it.\n const effectiveFormat = responseFormat ?? 'verbose_json'\n const useVerbose = effectiveFormat === 'verbose_json'\n\n const form = new FormData()\n form.append('model', model)\n form.append('response_format', effectiveFormat)\n if (language !== undefined) form.append('language', language)\n if (prompt !== undefined) form.append('prompt', prompt)\n if (modelOptions?.temperature !== undefined) {\n form.append('temperature', String(modelOptions.temperature))\n }\n if (modelOptions?.timestamp_granularities !== undefined) {\n for (const g of modelOptions.timestamp_granularities) {\n form.append('timestamp_granularities[]', g)\n }\n }\n\n // HTTP/HTTPS URLs are forwarded directly via Groq's `url` field, which\n // avoids a round-trip upload. All other inputs (File, Blob, ArrayBuffer,\n // base64, data URL) are converted to a File and sent as `file`.\n if (typeof audio === 'string' && /^https?:\\/\\//.test(audio)) {\n form.append('url', audio)\n } else {\n form.append('file', this.prepareAudioFile(audio))\n }\n\n try {\n options.logger.request(\n `activity=transcription provider=${this.name} model=${model} verbose=${useVerbose}`,\n { provider: this.name, model },\n )\n\n const response = await fetch(`${this.baseURL}/audio/transcriptions`, {\n method: 'POST',\n headers: {\n ...this.defaultHeaders,\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: form,\n })\n\n if (!response.ok) {\n const body = await response\n .json()\n .catch(() => null as Record<string, unknown> | null)\n const message =\n (body?.error as { message?: string } | undefined)?.message ??\n `Groq API error ${response.status}`\n throw new Error(message)\n }\n\n if (useVerbose) {\n const data = (await response.json()) as GroqVerboseTranscriptionResponse\n const requestId = data.x_groq?.id ?? generateId(this.name)\n\n // `TranscriptionResult` declares optional fields without `| undefined`,\n // so under exactOptionalPropertyTypes we must omit absent fields rather\n // than assigning `undefined`.\n const segments = data.segments?.map(\n (seg): TranscriptionSegment => ({\n id: seg.id,\n start: seg.start,\n end: seg.end,\n text: seg.text,\n confidence: Math.exp(seg.avg_logprob),\n }),\n )\n const words = data.words?.map((w) => ({\n word: w.word,\n start: w.start,\n end: w.end,\n }))\n\n return {\n id: requestId,\n model,\n text: data.text,\n ...(data.language !== undefined && { language: data.language }),\n ...(data.duration !== undefined && { duration: data.duration }),\n ...(segments !== undefined && { segments }),\n ...(words !== undefined && { words }),\n }\n } else if (effectiveFormat === 'text') {\n const text = await response.text()\n return {\n id: generateId(this.name),\n model,\n text,\n ...(language !== undefined && { language }),\n }\n } else {\n const data = (await response.json()) as GroqJsonTranscriptionResponse\n return {\n id: data.x_groq?.id ?? generateId(this.name),\n model,\n text: data.text,\n ...(language !== undefined && { language }),\n }\n }\n } catch (error: unknown) {\n options.logger.errors(`${this.name}.transcribe fatal`, {\n error,\n source: `${this.name}.transcribe`,\n })\n throw error\n }\n }\n\n private prepareAudioFile(audio: string | File | Blob | ArrayBuffer): File {\n if (typeof File !== 'undefined' && audio instanceof File) {\n return audio\n }\n if (typeof Blob !== 'undefined' && audio instanceof Blob) {\n this.ensureFileSupport()\n return new File([audio], 'audio.mp3', {\n type: audio.type || 'audio/mpeg',\n })\n }\n if (typeof ArrayBuffer !== 'undefined' && audio instanceof ArrayBuffer) {\n this.ensureFileSupport()\n return new File([audio], 'audio.mp3', { type: 'audio/mpeg' })\n }\n if (typeof audio === 'string') {\n this.ensureFileSupport()\n\n if (audio.startsWith('data:')) {\n const parts = audio.split(',')\n const header = parts[0]\n const base64Data = parts[1] || ''\n const mimeMatch = header?.match(/data:([^;]+)/)\n const mimeType = mimeMatch?.[1] || 'audio/mpeg'\n const bytes = base64ToArrayBuffer(base64Data)\n const extension = mimeType.split('/')[1] || 'mp3'\n return new File([bytes], `audio.${extension}`, { type: mimeType })\n }\n\n const bytes = base64ToArrayBuffer(audio)\n return new File([bytes], 'audio.mp3', { type: 'audio/mpeg' })\n }\n\n throw new Error('Invalid audio input type')\n }\n\n // Throws on Node < 20 where the global `File` constructor is unavailable.\n private ensureFileSupport(): void {\n if (typeof File === 'undefined') {\n throw new Error(\n '`File` is not available in this environment. ' +\n 'Use Node.js 20 or newer, or pass a File object directly.',\n )\n }\n }\n}\n\n/**\n * Creates a Groq transcription adapter with an explicit API key.\n * Type resolution happens here at the call site.\n *\n * @param model - The model name (e.g., 'whisper-large-v3-turbo')\n * @param apiKey - Your Groq API key\n * @param config - Optional additional configuration\n * @returns Configured Groq transcription adapter instance\n *\n * @example\n * ```typescript\n * const adapter = createGroqTranscription('whisper-large-v3-turbo', 'gsk_...');\n *\n * const result = await generateTranscription({\n * adapter,\n * audio: audioFile,\n * language: 'en',\n * });\n * ```\n */\nexport function createGroqTranscription<TModel extends GroqTranscriptionModel>(\n model: TModel,\n apiKey: string,\n config?: Omit<GroqTranscriptionConfig, 'apiKey'>,\n): GroqTranscriptionAdapter<TModel> {\n return new GroqTranscriptionAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates a Groq transcription adapter using the `GROQ_API_KEY` environment\n * variable. 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., 'whisper-large-v3-turbo')\n * @param config - Optional configuration (excluding apiKey which is auto-detected)\n * @returns Configured Groq transcription adapter instance\n * @throws Error if GROQ_API_KEY is not found in environment\n *\n * @example\n * ```typescript\n * const adapter = groqTranscription('whisper-large-v3-turbo');\n *\n * const result = await generateTranscription({\n * adapter,\n * audio: 'https://example.com/audio.mp3',\n * });\n *\n * console.log(result.text)\n * ```\n */\nexport function groqTranscription<TModel extends GroqTranscriptionModel>(\n model: TModel,\n config?: Omit<GroqTranscriptionConfig, 'apiKey'>,\n): GroqTranscriptionAdapter<TModel> {\n const apiKey = getGroqApiKeyFromEnv()\n return createGroqTranscription(model, apiKey, config)\n}\n"],"names":["bytes"],"mappings":"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"}
@@ -0,0 +1,81 @@
1
+ import { default as OpenAI } from 'openai';
2
+ import { BaseTTSAdapter } from '@tanstack/ai/adapters';
3
+ import { TTSOptions, TTSResult } from '@tanstack/ai';
4
+ import { GroqTTSModel } from '../model-meta.js';
5
+ import { GroqTTSProviderOptions } from '../audio/tts-provider-options.js';
6
+ import { GroqClientConfig } from '../utils.js';
7
+ /**
8
+ * Configuration for Groq TTS adapter
9
+ */
10
+ export interface GroqTTSConfig extends GroqClientConfig {
11
+ }
12
+ /**
13
+ * Groq Text-to-Speech Adapter
14
+ *
15
+ * Tree-shakeable adapter for Groq TTS functionality. Groq exposes an
16
+ * OpenAI-compatible `/audio/speech` endpoint, so the adapter drives it with
17
+ * the OpenAI SDK via a `baseURL` override (the same pattern as the Groq text
18
+ * adapter).
19
+ *
20
+ * Supports `canopylabs/orpheus-v1-english` and
21
+ * `canopylabs/orpheus-arabic-saudi`.
22
+ *
23
+ * Features:
24
+ * - English voices: autumn(f), diana(f), hannah(f), austin(m), daniel(m), troy(m)
25
+ * - Arabic voices: fahad(m), sultan(m), lulwa(f), noura(f)
26
+ * - Output formats: flac, mp3, mulaw, ogg, wav (default wav)
27
+ * - Speed control
28
+ * - Configurable sample rate via `modelOptions`
29
+ */
30
+ export declare class GroqTTSAdapter<TModel extends GroqTTSModel> extends BaseTTSAdapter<TModel, GroqTTSProviderOptions> {
31
+ readonly name: "groq";
32
+ protected client: OpenAI;
33
+ constructor(config: GroqTTSConfig, model: TModel);
34
+ generateSpeech(options: TTSOptions<GroqTTSProviderOptions>): Promise<TTSResult>;
35
+ private getContentType;
36
+ }
37
+ /**
38
+ * Creates a Groq speech adapter with explicit API key.
39
+ * Type resolution happens here at the call site.
40
+ *
41
+ * @param model - The model name (e.g., 'canopylabs/orpheus-v1-english')
42
+ * @param apiKey - Your Groq API key
43
+ * @param config - Optional additional configuration
44
+ * @returns Configured Groq speech adapter instance with resolved types
45
+ *
46
+ * @example
47
+ * ```typescript
48
+ * const adapter = createGroqSpeech('canopylabs/orpheus-v1-english', 'gsk_...')
49
+ *
50
+ * const result = await generateSpeech({
51
+ * adapter,
52
+ * text: 'Hello, world!',
53
+ * voice: 'autumn',
54
+ * })
55
+ * ```
56
+ */
57
+ export declare function createGroqSpeech<TModel extends GroqTTSModel>(model: TModel, apiKey: string, config?: Omit<GroqTTSConfig, 'apiKey'>): GroqTTSAdapter<TModel>;
58
+ /**
59
+ * Creates a Groq speech adapter with automatic API key detection from
60
+ * environment variables.
61
+ *
62
+ * Looks for `GROQ_API_KEY` in the environment.
63
+ *
64
+ * @param model - The model name (e.g., 'canopylabs/orpheus-v1-english')
65
+ * @param config - Optional configuration (excluding apiKey which is auto-detected)
66
+ * @returns Configured Groq speech adapter instance with resolved types
67
+ * @throws Error if GROQ_API_KEY is not found in environment
68
+ *
69
+ * @example
70
+ * ```typescript
71
+ * const adapter = groqSpeech('canopylabs/orpheus-v1-english')
72
+ *
73
+ * const result = await generateSpeech({
74
+ * adapter,
75
+ * text: 'Welcome to TanStack AI!',
76
+ * voice: 'autumn',
77
+ * format: 'wav',
78
+ * })
79
+ * ```
80
+ */
81
+ export declare function groqSpeech<TModel extends GroqTTSModel>(model: TModel, config?: Omit<GroqTTSConfig, 'apiKey'>): GroqTTSAdapter<TModel>;
@@ -0,0 +1,73 @@
1
+ import OpenAI from "openai";
2
+ import { BaseTTSAdapter } from "@tanstack/ai/adapters";
3
+ import { toRunErrorPayload } from "@tanstack/ai/adapter-internals";
4
+ import { arrayBufferToBase64, generateId } from "@tanstack/ai-utils";
5
+ import { withGroqDefaults, getGroqApiKeyFromEnv } from "../utils/client.js";
6
+ import { validateAudioInput } from "../audio/audio-provider-options.js";
7
+ class GroqTTSAdapter extends BaseTTSAdapter {
8
+ name = "groq";
9
+ client;
10
+ constructor(config, model) {
11
+ super(model, {});
12
+ this.client = new OpenAI(withGroqDefaults(config));
13
+ }
14
+ async generateSpeech(options) {
15
+ const { model, text, voice, format, speed, modelOptions } = options;
16
+ validateAudioInput({ input: text, model: this.model });
17
+ const request = {
18
+ model,
19
+ input: text,
20
+ voice: voice ?? "autumn",
21
+ response_format: format ?? "wav",
22
+ ...speed !== void 0 && { speed },
23
+ ...modelOptions ?? {}
24
+ };
25
+ try {
26
+ options.logger.request(
27
+ `activity=tts provider=${this.name} model=${model} format=${request.response_format ?? "default"} voice=${request.voice}`,
28
+ { provider: this.name, model }
29
+ );
30
+ const response = await this.client.audio.speech.create(request);
31
+ const arrayBuffer = await response.arrayBuffer();
32
+ const base64 = arrayBufferToBase64(arrayBuffer);
33
+ const outputFormat = request.response_format ?? "wav";
34
+ const contentType = this.getContentType(outputFormat);
35
+ return {
36
+ id: generateId(this.name),
37
+ model,
38
+ audio: base64,
39
+ format: outputFormat,
40
+ contentType
41
+ };
42
+ } catch (error) {
43
+ options.logger.errors(`${this.name}.generateSpeech fatal`, {
44
+ error: toRunErrorPayload(error, `${this.name}.generateSpeech failed`),
45
+ source: `${this.name}.generateSpeech`
46
+ });
47
+ throw error;
48
+ }
49
+ }
50
+ getContentType(format) {
51
+ const contentTypes = {
52
+ flac: "audio/flac",
53
+ mp3: "audio/mpeg",
54
+ mulaw: "audio/basic",
55
+ ogg: "audio/ogg",
56
+ wav: "audio/wav"
57
+ };
58
+ return contentTypes[format] || "audio/wav";
59
+ }
60
+ }
61
+ function createGroqSpeech(model, apiKey, config) {
62
+ return new GroqTTSAdapter({ apiKey, ...config }, model);
63
+ }
64
+ function groqSpeech(model, config) {
65
+ const apiKey = getGroqApiKeyFromEnv();
66
+ return createGroqSpeech(model, apiKey, config);
67
+ }
68
+ export {
69
+ GroqTTSAdapter,
70
+ createGroqSpeech,
71
+ groqSpeech
72
+ };
73
+ //# sourceMappingURL=tts.js.map
@@ -0,0 +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;"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Common audio provider options for Groq audio endpoints.
3
+ */
4
+ export interface AudioProviderOptions {
5
+ /**
6
+ * The text to generate audio for.
7
+ * Maximum length is 200 characters.
8
+ * Use [directions] for vocal control (English voices only).
9
+ */
10
+ input: string;
11
+ /**
12
+ * The audio model to use for generation.
13
+ */
14
+ model: string;
15
+ }
16
+ /**
17
+ * Validates that the audio input text does not exceed the maximum length.
18
+ * @throws Error if input text exceeds 200 characters
19
+ */
20
+ export declare const validateAudioInput: (options: AudioProviderOptions) => void;
@@ -0,0 +1,9 @@
1
+ const validateAudioInput = (options) => {
2
+ if (options.input.length > 200) {
3
+ throw new Error("Input text exceeds maximum length of 200 characters.");
4
+ }
5
+ };
6
+ export {
7
+ validateAudioInput
8
+ };
9
+ //# sourceMappingURL=audio-provider-options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audio-provider-options.js","sources":["../../../src/audio/audio-provider-options.ts"],"sourcesContent":["/**\n * Common audio provider options for Groq audio endpoints.\n */\nexport interface AudioProviderOptions {\n /**\n * The text to generate audio for.\n * Maximum length is 200 characters.\n * Use [directions] for vocal control (English voices only).\n */\n input: string\n /**\n * The audio model to use for generation.\n */\n model: string\n}\n\n/**\n * Validates that the audio input text does not exceed the maximum length.\n * @throws Error if input text exceeds 200 characters\n */\nexport const validateAudioInput = (options: AudioProviderOptions) => {\n if (options.input.length > 200) {\n throw new Error('Input text exceeds maximum length of 200 characters.')\n }\n}\n"],"names":[],"mappings":"AAoBO,MAAM,qBAAqB,CAAC,YAAkC;AACnE,MAAI,QAAQ,MAAM,SAAS,KAAK;AAC9B,UAAM,IAAI,MAAM,sDAAsD;AAAA,EACxE;AACF;"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Groq-specific options for audio transcription.
3
+ *
4
+ * These fields extend the shared `TranscriptionOptions` and are forwarded
5
+ * verbatim to the Groq transcription endpoint.
6
+ */
7
+ export interface GroqTranscriptionProviderOptions {
8
+ /**
9
+ * Sampling temperature between 0 and 1. Lower values produce more
10
+ * deterministic output. Groq recommends 0 (the default) for most use cases.
11
+ */
12
+ temperature?: number;
13
+ /**
14
+ * Granularity levels to include when `response_format` is `verbose_json`.
15
+ * Pass `['word']`, `['segment']`, or both to control which timestamp arrays
16
+ * appear in the result.
17
+ */
18
+ timestamp_granularities?: Array<'word' | 'segment'>;
19
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Groq TTS voice options for English models
3
+ */
4
+ export type GroqTTSEnglishVoice = 'autumn' | 'diana' | 'hannah' | 'austin' | 'daniel' | 'troy';
5
+ /**
6
+ * Groq TTS voice options for Arabic models
7
+ */
8
+ export type GroqTTSArabicVoice = 'fahad' | 'sultan' | 'lulwa' | 'noura';
9
+ /**
10
+ * Union of all Groq TTS voice options
11
+ */
12
+ export type GroqTTSVoice = GroqTTSEnglishVoice | GroqTTSArabicVoice;
13
+ /**
14
+ * Groq TTS output format options.
15
+ */
16
+ export type GroqTTSFormat = 'flac' | 'mp3' | 'mulaw' | 'ogg' | 'wav';
17
+ /**
18
+ * Groq TTS sample rate options
19
+ */
20
+ export type GroqTTSSampleRate = 8000 | 16000 | 22050 | 24000 | 32000 | 44100 | 48000;
21
+ /**
22
+ * Provider-specific options for Groq TTS.
23
+ * These options are passed via `modelOptions` when calling `generateSpeech`.
24
+ */
25
+ export interface GroqTTSProviderOptions {
26
+ /**
27
+ * The sample rate of the generated audio in Hz.
28
+ */
29
+ sample_rate?: GroqTTSSampleRate;
30
+ }
@@ -2,9 +2,13 @@
2
2
  * @module @tanstack/ai-groq
3
3
  *
4
4
  * Groq provider adapter for TanStack AI.
5
- * Provides tree-shakeable adapters for Groq's Chat Completions API.
5
+ * Provides tree-shakeable adapters for Groq's Chat Completions API and TTS API.
6
6
  */
7
7
  export { GroqTextAdapter, createGroqText, groqText, type GroqTextConfig, type GroqTextProviderOptions, } from './adapters/text.js';
8
- export type { GroqChatModelProviderOptionsByName, GroqChatModelToolCapabilitiesByName, GroqModelInputModalitiesByName, ResolveProviderOptions, ResolveInputModalities, GroqChatModels, } from './model-meta.js';
9
- export { GROQ_CHAT_MODELS } from './model-meta.js';
8
+ export { GroqTranscriptionAdapter, createGroqTranscription, groqTranscription, type GroqTranscriptionConfig, } from './adapters/transcription.js';
9
+ export type { GroqTranscriptionProviderOptions } from './audio/transcription-provider-options.js';
10
+ export { GroqTTSAdapter, createGroqSpeech, groqSpeech, type GroqTTSConfig, } from './adapters/tts.js';
11
+ export type { GroqTTSProviderOptions, GroqTTSVoice, GroqTTSEnglishVoice, GroqTTSArabicVoice, GroqTTSFormat, GroqTTSSampleRate, } from './audio/tts-provider-options.js';
12
+ export type { GroqChatModelProviderOptionsByName, GroqTTSModelProviderOptionsByName, GroqChatModelToolCapabilitiesByName, GroqModelInputModalitiesByName, ResolveProviderOptions, ResolveInputModalities, GroqChatModels, GroqTranscriptionModel, GroqTTSModel, } from './model-meta.js';
13
+ export { GROQ_CHAT_MODELS, GROQ_TRANSCRIPTION_MODELS, GROQ_TTS_MODELS, } from './model-meta.js';
10
14
  export type { GroqTextMetadata, GroqImageMetadata, GroqAudioMetadata, GroqVideoMetadata, GroqDocumentMetadata, GroqMessageMetadataByModality, } from './message-types.js';
package/dist/esm/index.js CHANGED
@@ -1,9 +1,19 @@
1
1
  import { GroqTextAdapter, createGroqText, groqText } from "./adapters/text.js";
2
- import { GROQ_CHAT_MODELS } from "./model-meta.js";
2
+ import { GroqTranscriptionAdapter, createGroqTranscription, groqTranscription } from "./adapters/transcription.js";
3
+ import { GroqTTSAdapter, createGroqSpeech, groqSpeech } from "./adapters/tts.js";
4
+ import { GROQ_CHAT_MODELS, GROQ_TRANSCRIPTION_MODELS, GROQ_TTS_MODELS } from "./model-meta.js";
3
5
  export {
4
6
  GROQ_CHAT_MODELS,
7
+ GROQ_TRANSCRIPTION_MODELS,
8
+ GROQ_TTS_MODELS,
9
+ GroqTTSAdapter,
5
10
  GroqTextAdapter,
11
+ GroqTranscriptionAdapter,
12
+ createGroqSpeech,
6
13
  createGroqText,
7
- groqText
14
+ createGroqTranscription,
15
+ groqSpeech,
16
+ groqText,
17
+ groqTranscription
8
18
  };
9
19
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
@@ -1,4 +1,5 @@
1
1
  import { GroqTextProviderOptions } from './text/text-provider-options.js';
2
+ import { GroqTTSProviderOptions } from './audio/tts-provider-options.js';
2
3
  declare const LLAMA_3_3_70B_VERSATILE: {
3
4
  readonly name: "llama-3.3-70b-versatile";
4
5
  readonly context_window: 131072;
@@ -294,14 +295,36 @@ export type GroqChatModelToolCapabilitiesByName = {
294
295
  [KIMI_K2_INSTRUCT_0905.name]: typeof KIMI_K2_INSTRUCT_0905.supports.tools;
295
296
  [QWEN3_32B.name]: typeof QWEN3_32B.supports.tools;
296
297
  };
298
+ /**
299
+ * Type-only map from Groq TTS model name to its provider options type.
300
+ */
301
+ export type GroqTTSModelProviderOptionsByName = {
302
+ [K in GroqTTSModel]: GroqTTSProviderOptions;
303
+ };
297
304
  /**
298
305
  * Resolves the provider options type for a specific Groq model.
299
- * Falls back to generic GroqTextProviderOptions for unknown models.
306
+ * Checks TTS models first, then chat models, then falls back to generic options.
300
307
  */
301
- export type ResolveProviderOptions<TModel extends string> = TModel extends keyof GroqChatModelProviderOptionsByName ? GroqChatModelProviderOptionsByName[TModel] : GroqTextProviderOptions;
308
+ export type ResolveProviderOptions<TModel extends string> = TModel extends GroqTTSModel ? GroqTTSProviderOptions : TModel extends keyof GroqChatModelProviderOptionsByName ? GroqChatModelProviderOptionsByName[TModel] : GroqTextProviderOptions;
302
309
  /**
303
310
  * Resolve input modalities for a specific model.
304
311
  * If the model has explicit modalities in the map, use those; otherwise use text only.
305
312
  */
306
313
  export type ResolveInputModalities<TModel extends string> = TModel extends keyof GroqModelInputModalitiesByName ? GroqModelInputModalitiesByName[TModel] : readonly ['text'];
314
+ /**
315
+ * All supported Groq transcription model identifiers.
316
+ */
317
+ export declare const GROQ_TRANSCRIPTION_MODELS: readonly ["whisper-large-v3-turbo", "whisper-large-v3"];
318
+ /**
319
+ * Union type of all supported Groq transcription model names.
320
+ */
321
+ export type GroqTranscriptionModel = (typeof GROQ_TRANSCRIPTION_MODELS)[number];
322
+ /**
323
+ * All supported Groq TTS model identifiers.
324
+ */
325
+ export declare const GROQ_TTS_MODELS: readonly ["canopylabs/orpheus-v1-english", "canopylabs/orpheus-arabic-saudi"];
326
+ /**
327
+ * Union type of all supported Groq TTS model names.
328
+ */
329
+ export type GroqTTSModel = (typeof GROQ_TTS_MODELS)[number];
307
330
  export {};