add readming mode

This commit is contained in:
2026-10-26 17:31:08 +08:00
parent 9b7c7d726b
commit ed8df744f9
15 changed files with 565 additions and 9 deletions

View File

@@ -21,7 +21,7 @@ _TRANSLATE_BATCH_CHARS = 2_200
_TRANSLATE_CONTEXT_BEFORE = 8
_TRANSLATE_CONTEXT_AFTER = 4
_TRANSLATE_WORKERS = 4
_TRANSLATE_TIMEOUT_SECONDS = 45.0
_TRANSLATE_TIMEOUT_SECONDS = 120.0
_TRANSLATE_SPLIT_THRESHOLD = 4
@@ -35,6 +35,7 @@ class AITutor:
api_key: str
model: str
timeout_seconds: float
translation_timeout_seconds: float = _TRANSLATE_TIMEOUT_SECONDS
@property
def available(self) -> bool:
@@ -301,13 +302,26 @@ class AITutor:
user_parts.append(
"输出示例:{" + ",".join(f'"{index}":"中文"' for index in expected_indexes[:2]) + "}"
)
user_prompt = "\n".join(user_parts)
content = self._complete(
system_prompt,
"\n".join(user_parts),
user_prompt,
temperature=0.2,
timeout_seconds=max(self.timeout_seconds, _TRANSLATE_TIMEOUT_SECONDS),
timeout_seconds=max(self.timeout_seconds, self.translation_timeout_seconds),
)
parsed = _parse_translation_payload(content, expected_indexes)
try:
parsed = _parse_translation_payload(content, expected_indexes)
except AITutorError:
logger.warning(
"Translation response parse failed: endpoint=%s model=%s "
"expected_indexes=%s prompt=%r raw_content=%r",
self.request_url,
self.model,
expected_indexes,
user_prompt[:2000],
content[:2000],
)
raise
source_by_index = {index: text for index, text in batch}
missing = [
index
@@ -315,6 +329,25 @@ class AITutor:
if translation_needs_repair(source_by_index.get(index, ""), parsed.get(index, ""))
]
if missing:
invalid = [
{
"index": index,
"source": source_by_index.get(index, ""),
"translation": parsed.get(index, ""),
}
for index in missing
]
logger.warning(
"Translation response failed validation: endpoint=%s model=%s "
"expected_indexes=%s parsed=%r invalid=%r prompt=%r raw_content=%r",
self.request_url,
self.model,
expected_indexes,
parsed,
invalid,
user_prompt[:2000],
content[:2000],
)
raise AITutorError("AI translation returned an unexpected response.")
return {
index: _apply_translation_terms(source_by_index.get(index, ""), parsed[index], glossary)
@@ -348,6 +381,16 @@ class AITutor:
},
timeout=timeout,
)
except requests.Timeout as exc:
logger.warning(
"AI request timed out: endpoint=%s model=%s timeout_seconds=%s",
self.request_url,
self.model,
timeout,
)
raise AITutorError(
f"AI service request timed out after {timeout:g} seconds."
) from exc
except requests.RequestException as exc:
logger.exception(
"AI request failed: endpoint=%s model=%s",
@@ -454,11 +497,13 @@ def _parse_translation_payload(content: str, expected_indexes: list[int] | int)
else:
expected = list(expected_indexes)
text = _JSON_FENCE_RE.sub("", content.strip()).strip()
parse_errors: list[str] = []
for candidate in (text, *_json_snippets(text)):
try:
parsed = _translations_from_json(json.loads(candidate), expected)
except (TypeError, ValueError):
except (TypeError, ValueError) as exc:
parsed = None
parse_errors.append(str(exc))
if parsed:
return parsed
@@ -482,6 +527,14 @@ def _parse_translation_payload(content: str, expected_indexes: list[int] | int)
}
if len(by_order) == len(expected):
return by_order
logger.warning(
"Translation payload parser exhausted all formats: expected_indexes=%s "
"json_parse_errors=%r numbered_line_keys=%s content=%r",
expected,
parse_errors,
sorted(numbered),
text[:2000],
)
raise AITutorError("AI translation returned an unexpected response.")