ishaq101 commited on
Commit
c2e783d
·
1 Parent(s): b35a210

fixing error stt and tts, empty chunk audio

Browse files
.gitignore CHANGED
@@ -27,4 +27,5 @@ API_CONTRACT_VOICE.md
27
  HIGHLIGHT_VOICE.md
28
  HIGHLIGHT_STT_TTS.md
29
 
30
- credentials/
 
 
27
  HIGHLIGHT_VOICE.md
28
  HIGHLIGHT_STT_TTS.md
29
 
30
+ credentials/
31
+ scripts/
main.py CHANGED
@@ -116,6 +116,8 @@ async def text_to_speech(req: TTSRequest) -> StreamingResponse:
116
  if not req.text.strip():
117
  raise HTTPException(status_code=400, detail="text must not be empty.")
118
 
 
 
119
  if req.provider == "gemini":
120
  if not GOOGLE_API_KEY:
121
  raise HTTPException(status_code=503, detail="Gemini TTS not configured.")
 
116
  if not req.text.strip():
117
  raise HTTPException(status_code=400, detail="text must not be empty.")
118
 
119
+ logger.info("TTS request: provider=%s, text_len=%d, text=%r", req.provider, len(req.text), req.text)
120
+
121
  if req.provider == "gemini":
122
  if not GOOGLE_API_KEY:
123
  raise HTTPException(status_code=503, detail="Gemini TTS not configured.")
src/config.py CHANGED
@@ -38,5 +38,5 @@ DEEPGRAM_LANGUAGE: str = os.getenv("DEEPGRAM_LANGUAGE", "id")
38
  DEEPGRAM_ENDPOINTING_MS: int = int(os.getenv("DEEPGRAM_ENDPOINTING_MS", "300"))
39
  DEEPGRAM_UTTERANCE_END_MS: int = int(os.getenv("DEEPGRAM_UTTERANCE_END_MS", "2000"))
40
 
41
- CHIRP3_REGION: str = os.getenv("CHIRP3_REGION", "us")
42
  CHIRP3_LANGUAGE: str = os.getenv("CHIRP3_LANGUAGE", "id-ID")
 
38
  DEEPGRAM_ENDPOINTING_MS: int = int(os.getenv("DEEPGRAM_ENDPOINTING_MS", "300"))
39
  DEEPGRAM_UTTERANCE_END_MS: int = int(os.getenv("DEEPGRAM_UTTERANCE_END_MS", "2000"))
40
 
41
+ CHIRP3_REGION: str = os.getenv("CHIRP3_REGION", "us-central1") # chirp_2 valid regions: us-central1, europe-west4, asia-southeast1
42
  CHIRP3_LANGUAGE: str = os.getenv("CHIRP3_LANGUAGE", "id-ID")
src/pipeline.py CHANGED
@@ -94,6 +94,7 @@ class VoicePipeline:
94
  self._tts_task = asyncio.create_task(self._speak(text))
95
 
96
  async def _speak(self, text: str) -> None:
 
97
  async with self._tts_lock:
98
  try:
99
  stream = (
 
94
  self._tts_task = asyncio.create_task(self._speak(text))
95
 
96
  async def _speak(self, text: str) -> None:
97
+ logger.info("TTS speak: provider=%s, text_len=%d, text=%r", self._tts_provider, len(text), text)
98
  async with self._tts_lock:
99
  try:
100
  stream = (
src/stt/chirp3_client.py CHANGED
@@ -19,34 +19,39 @@ OnTranscriptCallback = Callable[[str], Awaitable[None]]
19
 
20
  _CHUNK_SIZE = 24 * 1024 # stay under the 25 KB gRPC streaming limit
21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
  async def transcribe_audio(data: bytes, mimetype: str = "audio/wav") -> dict:
24
  """Transcribes a full audio file using Chirp 3 (Google Cloud Speech-to-Text V2)."""
25
  def _run() -> str:
26
- logger.info("chirp3 transcribe_audio: data=%d bytes, region=%s, project=%s", len(data), CHIRP3_REGION, GOOGLE_PROJECT_ID)
27
- client = SpeechClient(
28
- credentials=_credentials,
29
- client_options=ClientOptions(
30
- api_endpoint=f"{CHIRP3_REGION}-speech.googleapis.com"
31
- )
32
- )
33
  chunks = [data[i: i + _CHUNK_SIZE] for i in range(0, len(data), _CHUNK_SIZE)]
34
  audio_requests = (
35
  cloud_speech_types.StreamingRecognizeRequest(audio=chunk)
36
  for chunk in chunks
37
  )
38
- recognition_config = cloud_speech_types.RecognitionConfig(
39
- auto_decoding_config=cloud_speech_types.AutoDetectDecodingConfig(),
40
- language_codes=[CHIRP3_LANGUAGE],
41
- model="chirp_3",
42
- )
43
- streaming_config = cloud_speech_types.StreamingRecognitionConfig(
44
- config=recognition_config
45
- )
46
- config_request = cloud_speech_types.StreamingRecognizeRequest(
47
- recognizer=f"projects/{GOOGLE_PROJECT_ID}/locations/{CHIRP3_REGION}/recognizers/_",
48
- streaming_config=streaming_config,
49
- )
50
 
51
  def requests():
52
  yield config_request
@@ -118,13 +123,7 @@ class Chirp3STTStreamer:
118
  logger.exception("Chirp 3 STT transcription failed")
119
 
120
  def _recognize(self, wav_data: bytes) -> str:
121
- client = SpeechClient(
122
- credentials=_credentials,
123
- client_options=ClientOptions(
124
- api_endpoint=f"{CHIRP3_REGION}-speech.googleapis.com"
125
- )
126
- )
127
-
128
  chunks = [
129
  wav_data[i : i + _CHUNK_SIZE]
130
  for i in range(0, len(wav_data), _CHUNK_SIZE)
@@ -133,19 +132,7 @@ class Chirp3STTStreamer:
133
  cloud_speech_types.StreamingRecognizeRequest(audio=chunk)
134
  for chunk in chunks
135
  )
136
-
137
- recognition_config = cloud_speech_types.RecognitionConfig(
138
- auto_decoding_config=cloud_speech_types.AutoDetectDecodingConfig(),
139
- language_codes=[CHIRP3_LANGUAGE],
140
- model="chirp_3",
141
- )
142
- streaming_config = cloud_speech_types.StreamingRecognitionConfig(
143
- config=recognition_config
144
- )
145
- config_request = cloud_speech_types.StreamingRecognizeRequest(
146
- recognizer=f"projects/{GOOGLE_PROJECT_ID}/locations/{CHIRP3_REGION}/recognizers/_",
147
- streaming_config=streaming_config,
148
- )
149
 
150
  def requests():
151
  yield config_request
 
19
 
20
  _CHUNK_SIZE = 24 * 1024 # stay under the 25 KB gRPC streaming limit
21
 
22
+ def _make_client() -> SpeechClient:
23
+ return SpeechClient(
24
+ credentials=_credentials,
25
+ client_options=ClientOptions(api_endpoint=f"{CHIRP3_REGION}-speech.googleapis.com"),
26
+ )
27
+
28
+
29
+ def _make_config_request() -> cloud_speech_types.StreamingRecognizeRequest:
30
+ recognition_config = cloud_speech_types.RecognitionConfig(
31
+ auto_decoding_config=cloud_speech_types.AutoDetectDecodingConfig(),
32
+ language_codes=[CHIRP3_LANGUAGE],
33
+ model="chirp_2",
34
+ )
35
+ streaming_config = cloud_speech_types.StreamingRecognitionConfig(
36
+ config=recognition_config
37
+ )
38
+ return cloud_speech_types.StreamingRecognizeRequest(
39
+ recognizer=f"projects/{GOOGLE_PROJECT_ID}/locations/{CHIRP3_REGION}/recognizers/_",
40
+ streaming_config=streaming_config,
41
+ )
42
+
43
 
44
  async def transcribe_audio(data: bytes, mimetype: str = "audio/wav") -> dict:
45
  """Transcribes a full audio file using Chirp 3 (Google Cloud Speech-to-Text V2)."""
46
  def _run() -> str:
47
+ logger.info("chirp3 transcribe_audio: data=%d bytes, project=%s", len(data), GOOGLE_PROJECT_ID)
48
+ client = _make_client()
 
 
 
 
 
49
  chunks = [data[i: i + _CHUNK_SIZE] for i in range(0, len(data), _CHUNK_SIZE)]
50
  audio_requests = (
51
  cloud_speech_types.StreamingRecognizeRequest(audio=chunk)
52
  for chunk in chunks
53
  )
54
+ config_request = _make_config_request()
 
 
 
 
 
 
 
 
 
 
 
55
 
56
  def requests():
57
  yield config_request
 
123
  logger.exception("Chirp 3 STT transcription failed")
124
 
125
  def _recognize(self, wav_data: bytes) -> str:
126
+ client = _make_client()
 
 
 
 
 
 
127
  chunks = [
128
  wav_data[i : i + _CHUNK_SIZE]
129
  for i in range(0, len(wav_data), _CHUNK_SIZE)
 
132
  cloud_speech_types.StreamingRecognizeRequest(audio=chunk)
133
  for chunk in chunks
134
  )
135
+ config_request = _make_config_request()
 
 
 
 
 
 
 
 
 
 
 
 
136
 
137
  def requests():
138
  yield config_request
src/tts/cartesia_client.py CHANGED
@@ -32,7 +32,7 @@ async def synthesize_stream(text: str) -> AsyncIterator[bytes]:
32
  "Content-Type": "application/json",
33
  }
34
 
35
- logger.info("Cartesia TTS: synthesizing '%s'", text[:60])
36
  async with httpx.AsyncClient(timeout=30) as client:
37
  async with client.stream("POST", CARTESIA_TTS_URL, json=payload, headers=headers) as response:
38
  if response.status_code >= 400:
 
32
  "Content-Type": "application/json",
33
  }
34
 
35
+ logger.info("Cartesia TTS: text_len=%d, text=%r", len(text), text)
36
  async with httpx.AsyncClient(timeout=30) as client:
37
  async with client.stream("POST", CARTESIA_TTS_URL, json=payload, headers=headers) as response:
38
  if response.status_code >= 400:
src/tts/gemini_client.py CHANGED
@@ -44,7 +44,7 @@ async def synthesize_stream(text: str) -> AsyncIterator[bytes]:
44
  last_exc: Exception | None = None
45
 
46
  for model in models_to_try:
47
- logger.info("Gemini TTS [%s]: synthesizing '%s'", model, text[:60])
48
 
49
  def _collect(model_name: str = model) -> list[bytes]:
50
  client = texttospeech.TextToSpeechClient(credentials=_credentials)
@@ -64,10 +64,17 @@ async def synthesize_stream(text: str) -> AsyncIterator[bytes]:
64
  input=texttospeech.StreamingSynthesisInput(text=text)
65
  )
66
 
67
- chunks = [r.audio_content for r in client.streaming_synthesize(_gen())]
 
 
68
  logger.info(
69
- "Gemini TTS [%s]: received %d chunks, %d bytes total",
70
- model_name, len(chunks), sum(len(c) for c in chunks),
 
 
 
 
 
71
  )
72
  return chunks
73
 
 
44
  last_exc: Exception | None = None
45
 
46
  for model in models_to_try:
47
+ logger.info("Gemini TTS [%s]: text_len=%d, text=%r", model, len(text), text)
48
 
49
  def _collect(model_name: str = model) -> list[bytes]:
50
  client = texttospeech.TextToSpeechClient(credentials=_credentials)
 
64
  input=texttospeech.StreamingSynthesisInput(text=text)
65
  )
66
 
67
+ all_chunks = [r.audio_content for r in client.streaming_synthesize(_gen())]
68
+ chunks = [c for c in all_chunks if c]
69
+ total_bytes = sum(len(c) for c in chunks)
70
  logger.info(
71
+ "Gemini TTS [%s]: received %d chunks (%d empty skipped), %d bytes total, aligned=%s, first32=%s",
72
+ model_name,
73
+ len(chunks),
74
+ len(all_chunks) - len(chunks),
75
+ total_bytes,
76
+ total_bytes % 2 == 0,
77
+ chunks[0][:32].hex() if chunks else "N/A",
78
  )
79
  return chunks
80