Skip to content

fix(accent): degrade gracefully when OJAD is unavailable#60

Merged
torrid-fish merged 1 commit into
mainfrom
fix/ojad-graceful-degradation
Jun 4, 2026
Merged

fix(accent): degrade gracefully when OJAD is unavailable#60
torrid-fish merged 1 commit into
mainfrom
fix/ojad-graceful-degradation

Conversation

@torrid-fish

@torrid-fish torrid-fish commented Jun 4, 2026

Copy link
Copy Markdown
Member

Problem

OJAD (www.gavo.t.u-tokyo.ac.jp) is currently globally unreachable (80/443 TCP connect timing out, ping 100% loss — confirmed from an independent external egress, not an IP block). Because every accent chunk POSTs to OJAD for the per-mora pitch contour, an OJAD outage made /MarkAccent/:

  1. Hang for the full global 10s timeout (main.py httpx.AsyncClient(timeout=10.0)).
  2. Re-raise the httpx.ConnectTimeout from get_ojad_result (except Exception: raise).
  3. Surface as HTTP 500 via the pipeline's broad catch.

So users waited ~10s only to get a 500. Prod logs showed ~1898 ConnectTimeout / 1387 [OJAD] Request Failed in 24h.

Fix

Pitch accent from OJAD only enriches the furigana result, so an OJAD outage should degrade — not fail — the request.

  • api/accent/ojad.py — Add a short per-request timeout OJAD_TIMEOUT = httpx.Timeout(5.0, connect=2.0) so a down OJAD fails fast (~2s) instead of hanging on the global 10s. Replace except Exception: raise with except httpx.HTTPError (covers connect/read timeouts, connection errors, and raise_for_status()'s status errors) → log a warning and raise a dedicated OJADUnavailableError.
  • api/accent/pipeline.py — Catch OJADUnavailableError, set ojad_results = [] plus a warning, and continue. align_accent already emits accent_marking_type=0 for an empty OJAD list (its "MATCH FAILED" branch), so this yields furigana-only output at status 200. The broad except → 500 stays as a last-resort guard.
  • api/accent/models.py — Add an optional, backward-compatible warning field to AccentResponse for degraded results.

Verification

  • uv run ruff check api/accent/ and uv run mypy api/accent/ — both clean.
  • OJAD-down simulation (mocked ConnectTimeout): get_ojad_result raises OJADUnavailableError, the short timeout is passed through, and the pipeline returns status=200, warning set, all accent_marking_type==0, furigana preserved. No 500, no 10s hang.
  • Happy path (OJAD reachable): status=200, warning=null, real pitch contour preserved — no regression.

Close #59

OJAD (www.gavo.t.u-tokyo.ac.jp) outages made /MarkAccent/ hang for the
full global 10s timeout and then return HTTP 500, because get_ojad_result
re-raised the transport error and the pipeline turned it into a 500.

Pitch accent from OJAD only enriches the furigana result, so an OJAD
outage should not fail the whole request:

- ojad.py: add a short per-request timeout (connect=2s, read=5s) so a
  down OJAD fails fast instead of hanging on the global 10s, and raise a
  dedicated OJADUnavailableError on any httpx.HTTPError (connect/read
  timeouts, connection errors, non-2xx status).
- pipeline.py: catch OJADUnavailableError and degrade to furigana-only
  output (align_accent already emits accent_marking_type=0 for an empty
  OJAD list) with status 200 and a warning, instead of 500.
- models.py: add an optional, backward-compatible `warning` field to
  AccentResponse for degraded results.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown

🛡️ PR Quality Check Summary

PR Title: Passed (Length: 56/75, Format: OK). fix(accent): degrade gracefully when OJAD is unavailable
Branch Name: Follows naming convention (fix/ojad-graceful-degradation)
Commit Messages: All 1 commit(s) passed (Length, Format, Case)
Conflicts: No merge conflict markers found
Python Quality: All checks passed.


🎉 All checks passed!

@torrid-fish torrid-fish added this pull request to the merge queue Jun 4, 2026
Merged via the queue into main with commit d081abc Jun 4, 2026
12 checks passed
@torrid-fish torrid-fish deleted the fix/ojad-graceful-degradation branch June 4, 2026 07:09
torrid-fish added a commit that referenced this pull request Jun 4, 2026
torrid-fish added a commit that referenced this pull request Jun 4, 2026
The collected endpoint rebuilds AccentResponse from per-chunk results and
silently dropped the new `warning` field (#60), so OJAD-degraded responses
looked like full results. Keep the first chunk warning, mirroring the
first_error convention. The stream endpoint already passes it through via
model_dump().

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
torrid-fish added a commit that referenced this pull request Jun 4, 2026
The collected endpoint rebuilds AccentResponse from per-chunk results and
silently dropped the new `warning` field (#60), so OJAD-degraded responses
looked like full results. Keep the first chunk warning, mirroring the
first_error convention. The stream endpoint already passes it through via
model_dump().

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

OJAD graceful fallback

2 participants