fix(translate): run Cinematic/Autofit on every engine (incl. default Argos), bound the fit pass, scrub provider errors (#910)

P0 — Cinematic/Autofit silently no-op'd on argos/nllb/openai. Those three
branches returned BEFORE _maybe_cinematic, so only the deep_translator
fall-through reached the refine/fit pass. A user on the DEFAULT Argos engine
who picked Cinematic/Autofit got plain Fast output with a success toast and
no quality_used/cinematic_skipped/rate_ratio. All three now route through
_maybe_cinematic. provider=openai is already an LLM translation, so it skips
the reflect/adapt re-refine (new already_llm flag) but still stamps
rate-ratio badges and runs the Autofit fit pass; the dialect it baked into
its translate prompt is now reported applied.

P1 — the Autofit fit pass ran one blocking adjust_for_slot per segment in the
merge loop, OUTSIDE any budget (a 50-seg dub vs a slow provider spun
~50×timeout unbounded). New speech_rate.adjust_for_slot_many fans it out
concurrently under a wall-clock deadline SHARED with the cinematic refine;
segments still running at the deadline degrade to their literal with
rate_error='fit-budget'. Also set max_retries=0 on the OpenAI clients used
for translate/refine/fit so a 429 + Retry-After can't sleep through the budget.

P2 — glossary auto-extract's no-LLM message now points at Settings → LLM
Providers (was the stale TRANSLATE_BASE_URL/TRANSLATE_API_KEY). Provider error
bodies on the glossary auto-extract, the OpenAI translate-segment path, and the
DeepL/Microsoft translate-segment path are now scrubbed
(core.scrub.scrub_provider_error) — they could echo the API key / a user_id.
DubTab re-polls LLM availability on window focus / visibility so configuring a
provider in Settings lifts the Cinematic gate without a remount. Documented
LLM_DEFAULT_PROVIDER in docs/dubbing/translation-engines.md.

Tests: fail-before/pass-after for argos+cinematic (refine runs), argos+cinematic
no-LLM (cinematic_skipped), argos Fast (rate_ratio stamped), openai+autofit
budget bound, and provider-error scrubbing on the translate + glossary paths.

Co-authored-by: mergetest <test@local>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Palash Debnath
2026-07-02 23:53:11 +05:30
committed by GitHub
co-authored by mergetest Claude Fable 5
parent 75864a597f
commit af6690840e
10 changed files with 559 additions and 87 deletions
+66
View File
@@ -0,0 +1,66 @@
"""Glossary auto-extract — provider-error scrubbing + no-LLM guidance.
The auto-extract endpoint reuses the translator's LLM client. A provider that
echoes the API key / a user_id / a home path in its error body must not surface
that verbatim in the 502 detail, and the no-LLM 503 must point users at the
current setup surface (Settings → LLM Providers), not the legacy env vars.
"""
import os
os.environ.setdefault("OMNIVOICE_DISABLE_FILE_LOG", "1")
import pytest
from fastapi import HTTPException
def _req(**kw):
# AutoExtractRequest is defined in the glossary router module.
from api.routers.glossary import AutoExtractRequest
return AutoExtractRequest(**kw)
def test_auto_extract_no_llm_points_at_llm_providers(monkeypatch):
from api.routers import glossary
from services import translator
monkeypatch.setattr(translator, "_llm_client", lambda: None)
req = _req(target_lang="es", segments=[{"text": "Hello Marcus"}])
with pytest.raises(HTTPException) as ei:
glossary.auto_extract("proj1", req)
detail = ei.value.detail
assert ei.value.status_code == 503
assert "LLM Providers" in detail
# The stale env-var-only guidance must be gone.
assert "TRANSLATE_BASE_URL" not in detail
assert "TRANSLATE_API_KEY" not in detail
def test_auto_extract_scrubs_provider_error(monkeypatch):
from api.routers import glossary
from services import translator
secret = "sk-LEAKLEAKLEAKLEAKLEAK12345"
home = "/Users/alice/videos"
class _Completions:
def create(self, **kw):
raise RuntimeError(f"401 bad key {secret} user_id=acct_9 at {home}")
class _Chat:
completions = _Completions()
class _Client:
chat = _Chat()
monkeypatch.setattr(translator, "_llm_client", lambda: _Client())
monkeypatch.setattr(translator, "_llm_model", lambda: "m")
monkeypatch.setattr(translator, "_llm_timeout", lambda: 1.0)
req = _req(target_lang="es", segments=[{"text": "Hello Marcus"}])
with pytest.raises(HTTPException) as ei:
glossary.auto_extract("proj1", req)
detail = ei.value.detail
assert ei.value.status_code == 502
assert secret not in detail
assert home not in detail
assert "***REDACTED***" in detail