Conversation
Adds a tree-shakeable Text-to-Speech (TTS) adapter for the Groq API. This includes support for English and Arabic voices, various output formats (with WAV as default), and configuration options like speed and sample rate. Includes new types, model metadata for TTS models, and comprehensive unit tests.
📝 WalkthroughWalkthroughAdds a tree-shakeable Groq Text-to-Speech adapter, TTS types and model metadata, input validation, unit tests, and exports to the public package API. Includes schema/tool guard adjustments affecting object-schema handling. Changes
Sequence DiagramsequenceDiagram
participant Client as Client
participant Adapter as GroqTTSAdapter
participant SDK as Groq SDK
participant API as Groq API
Client->>Adapter: generateSpeech(input, options)
Adapter->>Adapter: validateAudioInput(input)
Adapter->>Adapter: build params (model, voice, format, speed, sample_rate)
Adapter->>SDK: audio.speech.create(params)
SDK->>API: POST /audio/speech
API-->>SDK: audio blob
SDK-->>Adapter: audio data
Adapter->>Adapter: convert to base64 + derive contentType
Adapter-->>Client: TTSResult { id, model, audio, format, contentType }
Estimated Code Review Effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
View your CI Pipeline Execution ↗ for commit 28ad865
☁️ Nx Cloud last updated this comment at |
@tanstack/ai
@tanstack/ai-anthropic
@tanstack/ai-client
@tanstack/ai-devtools-core
@tanstack/ai-fal
@tanstack/ai-gemini
@tanstack/ai-grok
@tanstack/ai-groq
@tanstack/ai-ollama
@tanstack/ai-openai
@tanstack/ai-openrouter
@tanstack/ai-preact
@tanstack/ai-react
@tanstack/ai-react-ui
@tanstack/ai-solid
@tanstack/ai-solid-ui
@tanstack/ai-svelte
@tanstack/ai-vue
@tanstack/ai-vue-ui
@tanstack/preact-ai-devtools
@tanstack/react-ai-devtools
@tanstack/solid-ai-devtools
commit: |
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/typescript/ai-groq/src/utils/schema-converter.ts (1)
53-91:⚠️ Potential issue | 🟠 MajorPreserve strict object normalization for schemas without
properties.Line 53 now skips
{ type: 'object' }schemas entirely, sostructuredOutput()can send a strict schema withoutadditionalProperties: falsewhen the AI layer omitsproperties.packages/typescript/ai-groq/src/adapters/text.ts:134-182passes that result straight to Groq, so this regresses zero-property object outputs and nested object schemas that are declared without apropertieskey.Suggested fix
- if (result.type === 'object' && result.properties) { - const properties = { ...result.properties } + if (result.type === 'object') { + const properties = { ...(result.properties ?? {}) } const allPropertyNames = Object.keys(properties) for (const propName of allPropertyNames) { const prop = properties[propName] const wasOptional = !originalRequired.includes(propName) @@ } result.properties = properties result.required = allPropertyNames result.additionalProperties = false }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/typescript/ai-groq/src/utils/schema-converter.ts` around lines 53 - 91, The code currently only normalizes object schemas when result.properties exists, skipping strict normalization for plain { type: 'object' } and nested object/array items that lack a properties key; update the logic in makeGroqStructuredOutputCompatible so that when result.type === 'object' but result.properties is absent or empty you still enforce strict output by setting result.properties = result.properties || {} (or leave empty object), result.required = [], and result.additionalProperties = false; also ensure the same normalization is applied to nested schemas passed into the recursive calls (e.g., where prop.type === 'object' and prop.properties may be undefined, and for array items via prop.items) so zero-property objects and nested object schemas without a properties key remain strict.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/typescript/ai-groq/src/adapters/tts.ts`:
- Around line 64-65: The code uses Node-only
Buffer.from(arrayBuffer).toString('base64') to produce base64 for the TTS
response (see variables arrayBuffer and base64), which breaks in browsers;
replace that call with an isomorphic encoder (e.g. add a helper
base64Encode(arrayBuffer) that checks for global Buffer and uses
Buffer.from(...).toString('base64') in Node, and uses a browser path (Uint8Array
-> binary string -> btoa) otherwise) and use that helper where base64 is
computed; this keeps compatibility with the existing browser-oriented window.env
lookup in the same adapter.
- Around line 46-60: In generateSpeech, stop using the caller-supplied model and
replace the request.model with this.model (the adapter-configured model) and
remove the unchecked casts of voice/format; validate the incoming options.voice
and options.format against explicit allow-lists (e.g., allowed voices and
allowed formats matching GroqTTSVoice and GroqTTSFormat) and fall back to safe
defaults ('autumn' for voice, 'wav' for format) only after validation; update
the construction of Groq_SDK.Audio.Speech.SpeechCreateParams in generateSpeech
to use validatedVoice and validatedFormat variables and preserve other fields
(input, speed, ...modelOptions) so unsupported values are rejected before
calling the Groq API.
In `@packages/typescript/ai-groq/src/audio/tts-provider-options.ts`:
- Around line 22-26: The JSDoc for GroqTTSFormat is misleading; update the
comment above the exported type GroqTTSFormat to accurately reflect supported
formats (either list all supported values 'flac', 'mp3', 'mulaw', 'ogg', 'wav'
or remove the "Only wav is currently supported" claim) so generated docs match
the type declaration; edit the comment near the GroqTTSFormat type to be
accurate and concise.
In `@packages/typescript/ai-groq/src/model-meta.ts`:
- Around line 377-418: ResolveProviderOptions<TModel> is only discriminating
chat models so TModel like 'canopylabs/orpheus-v1-english' resolves to
GroqTextProviderOptions; modify the provider-options resolver type to account
for the new TTS model union by adding a branch that maps GroqTTSModel (exported
GROQ_TTS_MODELS / GroqTTSModel) to GroqTTSProviderOptions (instead of falling
through to GroqTextProviderOptions), i.e. update the conditional type that
currently checks GROQ_CHAT_MODELS to also check (or add) GROQ_TTS_MODELS /
GroqTTSModel so sample_rate and other TTS-specific options are correctly typed.
---
Outside diff comments:
In `@packages/typescript/ai-groq/src/utils/schema-converter.ts`:
- Around line 53-91: The code currently only normalizes object schemas when
result.properties exists, skipping strict normalization for plain { type:
'object' } and nested object/array items that lack a properties key; update the
logic in makeGroqStructuredOutputCompatible so that when result.type ===
'object' but result.properties is absent or empty you still enforce strict
output by setting result.properties = result.properties || {} (or leave empty
object), result.required = [], and result.additionalProperties = false; also
ensure the same normalization is applied to nested schemas passed into the
recursive calls (e.g., where prop.type === 'object' and prop.properties may be
undefined, and for array items via prop.items) so zero-property objects and
nested object schemas without a properties key remain strict.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 5e16ea17-fc04-4f5e-abfb-ea476c75ea0e
📒 Files selected for processing (9)
.changeset/green-colts-kiss.mdpackages/typescript/ai-groq/src/adapters/tts.tspackages/typescript/ai-groq/src/audio/audio-provider-options.tspackages/typescript/ai-groq/src/audio/tts-provider-options.tspackages/typescript/ai-groq/src/index.tspackages/typescript/ai-groq/src/model-meta.tspackages/typescript/ai-groq/src/tools/function-tool.tspackages/typescript/ai-groq/src/utils/schema-converter.tspackages/typescript/ai-groq/tests/groq-tts.test.ts
💤 Files with no reviewable changes (1)
- packages/typescript/ai-groq/src/tools/function-tool.ts
- Assign default values before type assertion to avoid unnecessary conditionals - Use a single variable `voiceFormat` for GroqTTSFormat and reuse in request - Remove redundant casts in `SpeechCreateParams` to satisfy ESLint - Maintain type safety between TTSVoice/GroqTTSVoice and TTSFormat/GroqTTSFormat
There was a problem hiding this comment.
♻️ Duplicate comments (1)
packages/typescript/ai-groq/src/adapters/tts.ts (1)
46-85:⚠️ Potential issue | 🟠 MajorPrior review issues remain unaddressed: model consistency, voice/format validation, and isomorphic base64.
Three issues flagged in previous reviews persist:
Model inconsistency (lines 50, 63, 80): Uses caller-supplied
options.modelinstead ofthis.model. The adapter is constructed with a specific model, butgenerateSpeechignores it.Unchecked type casts (lines 60, 65):
TTSOptionsallowsformat: 'opus' | 'aac' | 'pcm'which aren't valid for Groq (GroqTTSFormatsupports'flac' | 'mp3' | 'mulaw' | 'ogg' | 'wav'). The casts bypass compile-time safety, causing API failures at runtime.Node-only Buffer.from (line 74): The adapter supports browser environments via
window.envdetection ingetGroqApiKeyFromEnv, butBuffer.from()fails in browsers.Suggested fix
async generateSpeech( options: TTSOptions<GroqTTSProviderOptions>, ): Promise<TTSResult> { const { - model, text, voice = 'autumn', format = 'wav', speed, modelOptions, } = options validateAudioInput({ input: text, model }) - const voiceFormat = format as GroqTTSFormat + const validatedFormat = this.validateFormat(format) + const validatedVoice = this.validateVoice(voice) const request: Groq_SDK.Audio.Speech.SpeechCreateParams = { - model, + model: this.model, input: text, - voice: voice as GroqTTSVoice, - response_format: voiceFormat, + voice: validatedVoice, + response_format: validatedFormat, speed, ...modelOptions, } const response = await this.client.audio.speech.create(request) const arrayBuffer = await response.arrayBuffer() - const base64 = Buffer.from(arrayBuffer).toString('base64') + const base64 = this.arrayBufferToBase64(arrayBuffer) - const contentType = this.getContentType(voiceFormat) + const contentType = this.getContentType(validatedFormat) return { id: generateId(this.name), - model, + model: this.model, audio: base64, - format: voiceFormat, + format: validatedFormat, contentType, } } + + private validateFormat(format: string): GroqTTSFormat { + const validFormats: GroqTTSFormat[] = ['flac', 'mp3', 'mulaw', 'ogg', 'wav'] + if (validFormats.includes(format as GroqTTSFormat)) { + return format as GroqTTSFormat + } + return 'wav' // fallback to default + } + + private validateVoice(voice: string): GroqTTSVoice { + const validVoices: GroqTTSVoice[] = [ + 'autumn', 'diana', 'hannah', 'austin', 'daniel', 'troy', + 'fahad', 'sultan', 'lulwa', 'noura' + ] + if (validVoices.includes(voice as GroqTTSVoice)) { + return voice as GroqTTSVoice + } + return 'autumn' // fallback to default + } + + private arrayBufferToBase64(buffer: ArrayBuffer): string { + if (typeof Buffer !== 'undefined') { + return Buffer.from(buffer).toString('base64') + } + const bytes = new Uint8Array(buffer) + let binary = '' + for (let i = 0; i < bytes.byteLength; i++) { + binary += String.fromCharCode(bytes[i]) + } + return btoa(binary) + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/typescript/ai-groq/src/adapters/tts.ts` around lines 46 - 85, generateSpeech currently ignores the adapter's configured model and uses caller-supplied options.model, unsafely casts voice/format to Groq types, and uses Node-only Buffer.from for base64 conversion; update generateSpeech to use this.model (not options.model) when building the request, remove the unchecked casts for voice/format and instead validate/map options.voice and options.format against GroqTTSVoice and GroqTTSFormat (throw or normalize when unsupported), and replace Buffer.from(arrayBuffer).toString('base64') with an isomorphic conversion helper (e.g., use Buffer when available or a browser-safe ArrayBuffer→base64 path like Uint8Array→binary string→btoa) so the adapter works in both Node and browsers; refer to the generateSpeech method, request variable (Groq_SDK.Audio.Speech.SpeechCreateParams), and getContentType for where to apply these fixes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@packages/typescript/ai-groq/src/adapters/tts.ts`:
- Around line 46-85: generateSpeech currently ignores the adapter's configured
model and uses caller-supplied options.model, unsafely casts voice/format to
Groq types, and uses Node-only Buffer.from for base64 conversion; update
generateSpeech to use this.model (not options.model) when building the request,
remove the unchecked casts for voice/format and instead validate/map
options.voice and options.format against GroqTTSVoice and GroqTTSFormat (throw
or normalize when unsupported), and replace
Buffer.from(arrayBuffer).toString('base64') with an isomorphic conversion helper
(e.g., use Buffer when available or a browser-safe ArrayBuffer→base64 path like
Uint8Array→binary string→btoa) so the adapter works in both Node and browsers;
refer to the generateSpeech method, request variable
(Groq_SDK.Audio.Speech.SpeechCreateParams), and getContentType for where to
apply these fixes.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: a0381b5d-b6d4-4520-8faf-a4c1e967da64
📒 Files selected for processing (3)
packages/typescript/ai-groq/src/adapters/tts.tspackages/typescript/ai-groq/src/index.tspackages/typescript/ai-groq/src/model-meta.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/typescript/ai-groq/src/index.ts
Adds a tree-shakeable Text-to-Speech (TTS) adapter for the Groq API. This includes support for English and Arabic voices, various output formats (with WAV as default),
and configuration options like speed and sample rate.
Includes new types, model metadata for TTS models, and comprehensive unit tests.
🎯 Changes
✅ Checklist
pnpm run test:pr.🚀 Release Impact
Summary by CodeRabbit
New Features
Tests