feat: 原文中文对照(逐句)— 高亮句同色同序号对位
- /api/translate 增加逐句模式(sentences): 前端拆句(en 为原文精确文本)→ 模型逐句翻译, 数量/顺序校验复用 _count_check(不一致自动重试);响应 en 原样回填保证对位可靠 - 左栏原文区新增「原文中文对照 →」: 逐句中文展示,命中批注 evidence 的句子 与原文同色(note-rhetoric/repeat/growth/structure)+ 同序号(sup), 点击对照行可联动高亮原文句与批注卡片 - 对照缓存按段保存并持久化(原文变化/换文书自动失效清空) - 测试: 新增逐句模式 2 个 hermetic 测试(33 passed) - 浏览器实测 18/18 通过(含逐句渲染/高亮对位/点击联动/刷新持久化)
This commit is contained in:
@@ -21,6 +21,7 @@ from prompts import (
|
||||
build_reference_prompt,
|
||||
build_scaffold_prompt,
|
||||
build_translate_prompt,
|
||||
build_translate_sentences_prompt,
|
||||
normalize_diagnose_payload,
|
||||
)
|
||||
from schemas import (
|
||||
@@ -36,6 +37,7 @@ from schemas import (
|
||||
RevisionTarget,
|
||||
ScaffoldRequest,
|
||||
ScaffoldResponse,
|
||||
SentencePair,
|
||||
TranslateRequest,
|
||||
TranslateResponse,
|
||||
)
|
||||
@@ -203,7 +205,12 @@ def reference(req: ReferenceRequest) -> ReferenceResponse:
|
||||
|
||||
@app.post("/api/translate", response_model=TranslateResponse)
|
||||
def translate(req: TranslateRequest) -> TranslateResponse:
|
||||
"""体验优化:改写区「译成中文对照」——顾问自查改写后原意/细节是否保留。"""
|
||||
"""体验优化:中文对照翻译。
|
||||
- text 整段模式:改写区「译成中文对照」(自查改写后原意/细节是否保留)
|
||||
- sentences 逐句模式:原文区「原文中文对照」(前端拆分句子,逐句对位,
|
||||
让「需要改写的高亮句」在中文对照里能同色找到)。"""
|
||||
if req.sentences:
|
||||
return _translate_sentences(req)
|
||||
if not req.text.strip():
|
||||
raise HTTPException(status_code=422, detail="没有可翻译的文本")
|
||||
system, user = build_translate_prompt(req.text)
|
||||
@@ -217,6 +224,28 @@ def translate(req: TranslateRequest) -> TranslateResponse:
|
||||
return resp
|
||||
|
||||
|
||||
def _translate_sentences(req: TranslateRequest) -> TranslateResponse:
|
||||
clean = [s for s in (x.strip() for x in req.sentences) if s]
|
||||
if not clean:
|
||||
raise HTTPException(status_code=422, detail="没有可翻译的句子")
|
||||
system, user = build_translate_sentences_prompt(clean)
|
||||
# 句子数量校验复用 _count_check:不一致自动重试一次(对位可靠性依赖等长同序)
|
||||
data = run_json("翻译", system, user, check=_count_check(len(clean), "sentences"))
|
||||
items = data.get("sentences") or []
|
||||
pairs: list[SentencePair] = []
|
||||
for i, en in enumerate(clean):
|
||||
zh = ""
|
||||
if i < len(items) and isinstance(items[i], dict):
|
||||
value = items[i].get("zh")
|
||||
if isinstance(value, list): # 模型偶发把字符串字段返回成数组
|
||||
value = "".join(str(x) for x in value)
|
||||
zh = str(value or "").strip()
|
||||
pairs.append(SentencePair(en=en, zh=zh))
|
||||
if not any(p.zh for p in pairs):
|
||||
raise HTTPException(status_code=502, detail="翻译结果为空,请重试")
|
||||
return TranslateResponse(paragraph_id=req.paragraph_id, sentences=pairs)
|
||||
|
||||
|
||||
@app.get("/")
|
||||
def index() -> FileResponse:
|
||||
return FileResponse(HERE / "index.html", media_type="text/html")
|
||||
|
||||
Reference in New Issue
Block a user