Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions backend/app/api/docs/tts_evaluation/start_evaluation.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ Optional fields:

The evaluation will:
1. Process each text sample through the specified TTS models
2. Generate speech audio using Gemini Batch API
3. Store WAV audio files in S3 for human review
2. Generate speech audio: Gemini models via the Gemini Batch API; Sarvam and ElevenLabs models via direct API calls in background workers
3. Store 24 kHz mono WAV audio files in S3 for human review

**Supported models:** `gemini-2.5-pro-preview-tts`
Synthesis uses the dataset's language; datasets without a language are synthesized as `en-IN`.
The response's `run_metadata.voices` lists the voice used per model.

**Supported models:** `gemini-2.5-pro-preview-tts`, `gemini-3.1-flash-tts-preview`, `bulbul:v3` (Sarvam), `eleven_v3`, `eleven_v4` (ElevenLabs)
15 changes: 15 additions & 0 deletions backend/app/api/routes/tts_evaluations/evaluation.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
from app.services.tts_evaluations.constants import (
DEFAULT_STYLE_PROMPT,
DEFAULT_VOICE_NAME,
ELEVENLABS_TTS_MODELS,
ELEVENLABS_VOICE_ID,
SARVAM_SPEAKER,
SARVAM_TTS_MODELS,
)
from app.utils import APIResponse, load_description

Expand Down Expand Up @@ -114,11 +118,22 @@ def start_tts_evaluation(
detail=f"Failed to queue batch submission: {e}",
)

voices: dict[str, str] = {}
for model in run_create.models:
if model in SARVAM_TTS_MODELS:
voices[model] = SARVAM_SPEAKER
elif model in ELEVENLABS_TTS_MODELS:
voices[model] = ELEVENLABS_VOICE_ID
else:
voices[model] = DEFAULT_VOICE_NAME

return APIResponse.success_response(
data=TTSEvaluationRunPublic.from_model(
run,
run_metadata={
# Legacy key kept for existing clients; `voices` is per model.
"voice_name": DEFAULT_VOICE_NAME,
"voices": voices,
"style_prompt": DEFAULT_STYLE_PROMPT,
},
)
Expand Down
22 changes: 22 additions & 0 deletions backend/app/celery/tasks/job_execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,28 @@ def run_tts_result_processing(
)


@celery_app.task(bind=True, queue="default", priority=2)
@gevent_timeout(settings.CELERY_TASK_SOFT_TIME_LIMIT, "run_tts_sync_generation")
def run_tts_sync_generation(
self, project_id: int, job_id: str, trace_id: str, **kwargs
):
from app.services.tts_evaluations.sync_generation import (
execute_tts_sync_generation,
)

_set_trace(trace_id)
return _run_with_otel_parent(
self,
lambda: execute_tts_sync_generation(
project_id=project_id,
job_id=job_id,
task_id=current_task.request.id,
task_instance=self,
**kwargs,
),
)


@celery_app.task(bind=True, queue="default", priority=6)
@gevent_timeout(settings.CELERY_TASK_SOFT_TIME_LIMIT, "run_evaluation_fast_chunk")
def run_evaluation_fast_chunk(
Expand Down
18 changes: 18 additions & 0 deletions backend/app/celery/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,24 @@ def start_tts_result_processing(
return task_id


def start_tts_sync_generation(
project_id: int, job_id: str, trace_id: str = "N/A", **kwargs: Any
) -> str:
from app.celery.tasks.job_execution import run_tts_sync_generation

task_id = _enqueue_with_trace_context(
run_tts_sync_generation,
project_id=project_id,
job_id=job_id,
trace_id=trace_id,
**kwargs,
)
logger.info(
f"[start_tts_sync_generation] Started job {job_id} with Celery task {task_id}"
)
return task_id


def start_fast_evaluation_chunk(
eval_run_id: int, chunk_index: int, trace_id: str = "N/A"
) -> str:
Expand Down
5 changes: 5 additions & 0 deletions backend/app/core/batch/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ def start_batch_job(

batch_job = create_batch_job(session=session, batch_job_create=batch_job_create)

# create_batch_job's refresh() re-opens a transaction; end it so no pooled
# connection sits idle-in-transaction during the (slow) upload + create.
# Nothing is pending at this point, so this only releases the connection.
session.commit()

try:
batch_result = provider.create_batch(jsonl_data=jsonl_data, config=config)

Expand Down
Loading
Loading