3v324v23 commited on
Commit
d2ec62c
·
1 Parent(s): 7868448

Switch podcast TTS to Gemini Kore

Browse files
Files changed (1) hide show
  1. start-hf.sh +150 -3
start-hf.sh CHANGED
@@ -125,6 +125,153 @@ if "project: Optional[str] = None" not in src or "google.auth.default(scopes=sco
125
  if count != 1:
126
  raise RuntimeError("Could not patch Esperanto Vertex TTS _get_access_token; source layout changed")
127
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  target.write_text(src, encoding="utf-8")
129
 
130
  print(f"Patched Vertex TTS provider: {target}")
@@ -212,7 +359,7 @@ async def upsert_speaker_profile(tts_model_id: str) -> str:
212
  speakers = [
213
  {
214
  "name": "Вядоўца",
215
- "voice_id": "en-US-Standard-A",
216
  "backstory": "Вядоўца беларускамоўнага падкаста.",
217
  "personality": "Warm, calm, clear, intelligent, natural delivery.",
218
  "voice_model": tts_model_id,
@@ -231,7 +378,7 @@ async def upsert_speaker_profile(tts_model_id: str) -> str:
231
  profile.description = ""
232
  profile.voice_model = tts_model_id
233
  profile.tts_provider = "vertex"
234
- profile.tts_model = "standard"
235
  profile.speakers = speakers
236
  await profile.save()
237
  return str(profile.id)
@@ -288,7 +435,7 @@ async def seed() -> None:
288
  flash_id = await upsert_model("gemini-2.5-flash", "language", credential_id)
289
  pro_id = await upsert_model("gemini-2.5-pro", "language", credential_id)
290
  embedding_id = await upsert_model("text-embedding-005", "embedding", credential_id)
291
- tts_id = await upsert_model("standard", "text_to_speech", credential_id)
292
  await set_default_models(flash_id, pro_id, embedding_id, tts_id)
293
  await upsert_speaker_profile(tts_id)
294
  await upsert_episode_profile(flash_id, flash_id)
 
125
  if count != 1:
126
  raise RuntimeError("Could not patch Esperanto Vertex TTS _get_access_token; source layout changed")
127
 
128
+ if "_is_gemini_tts_model" not in src:
129
+ new_generate = r''' def _is_gemini_tts_model(self) -> bool:
130
+ """Return True for Gemini text-to-speech models."""
131
+ model = self.model_name or ""
132
+ return model.startswith("gemini-") and "tts" in model
133
+
134
+ def _get_gemini_tts_language(self, kwargs: Dict) -> str:
135
+ """Resolve Gemini-TTS language code."""
136
+ config = getattr(self, "_config", {}) or {}
137
+ return (
138
+ kwargs.get("language_code")
139
+ or config.get("language_code")
140
+ or os.getenv("VERTEX_TTS_LANGUAGE")
141
+ or os.getenv("GOOGLE_CLOUD_REGION_LANGUAGE")
142
+ or "be-BY"
143
+ )
144
+
145
+ def _get_gemini_tts_prompt(self, kwargs: Dict) -> str:
146
+ """Resolve Gemini-TTS style prompt."""
147
+ config = getattr(self, "_config", {}) or {}
148
+ return (
149
+ kwargs.get("prompt")
150
+ or config.get("prompt")
151
+ or os.getenv("VERTEX_TTS_PROMPT")
152
+ or "Read the following text in Belarusian (Belarus), with a warm, calm, clear, intelligent, natural podcast delivery."
153
+ )
154
+
155
+ def _build_tts_payload(self, text: str, voice: str, kwargs: Dict) -> Dict:
156
+ """Build Cloud Text-to-Speech payload for legacy and Gemini-TTS models."""
157
+ if self._is_gemini_tts_model():
158
+ return {
159
+ "input": {
160
+ "prompt": self._get_gemini_tts_prompt(kwargs),
161
+ "text": text,
162
+ },
163
+ "voice": {
164
+ "languageCode": self._get_gemini_tts_language(kwargs),
165
+ "name": voice,
166
+ "modelName": self.model_name,
167
+ },
168
+ "audioConfig": {
169
+ "audioEncoding": kwargs.get("audio_encoding", "MP3"),
170
+ },
171
+ }
172
+
173
+ language_code = voice.split("-")[0] + "-" + voice.split("-")[1]
174
+ return {
175
+ "input": {
176
+ "text": text
177
+ },
178
+ "voice": {
179
+ "languageCode": language_code,
180
+ "name": voice
181
+ },
182
+ "audioConfig": {
183
+ "audioEncoding": "MP3"
184
+ }
185
+ }
186
+
187
+ def _audio_content_type(self, kwargs: Dict) -> str:
188
+ encoding = kwargs.get("audio_encoding", "MP3").upper()
189
+ if encoding == "LINEAR16":
190
+ return "audio/wav"
191
+ if encoding == "OGG_OPUS":
192
+ return "audio/ogg"
193
+ return "audio/mp3"
194
+
195
+ def generate_speech(
196
+ self,
197
+ text: str,
198
+ voice: str,
199
+ output_file: Optional[Union[str, Path]] = None,
200
+ **kwargs
201
+ ) -> AudioResponse:
202
+ """Generate speech from text."""
203
+ payload = self._build_tts_payload(text, voice, kwargs)
204
+
205
+ response = self.client.post(
206
+ f"{self.base_url}/text:synthesize",
207
+ headers=self._get_headers(),
208
+ json=payload
209
+ )
210
+ self._handle_error(response)
211
+
212
+ response_data = response.json()
213
+ audio_data = base64.b64decode(response_data["audioContent"])
214
+
215
+ response_audio = AudioResponse(
216
+ audio_data=audio_data,
217
+ content_type=self._audio_content_type(kwargs),
218
+ model=self.model_name or self._get_default_model(),
219
+ voice=voice,
220
+ provider="vertex"
221
+ )
222
+
223
+ if output_file:
224
+ output_path = Path(output_file)
225
+ output_path.parent.mkdir(parents=True, exist_ok=True)
226
+ output_path.write_bytes(audio_data)
227
+
228
+ return response_audio
229
+
230
+ async def agenerate_speech(
231
+ self,
232
+ text: str,
233
+ voice: str,
234
+ output_file: Optional[Union[str, Path]] = None,
235
+ **kwargs
236
+ ) -> AudioResponse:
237
+ """Generate speech from text asynchronously."""
238
+ payload = self._build_tts_payload(text, voice, kwargs)
239
+
240
+ response = await self.async_client.post(
241
+ f"{self.base_url}/text:synthesize",
242
+ headers=self._get_headers(),
243
+ json=payload
244
+ )
245
+ self._handle_error(response)
246
+
247
+ response_data = response.json()
248
+ audio_data = base64.b64decode(response_data["audioContent"])
249
+
250
+ response_audio = AudioResponse(
251
+ audio_data=audio_data,
252
+ content_type=self._audio_content_type(kwargs),
253
+ model=self.model_name or self._get_default_model(),
254
+ voice=voice,
255
+ provider="vertex"
256
+ )
257
+
258
+ if output_file:
259
+ output_path = Path(output_file)
260
+ output_path.parent.mkdir(parents=True, exist_ok=True)
261
+ output_path.write_bytes(audio_data)
262
+
263
+ return response_audio
264
+ '''
265
+
266
+ src, count = re.subn(
267
+ r" def generate_speech\([\s\S]*$",
268
+ new_generate,
269
+ src,
270
+ count=1,
271
+ )
272
+ if count != 1:
273
+ raise RuntimeError("Could not patch Esperanto Vertex TTS generate_speech; source layout changed")
274
+
275
  target.write_text(src, encoding="utf-8")
276
 
277
  print(f"Patched Vertex TTS provider: {target}")
 
359
  speakers = [
360
  {
361
  "name": "Вядоўца",
362
+ "voice_id": "Kore",
363
  "backstory": "Вядоўца беларускамоўнага падкаста.",
364
  "personality": "Warm, calm, clear, intelligent, natural delivery.",
365
  "voice_model": tts_model_id,
 
378
  profile.description = ""
379
  profile.voice_model = tts_model_id
380
  profile.tts_provider = "vertex"
381
+ profile.tts_model = "gemini-2.5-flash-tts"
382
  profile.speakers = speakers
383
  await profile.save()
384
  return str(profile.id)
 
435
  flash_id = await upsert_model("gemini-2.5-flash", "language", credential_id)
436
  pro_id = await upsert_model("gemini-2.5-pro", "language", credential_id)
437
  embedding_id = await upsert_model("text-embedding-005", "embedding", credential_id)
438
+ tts_id = await upsert_model("gemini-2.5-flash-tts", "text_to_speech", credential_id)
439
  await set_default_models(flash_id, pro_id, embedding_id, tts_id)
440
  await upsert_speaker_profile(tts_id)
441
  await upsert_episode_profile(flash_id, flash_id)