Skip to content

Commit d71a047

Browse files
committed
merge main into feat/local-unidic (pick up OJAD graceful-degradation fix #60)
# Conflicts: # api/accent/pipeline.py
2 parents 01fcd71 + d081abc commit d71a047

3 files changed

Lines changed: 42 additions & 7 deletions

File tree

api/accent/models.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,3 +200,8 @@ class AccentResponse(BaseModel):
200200
default=None,
201201
description="An object that describes the details of an error when one occurs",
202202
)
203+
warning: str | None = Field(
204+
default=None,
205+
description="A non-fatal warning when results are degraded, e.g. furigana "
206+
"returned without pitch accent because OJAD was unavailable",
207+
)

api/accent/ojad.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,20 @@
2323

2424
OJAD_URL = "https://www.gavo.t.u-tokyo.ac.jp/ojad/phrasing/index"
2525

26+
# Per-request timeout for the OJAD POST. Overrides the global client timeout so a
27+
# down / unresponsive OJAD fails fast (≈2s on a dead host) instead of hanging for
28+
# the full global 10s before the pipeline can degrade gracefully.
29+
OJAD_TIMEOUT = httpx.Timeout(5.0, connect=2.0)
30+
31+
32+
class OJADUnavailableError(Exception):
33+
"""OJAD could not be reached or returned an error.
34+
35+
Raised on any transport failure (connect / read timeout, connection
36+
refused) or non-2xx status. Callers should treat this as "no pitch
37+
contour available" and degrade rather than fail the whole request.
38+
"""
39+
2640

2741
async def get_ojad_result(
2842
query_text: str,
@@ -47,12 +61,14 @@ async def get_ojad_result(
4761

4862
# Send a POST and receive the website html code
4963
try:
50-
response = await client.post(OJAD_URL, data=data)
64+
response = await client.post(OJAD_URL, data=data, timeout=OJAD_TIMEOUT)
5165
response.raise_for_status()
5266
logger.debug(f"[OJAD] Status Code: {response.status_code}")
53-
except Exception:
54-
logger.exception("[OJAD] Request Failed")
55-
raise
67+
except httpx.HTTPError as e:
68+
# Covers ConnectTimeout / ReadTimeout / ConnectError as well as the
69+
# HTTPStatusError from raise_for_status() — all httpx.HTTPError subclasses.
70+
logger.warning(f"[OJAD] Unavailable: {e}")
71+
raise OJADUnavailableError(str(e)) from e
5672

5773
website = response.text
5874

api/accent/pipeline.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040

4141
from api.accent.align import align_accent
4242
from api.accent.models import AccentResponse, ErrorInfo, WordAccentResult
43-
from api.accent.ojad import get_ojad_result
43+
from api.accent.ojad import OJADUnavailableError, get_ojad_result
4444
from api.accent.postprocess import (
4545
apply_furigana_toggles,
4646
convert_furigana_script,
@@ -145,7 +145,21 @@ async def process_accent_chunk(
145145
# a normal contour; fugashi keeps the original surface so the
146146
# tokenizer's acronym-merge preserves `Wifi.7` for display.
147147
ojad_query_text = strip_acronym_dots_for_ojad(stripped_text)
148-
_ojad_surface, ojad_results = await get_ojad_result(ojad_query_text, client)
148+
149+
# OJAD only enriches the result with pitch accent. If it is down, fall
150+
# back to furigana-only output (align_accent emits a fallback word with
151+
# no pitch contour for every token when given an empty list) rather
152+
# than failing the request — see align_accent's `m == 0` branch.
153+
warning = None
154+
try:
155+
_ojad_surface, ojad_results = await get_ojad_result(ojad_query_text, client)
156+
except OJADUnavailableError as e:
157+
logger.warning(f"OJAD unavailable, degrading to furigana-only: {e}")
158+
ojad_results = []
159+
warning = (
160+
"OJAD pitch-accent service is unavailable; "
161+
"returning furigana without pitch accent."
162+
)
149163

150164
final_results = await align_accent(furigana_results, ojad_results)
151165
final_results = apply_accent_overrides(final_results)
@@ -180,7 +194,7 @@ async def process_accent_chunk(
180194
# script before serialisation. Hiragana is the no-op default.
181195
final_results = convert_furigana_script(final_results, script)
182196

183-
return AccentResponse(status=200, result=final_results)
197+
return AccentResponse(status=200, result=final_results, warning=warning)
184198

185199
except Exception as e:
186200
logger.exception(f"Unexpected error occurred: {text}")

0 commit comments

Comments
 (0)