fix: surface ElevenLabs config errors and add availability flag
- Extract actual error message from blob response in previewVoice so users see the real API error instead of generic "Failed to generate preview" - VoicePreviewButton now reads err.message from thrown Error objects - Add available: bool field to ProviderVoicesResponse; returns false when ELEVENLABS_API_KEY is not configured so the frontend can react proactively instead of hitting a 400 on preview - VoiceSelector shows a descriptive config warning when available=false Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
1e177a6d5c
commit
a22fe5c1bc
5 changed files with 44 additions and 18 deletions
|
|
@ -48,6 +48,7 @@ class ProviderVoicesResponse(BaseModel):
|
|||
provider: str
|
||||
voices: list[VoiceInfo]
|
||||
default: str
|
||||
available: bool = True
|
||||
|
||||
|
||||
class LanguagesResponse(BaseModel):
|
||||
|
|
@ -99,6 +100,13 @@ async def list_voices(
|
|||
List available TTS voices for the specified provider.
|
||||
"""
|
||||
if provider == "elevenlabs":
|
||||
if not tts_service.elevenlabs_available:
|
||||
return ProviderVoicesResponse(
|
||||
provider="elevenlabs",
|
||||
voices=[],
|
||||
default="",
|
||||
available=False,
|
||||
)
|
||||
el_voices = await elevenlabs_voice_service.get_voices()
|
||||
voices = [
|
||||
VoiceInfo(
|
||||
|
|
@ -116,6 +124,7 @@ async def list_voices(
|
|||
provider="elevenlabs",
|
||||
voices=voices,
|
||||
default=default_id,
|
||||
available=True,
|
||||
)
|
||||
|
||||
# Default: Gemini
|
||||
|
|
|
|||
|
|
@ -106,7 +106,8 @@ export function VoicePreviewButton({
|
|||
await audio.play();
|
||||
setIsPlaying(true);
|
||||
} catch (err) {
|
||||
setError('Failed to generate preview');
|
||||
const errorMessage = err instanceof Error ? err.message : 'Failed to generate preview';
|
||||
setError(errorMessage);
|
||||
console.error('Voice preview error:', err);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
|
|
|
|||
|
|
@ -38,7 +38,9 @@ export function VoiceSelector({
|
|||
setLanguages(languagesData);
|
||||
|
||||
// Set default voice from API response if switching providers
|
||||
if (voicesData.default && voicesData.voices.length > 0) {
|
||||
if (voicesData.available === false) {
|
||||
setError(`ElevenLabs TTS is not configured on the server. Contact your administrator to set up the ELEVENLABS_API_KEY.`);
|
||||
} else if (voicesData.default && voicesData.voices.length > 0) {
|
||||
// Only reset default voice if the current one isn't in the new voice list
|
||||
const currentVoiceExists = voicesData.voices.some(v => v.id === preferences.default_voice);
|
||||
if (!currentVoiceExists) {
|
||||
|
|
|
|||
|
|
@ -376,22 +376,35 @@ class ApiClient {
|
|||
stability?: number,
|
||||
similarityBoost?: number,
|
||||
): Promise<Blob> {
|
||||
const response = await this.client.post(
|
||||
'/tts/preview',
|
||||
{
|
||||
voice_name: voiceName,
|
||||
language,
|
||||
provider: provider || 'gemini',
|
||||
model: model || 'flash',
|
||||
speed: speed || 1.0,
|
||||
style_preset: stylePreset || 'neutral',
|
||||
custom_style_prompt: customStylePrompt,
|
||||
stability: stability,
|
||||
similarity_boost: similarityBoost,
|
||||
},
|
||||
{ responseType: 'blob' }
|
||||
);
|
||||
return response.data;
|
||||
try {
|
||||
const response = await this.client.post(
|
||||
'/tts/preview',
|
||||
{
|
||||
voice_name: voiceName,
|
||||
language,
|
||||
provider: provider || 'gemini',
|
||||
model: model || 'flash',
|
||||
speed: speed || 1.0,
|
||||
style_preset: stylePreset || 'neutral',
|
||||
custom_style_prompt: customStylePrompt,
|
||||
stability: stability,
|
||||
similarity_boost: similarityBoost,
|
||||
},
|
||||
{ responseType: 'blob' }
|
||||
);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError(error) && error.response?.data instanceof Blob) {
|
||||
const text = await error.response.data.text();
|
||||
try {
|
||||
const json = JSON.parse(text);
|
||||
throw new Error(json.detail || 'Failed to generate preview');
|
||||
} catch {
|
||||
throw new Error(text || 'Failed to generate preview');
|
||||
}
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Review Notes endpoints
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ export interface ProviderVoicesResponse {
|
|||
provider: string;
|
||||
voices: VoiceInfo[];
|
||||
default: string;
|
||||
available?: boolean;
|
||||
}
|
||||
|
||||
/** @deprecated Use ProviderVoicesResponse instead */
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue