pgits Claude commited on
Commit
5a46657
Β·
1 Parent(s): b2e61ec

Fix TTS CORS issues with backend proxy endpoint

Browse files

Add TTS synthesis proxy at /tts/synthesize to avoid CORS:
- Backend calls Gradio TTS service directly (no CORS issues)
- Frontend calls our proxy endpoint instead of external service
- Add gradio-client dependency for TTS integration
- Simplify TTS workflow with direct response handling

Resolves TTS CORS and 502 Bad Gateway errors

v1.0.10

πŸ€– Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

Files changed (3) hide show
  1. app/api/chat_widget.py +9 -23
  2. app/api/main.py +34 -1
  3. requirements.txt +2 -1
app/api/chat_widget.py CHANGED
@@ -414,45 +414,31 @@ async def chat_widget(request: Request):
414
  try {{
415
  console.log('🎡 Synthesizing TTS for:', text.substring(0, 50) + '...');
416
 
417
- // Call TTS service directly (HuggingFace to HuggingFace)
418
- const response = await fetch('https://pgits-kyutai-tts-service-v3.hf.space/call/predict', {{
419
  method: 'POST',
420
  headers: {{
421
  'Content-Type': 'application/json',
422
  }},
423
  body: JSON.stringify({{
424
- data: [
425
- text,
426
- 'expresso/ex03-ex01_happy_001_channel1_334s.wav'
427
- ]
428
  }})
429
  }});
430
 
431
  if (!response.ok) {{
432
- throw new Error(`TTS service error: ${{response.status}}`);
433
  }}
434
 
435
  const result = await response.json();
436
  console.log('🎡 TTS response:', result);
437
 
438
- // Handle Gradio queue response
439
- let audioUrl;
440
- if (result.event_id) {{
441
- // If queued, wait for completion
442
- audioUrl = await this.waitForTTSResult(result.event_id);
443
- }} else if (result.data && result.data[0]) {{
444
- // Direct response
445
- audioUrl = result.data[0];
446
- }} else {{
447
- throw new Error('No audio URL in TTS response');
448
- }}
449
-
450
- if (!audioUrl) {{
451
- throw new Error('TTS generation failed - no audio URL');
452
  }}
453
 
454
- // Convert relative URL to absolute
455
- const fullAudioUrl = audioUrl.startsWith('http') ? audioUrl : `https://pgits-kyutai-tts-service-v3.hf.space/file=${{audioUrl}}`;
456
  console.log('πŸ”Š Fetching audio from:', fullAudioUrl);
457
 
458
  // Fetch the audio file
 
414
  try {{
415
  console.log('🎡 Synthesizing TTS for:', text.substring(0, 50) + '...');
416
 
417
+ // Call our TTS proxy endpoint (no CORS issues)
418
+ const response = await fetch('{base_url}/tts/synthesize', {{
419
  method: 'POST',
420
  headers: {{
421
  'Content-Type': 'application/json',
422
  }},
423
  body: JSON.stringify({{
424
+ text: text,
425
+ voice: 'expresso/ex03-ex01_happy_001_channel1_334s.wav'
 
 
426
  }})
427
  }});
428
 
429
  if (!response.ok) {{
430
+ throw new Error(`TTS proxy error: ${{response.status}}`);
431
  }}
432
 
433
  const result = await response.json();
434
  console.log('🎡 TTS response:', result);
435
 
436
+ if (!result.success || !result.audio_url) {{
437
+ throw new Error('TTS generation failed');
 
 
 
 
 
 
 
 
 
 
 
 
438
  }}
439
 
440
+ // Convert relative URL to absolute if needed
441
+ const fullAudioUrl = result.audio_url.startsWith('http') ? result.audio_url : `https://pgits-kyutai-tts-service-v3.hf.space/file=${{result.audio_url}}`;
442
  console.log('πŸ”Š Fetching audio from:', fullAudioUrl);
443
 
444
  // Fetch the audio file
app/api/main.py CHANGED
@@ -408,11 +408,44 @@ async def stream_chat(request: ChatRequest):
408
  "Access-Control-Allow-Origin": "*",
409
  }
410
  )
411
-
412
  except Exception as e:
413
  raise HTTPException(status_code=500, detail=f"Stream chat error: {str(e)}")
414
 
415
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
416
  @app.get("/auth/login", response_model=AuthResponse)
417
  async def google_auth_login(request: Request, state: Optional[str] = None):
418
  """Initiate Google OAuth login."""
 
408
  "Access-Control-Allow-Origin": "*",
409
  }
410
  )
411
+
412
  except Exception as e:
413
  raise HTTPException(status_code=500, detail=f"Stream chat error: {str(e)}")
414
 
415
 
416
+ @app.post("/tts/synthesize")
417
+ async def tts_synthesize(request: Dict[str, Any]):
418
+ """TTS synthesis proxy to avoid CORS issues."""
419
+ try:
420
+ from gradio_client import Client
421
+
422
+ text = request.get("text", "")
423
+ voice = request.get("voice", "expresso/ex03-ex01_happy_001_channel1_334s.wav")
424
+
425
+ if not text.strip():
426
+ raise HTTPException(status_code=400, detail="Text is required")
427
+
428
+ logger.info(f"🎡 TTS synthesis request: {text[:50]}...")
429
+
430
+ # Connect to TTS service
431
+ client = Client("https://pgits-kyutai-tts-service-v3.hf.space")
432
+ result = client.predict(text, voice, api_name="/predict")
433
+
434
+ if not result:
435
+ raise HTTPException(status_code=500, detail="TTS generation failed")
436
+
437
+ # Return the audio file URL
438
+ return {
439
+ "success": True,
440
+ "audio_url": result,
441
+ "text": text
442
+ }
443
+
444
+ except Exception as e:
445
+ logger.error(f"TTS synthesis error: {e}")
446
+ raise HTTPException(status_code=500, detail=f"TTS synthesis failed: {str(e)}")
447
+
448
+
449
  @app.get("/auth/login", response_model=AuthResponse)
450
  async def google_auth_login(request: Request, state: Optional[str] = None):
451
  """Initiate Google OAuth login."""
requirements.txt CHANGED
@@ -19,4 +19,5 @@ python-multipart>=0.0.6
19
  python-jose>=3.3.0
20
  PyJWT>=2.8.0
21
  httpx>=0.25.0
22
- python-dotenv>=1.0.0
 
 
19
  python-jose>=3.3.0
20
  PyJWT>=2.8.0
21
  httpx>=0.25.0
22
+ python-dotenv>=1.0.0
23
+ gradio-client>=0.17.0