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:
co-authored by
mergetest
Claude Fable 5
parent
75864a597f
commit
af6690840e
@@ -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
|
||||
Reference in New Issue
Block a user