Files
human-voice-rewrite-demo/schemas.py
T
LuminousRuoxi 85a2eb720a feat: 改写工作台体验优化 — 中文对照翻译 + 字数控制提示 + 复检历史/按建议再次改写 + 输入法组合期不误发送
- 新增 POST /api/translate(build_translate_prompt): 英文改写稿忠实直译成中文,供顾问自查改写后原意是否保留
- 改写区「译成中文对照」按钮 + 译文展开/收起;改写稿变化后旧译文自动过期(缓存按文本比对)
- 每段字数控制提示: rewrite-meta 显示「原文 X 词 · 当前 Y 词」,偏差过大黄色提醒;
  顶部进度加「全文 X / wordLimit 词」,超出题目字数限制标红
- 复检汇总建议历史: 每次复检入历史(状态/未通过检查项/返工目标/绑定版本,上限 10 条);
  Workbench 提交区 + 复检卡两处入口;历史条目「按此建议再次改写」回到目标段并显示返工 banner
- 对话输入框: 中文输入法组合期(isComposing/keyCode 229)回车不再误发送
- 测试: 新增 /api/translate 3 个 hermetic 测试(31 passed);浏览器实测 18/18 通过
2026-09-10 14:09:31 +08:00

182 lines
5.5 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 TranslateRequest(BaseModel):
text: str = Field(min_length=1)
# 段落上下文(可选):帮助模型稳定语气,不参与翻译输出
paragraph_id: str = ""
class TranslateResponse(BaseModel):
paragraph_id: str = ""
translation: str = ""