fixed a bug:translate maybe timeout

This commit is contained in:
2026-10-03 19:49:21 +08:00
parent 6bd64ee4a9
commit 9daea7b807
10 changed files with 554 additions and 99 deletions

View File

@@ -1,6 +1,10 @@
import pytest
from sentence_api.ai_tutor import AITutor, AITutorError
import json
import re
import threading
from sentence_api.ai_tutor import AITutor, AITutorError, _parse_translation_payload
class FakeResponse:
@@ -73,15 +77,13 @@ def test_ai_tutor_rejects_unexpected_response(monkeypatch):
tutor.explain("Hello.")
from sentence_api.ai_tutor import _parse_translation_payload
def test_ai_tutor_translates_full_document(monkeypatch):
requests_payloads = []
def fake_post(endpoint, headers, json, timeout):
requests_payloads.append((endpoint, headers, json, timeout))
return FakeResponse(body={"choices": [{"message": {"content": '["你好。", "见到你很高兴。"]'}}]})
return FakeResponse(body={"choices": [{"message": {"content": '{"0":"你好。","1":"见到你很高兴。"}'}}]})
monkeypatch.setattr("sentence_api.ai_tutor.requests.post", fake_post)
tutor = AITutor("https://ai.test/v1", "secret", "test-model", 1.5)
@@ -94,11 +96,87 @@ def test_ai_tutor_translates_full_document(monkeypatch):
assert translated == {0: "你好。", 1: "见到你很高兴。"}
_endpoint, _headers, payload, timeout = requests_payloads[0]
user_prompt = payload["messages"][1]["content"]
assert "Hello." in user_prompt
assert "Nice to meet you." in user_prompt
assert timeout == 90.0
assert "[0] Hello." in user_prompt
assert "[1] Nice to meet you." in user_prompt
assert timeout == 45.0
def _translation_response_from_prompt(payload):
user_prompt = payload["messages"][1]["content"]
section = user_prompt.split("待翻译句子:", 1)[1]
indexes = [int(match) for match in re.findall(r"^\[(\d+)\]", section, re.M)]
body = {str(index): f"中文{index}" for index in indexes}
return FakeResponse(
body={"choices": [{"message": {"content": json.dumps(body, ensure_ascii=False)}}]}
)
def test_ai_tutor_translates_long_input_in_batches(monkeypatch):
requests_payloads = []
lock = threading.Lock()
def fake_post(endpoint, headers, json, timeout):
with lock:
requests_payloads.append(json)
return _translation_response_from_prompt(json)
monkeypatch.setattr("sentence_api.ai_tutor.requests.post", fake_post)
tutor = AITutor("https://ai.test/v1", "secret", "test-model", 1)
sentences = [(index, f"This is sentence {index} with enough text.") for index in range(30)]
translated = tutor.translate_sentences(sentences, course_title="Long talk")
assert translated == {index: f"中文{index}" for index in range(30)}
assert len(requests_payloads) >= 3
first_prompt = requests_payloads[0]["messages"][1]["content"]
assert "前文" in first_prompt or "后文" in first_prompt or "待翻译句子:" in first_prompt
def test_ai_tutor_skips_cached_sentences_and_persists_windows(monkeypatch):
persisted = []
def fake_post(endpoint, headers, json, timeout):
return _translation_response_from_prompt(json)
monkeypatch.setattr("sentence_api.ai_tutor.requests.post", fake_post)
tutor = AITutor("https://ai.test/v1", "secret", "test-model", 1)
translated = tutor.translate_sentences(
[(0, "Hello."), (1, "Thanks."), (2, "Bye.")],
existing={0: "你好。"},
persist=persisted.append,
course_title="Greetings",
)
assert translated[0] == "你好。"
assert translated[1] == "中文1"
assert translated[2] == "中文2"
assert persisted
assert 0 not in persisted[0]
def test_ai_tutor_splits_invalid_windows_and_retries(monkeypatch):
calls = {"count": 0}
def fake_post(endpoint, headers, json, timeout):
calls["count"] += 1
if calls["count"] == 1:
return FakeResponse(body={"choices": [{"message": {"content": "not-json"}}]})
return _translation_response_from_prompt(json)
monkeypatch.setattr("sentence_api.ai_tutor.requests.post", fake_post)
tutor = AITutor("https://ai.test/v1", "secret", "test-model", 1)
sentences = [(index, f"This is sentence {index} with enough text.") for index in range(12)]
translated = tutor.translate_sentences(sentences)
assert translated == {index: f"中文{index}" for index in range(12)}
assert calls["count"] >= 3
def test_parse_translation_payload_accepts_fenced_json_and_numbered_lines():
assert _parse_translation_payload('```json\n["一", "二"]\n```', 2) == ["一", "二"]
assert _parse_translation_payload("1. 一\n2. 二", 2) == ["一", "二"]
assert _parse_translation_payload('```json\n["一", "二"]\n```', 2) == {0: "一", 1: "二"}
assert _parse_translation_payload("1. 一\n2. 二", 2) == {0: "一", 1: "二"}
assert _parse_translation_payload('prefix {"12":"甲","13":"乙"} suffix', [12, 13]) == {
12: "甲",
13: "乙",
}