Skip to content

Prove spoken-language transcription end to end; fix CORS for X-Request-ID - #45

Merged
Chaddacus merged 2 commits into
devfrom
codex/multilingual-transcription
Sep 1, 2026
Merged

Chaddacus merged 2 commits into
devfrom
codex/multilingual-transcription

Conversation

@Chaddacus

@Chaddacus Chaddacus commented Sep 1, 2026

Copy link
Copy Markdown
Owner

Why

A user reported Spanish audio transcribed as English. Grounding on production showed the report predates the Deepgram cutover (#38, deployed 2026-06-29) and the current auto path already keeps the spoken language. This PR adds the proof and hardening instead of a provider change, and fixes a CORS defect found while proving it.

What

  • Decision record: docs/adr/004-language-detection-strategy.md — keep detect_language=true; language=multi rejected (wrecks ko/zh, probe table inside).
  • Fixtures: backend/tests/fixtures/audio/ — es-MX, es-ES, en-US, ko, zh, mixed en→es (348 KB total, synthetic voices).
  • Unit tests: tests/test_language_detection.py (16 tests, recorded provider shapes).
  • Live provider tests: tests/live/pytest -m live, excluded from the default run.
  • Browser proof: frontend/e2e/tests/multilingual-upload.spec.js — real upload → real Deepgram → Spanish text + Spanish badge. Opt-in with CLIO_LIVE_E2E=1.
  • UI: language badge shows the language name ("Spanish") with a lang attribute and data-testid="note-language".
  • CORS fix: x-request-id added to CORS_ALLOW_HEADERS + tests/test_cors_preflight.py. The browser dropped POST /api/notes/ after preflight on any cross-origin deployment (dev stack, CI e2e). Production is same-origin and unaffected.

Evidence

  • pytest: 139 passed, 6 live deselected. pytest -m live: 6 passed against real Deepgram.
  • ruff check: clean. tsc --noEmit: clean. npm run lint: 0 errors (12 pre-existing warnings).
  • Playwright multilingual-upload.spec.js: 2/2 passed against the dev stack; screenshots inspected (Spanish badge, untranslated Spanish text; mixed clip keeps both languages).
  • Full existing Playwright suite: 49 passed, 4 failed. The 4 are pre-existing and unrelated: health and registration pass when run alone (failures were 429 throttling under the parallel run); comprehensive-harness and production-sweep look for an h1 "Record Voice Note" that the page no longer has ("New Voice Note").

Not in scope

  • Translation ("translate to this language") is a separate AI capability with its own contract and evals.
  • production-sweep.spec.js runs against production and registers users there; flagged, not changed.

Acceptance mapping

Owner-confirmed acceptance, three clauses, and where each is pinned:

  1. "A Spanish recording comes back as Spanish text" — tests/live/test_deepgram_live.py (es-MX, es-ES) and the Playwright Spanish case.
  2. "A recording that switches between English and Spanish keeps each part in its own language" — test_mixed_english_spanish_keeps_both_languages (live) and the Playwright mixed case.
  3. "The original transcript is never replaced" — test_spanish_text_is_kept_not_translated (unit) and the Playwright assertion that the Spanish note does not contain the English rendering. The explicit, user-invoked Re-transcribe action does overwrite the transcript by design; that path is unchanged here and stays covered by the pre-existing test_retranscribe_task_happy_path.

Review

Fresh-context adversarial review: approve with minors. Both addressed in the follow-up commit: the badge no longer carries a lang attribute (its label is English), the detected code is exposed as data-language instead; this section adds the acceptance mapping.

…t-ID

A user reported Spanish audio coming back as English. The report predates the
Deepgram cutover (#38); the current auto path (detect_language=true) already
keeps the spoken language, and Deepgram's `multi` mode was rejected because it
wrecks Korean and Chinese (ADR-004 has the probe table). So this change adds
the proof and hardening rather than a provider change:

- backend/tests/fixtures/audio/: short synthetic clips (macOS voices) for
  es-MX, es-ES, en-US, ko, zh and an English-then-Spanish mix.
- tests/test_language_detection.py: recorded-response tests pinning that auto
  asks for detection and stores whatever Deepgram detects, for every offered
  language; Spanish text is stored verbatim.
- tests/live/: real fixtures through real Deepgram, `pytest -m live`
  (excluded from the default run via pytest.ini).
- frontend/e2e/tests/multilingual-upload.spec.js: uploads the Spanish and the
  mixed clip through the real UI and asserts Spanish text plus a Spanish badge
  (opt-in with CLIO_LIVE_E2E=1).
- NoteDetailPage: the language badge now shows "Spanish" instead of "es",
  carries a lang attribute and a test id; the option list is hoisted so the
  badge and the re-transcribe dialog share it.

Found while running the browser proof: the frontend sends X-Request-ID on
note creation (#41) but CORS_ALLOW_HEADERS did not list it, so on any
cross-origin deployment (dev stack, CI e2e) the browser dropped the POST after
preflight. Added the header and tests/test_cors_preflight.py. Production is
same-origin and was not affected.
…language

Review finding: the badge text is an English label (Spanish), so lang="es"
declared the wrong language for that content. The detected code is now exposed
as data-language for tests and tooling; the e2e assertion follows.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant