- /api/translate 增加逐句模式(sentences): 前端拆句(en 为原文精确文本)→ 模型逐句翻译, 数量/顺序校验复用 _count_check(不一致自动重试);响应 en 原样回填保证对位可靠 - 左栏原文区新增「原文中文对照 →」: 逐句中文展示,命中批注 evidence 的句子 与原文同色(note-rhetoric/repeat/growth/structure)+ 同序号(sup), 点击对照行可联动高亮原文句与批注卡片 - 对照缓存按段保存并持久化(原文变化/换文书自动失效清空) - 测试: 新增逐句模式 2 个 hermetic 测试(33 passed) - 浏览器实测 18/18 通过(含逐句渲染/高亮对位/点击联动/刷新持久化)
191 lines
5.9 KiB
Python
191 lines
5.9 KiB
Python
"""Pydantic schemas for the Human Voice Rewrite demo API.
|
|
|
|
Aligned with PRD v1.1 §31 (AI 输出结构建议):
|
|
- §31.1 First Analysis -> AnalyzeRequest / AnalyzeResponse
|
|
- §31.2 Human Voice Diagnosis -> DiagnoseRequest / DiagnoseResponse
|
|
- §31.3 Recheck -> RecheckRequest / RecheckResponse
|
|
Plus Progressive Help endpoints (PRD §22: Scaffold → Reference).
|
|
"""
|
|
|
|
from typing import Literal
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
|
|
# ---------------------------------------------------------------- analyze
|
|
class AnalyzeRequest(BaseModel):
|
|
prompt: str = ""
|
|
word_limit: int | None = None
|
|
paragraphs: list[str] = Field(min_length=1)
|
|
constraints: list[str] = [] # advisor constraints already known before first pass
|
|
|
|
|
|
class ParagraphUnderstanding(BaseModel):
|
|
id: str = ""
|
|
original_text: str = ""
|
|
natural_meaning_zh: str = "" # 接近自然翻译的一段中文
|
|
semantic_anchor: str = "" # 必须保留的核心意思
|
|
optional_content_opportunity: str = ""
|
|
|
|
|
|
class PromptAlignment(BaseModel):
|
|
prompt_intent: str = ""
|
|
current_alignment: str = ""
|
|
optional_opportunity: str = ""
|
|
|
|
|
|
class AnalyzeResponse(BaseModel):
|
|
essay_summary: str = ""
|
|
prompt_alignment: PromptAlignment = PromptAlignment()
|
|
paragraphs: list[ParagraphUnderstanding] = []
|
|
|
|
|
|
# ---------------------------------------------------------------- diagnose
|
|
class DiagnoseRequest(BaseModel):
|
|
prompt: str = ""
|
|
word_limit: int | None = None
|
|
paragraphs: list[str] = Field(min_length=1)
|
|
# 优先级(PRD §10.1):顾问确认理解 > 顾问补充约束 > 模型初始理解
|
|
confirmed_anchors: list[str] = [] # 与 paragraphs 等长;非空条目覆盖模型初判
|
|
global_constraints: list[str] = []
|
|
paragraph_constraints: dict[str, list[str]] = {}
|
|
initial_analysis: AnalyzeResponse | None = None
|
|
|
|
|
|
PatternCategory = Literal["rhetoric", "repeat", "growth", "structure"]
|
|
|
|
|
|
class Pattern(BaseModel):
|
|
pattern_id: str = ""
|
|
name: str = ""
|
|
category: str = "" # rhetoric / repeat / growth / structure
|
|
affected_paragraphs: list[str] = []
|
|
evidence: list[str] = [] # 必须能在原文中找到的片段
|
|
why_ai_like: str = ""
|
|
human_impact: str = ""
|
|
transformation_rule: str = ""
|
|
|
|
|
|
class Annotation(BaseModel):
|
|
annotation_id: str = ""
|
|
pattern_id: str = ""
|
|
category: str = "rhetoric"
|
|
kind_label: str = ""
|
|
title: str = ""
|
|
evidence: list[str] = []
|
|
observation: str = ""
|
|
rewrite_action: str = ""
|
|
why_ai_like: str = ""
|
|
|
|
|
|
class ParagraphBrief(BaseModel):
|
|
paragraph_id: str = ""
|
|
confirmed_meaning: str = ""
|
|
rewrite_goal: str = ""
|
|
ai_focus: str = ""
|
|
must_preserve: list[str] = []
|
|
annotations: list[Annotation] = []
|
|
context_hint: str = ""
|
|
scaffold: str = ""
|
|
reference_snippet: str = ""
|
|
|
|
|
|
class DiagnoseResponse(BaseModel):
|
|
overall_diagnosis: str = ""
|
|
patterns: list[Pattern] = []
|
|
paragraph_briefs: list[ParagraphBrief] = []
|
|
optional_suggestion: str = "" # PRD §16 Optional 进阶建议(不阻塞)
|
|
|
|
|
|
# ---------------------------------------------------------------- recheck
|
|
class GlobalChecks(BaseModel):
|
|
semantic_preservation: str = "pass"
|
|
voice_consistency: str = "pass"
|
|
pattern_reduction: str = "pass"
|
|
new_pattern: str = "pass"
|
|
coherence: str = "pass"
|
|
reference_copying: str = "pass"
|
|
word_limit: str = "pass"
|
|
|
|
|
|
class RevisionTarget(BaseModel):
|
|
paragraph_id: str = "p1"
|
|
blocking_issue: str = ""
|
|
evidence: list[str] = []
|
|
single_revision_goal: str = "" # 单一明确返工目标
|
|
|
|
|
|
class RecheckResponse(BaseModel):
|
|
status: Literal["pass", "revision_required"]
|
|
checked_rewrite_version: str = ""
|
|
global_checks: GlobalChecks
|
|
revision_targets: list[RevisionTarget] = []
|
|
|
|
|
|
class RecheckRequest(BaseModel):
|
|
prompt: str = ""
|
|
word_limit: int | None = None
|
|
original_paragraphs: list[str] = Field(min_length=1)
|
|
confirmed_anchors: list[str] = []
|
|
global_constraints: list[str] = []
|
|
paragraph_constraints: dict[str, list[str]] = {}
|
|
rewrite_paragraphs: list[str] = Field(min_length=1)
|
|
diagnosis: DiagnoseResponse | None = None
|
|
references_shown: list[str] = [] # 仅用户实际展开过的参考
|
|
scaffolds_shown: list[str] = [] # 仅用户实际展开过的支架
|
|
rewrite_version: str = ""
|
|
previous_recheck: RecheckResponse | None = None
|
|
previous_revision_target: RevisionTarget | None = None
|
|
|
|
|
|
# ---------------------------------------------------------------- progressive help
|
|
class ScaffoldRequest(BaseModel):
|
|
paragraph_id: str
|
|
original_text: str
|
|
semantic_anchor: str
|
|
rewrite_goal: str = ""
|
|
global_constraints: list[str] = []
|
|
paragraph_constraints: list[str] = []
|
|
|
|
|
|
class ScaffoldResponse(BaseModel):
|
|
paragraph_id: str = ""
|
|
scaffold: str = ""
|
|
|
|
|
|
class ReferenceRequest(BaseModel):
|
|
paragraph_id: str
|
|
original_text: str
|
|
semantic_anchor: str
|
|
rewrite_goal: str = ""
|
|
primary_goal: str = "" # 兼容旧字段名
|
|
global_constraints: list[str] = []
|
|
paragraph_constraints: list[str] = []
|
|
|
|
|
|
class ReferenceResponse(BaseModel):
|
|
paragraph_id: str = ""
|
|
starter: str = "" # 1-2 句英文局部参考,非整段
|
|
reference_snippet: str = ""
|
|
|
|
|
|
# ------------------------------------------------------- 体验优化:中文对照翻译
|
|
class SentencePair(BaseModel):
|
|
en: str = "" # 原文句子(前端拆分,服务端原样回填,保证高亮对位可靠)
|
|
zh: str = ""
|
|
|
|
|
|
class TranslateRequest(BaseModel):
|
|
# 整段模式(改写稿翻译):text 必填;
|
|
# 逐句模式(原文中文对照):sentences 必填。二者取其一。
|
|
text: str = ""
|
|
sentences: list[str] = []
|
|
# 段落上下文(可选):帮助模型稳定语气,不参与翻译输出
|
|
paragraph_id: str = ""
|
|
|
|
|
|
class TranslateResponse(BaseModel):
|
|
paragraph_id: str = ""
|
|
translation: str = "" # 整段模式
|
|
sentences: list[SentencePair] = [] # 逐句模式(与请求句子等长、同序)
|