"""E2E stub server: deterministic LLM payloads per workflow stage. Injects a dispatcher into main_mod.get_client so the real FastAPI app, pydantic validation, evidence sanitization and the frontend run unchanged. Run on the same port the real server uses (index.html fetches /api/* same-origin). """ import uvicorn import main as main_mod ANALYZE = { "essay_summary": "文章以一支静止的铅笔开头,描述作者遇到一个无法立即解决的问题,并从中学会了耐心,最后回到书桌前继续面对。", "prompt_alignment": {"prompt_intent": "", "current_alignment": "", "optional_opportunity": ""}, "paragraphs": [ { "id": "p1", "original_text": "The pencil in my hand was still. I wanted to find the answer.", "natural_meaning_zh": "我手里的铅笔静止不动,我想要找到答案。", "semantic_anchor": "铅笔静止暗示思考停滞,内心渴望答案。", "optional_content_opportunity": "", }, { "id": "p2", "original_text": "I met a problem that I could not solve right away.", "natural_meaning_zh": "我遇到了一个无法立刻解决的问题。", "semantic_anchor": "明确点出问题的存在,且无法立即解决。", "optional_content_opportunity": "", }, { "id": "p3", "original_text": "I learned to be patient.", "natural_meaning_zh": "我学会了耐心。", "semantic_anchor": "核心教训是耐心,但未说明如何学到。", "optional_content_opportunity": "", }, { "id": "p4", "original_text": "Now, I sit at my desk again.", "natural_meaning_zh": "现在,我再次坐在书桌前。", "semantic_anchor": "回到起点,暗示重新面对问题,但未说明结果。", "optional_content_opportunity": "", }, ], } DIAGNOSE = { "overall_diagnosis": "文章极短,但每一段都呈现典型的生成式写作模式:抽象陈述、缺乏具体细节、结构过于简化和对称。", "patterns": [ { "pattern_id": "P02", "name": "Explicit Lesson", "category": "growth", "affected_paragraphs": ["p3"], "evidence": ["I learned to be patient."], "why_ai_like": "总结式结尾是生成模型最顺手的收束方式。", "human_impact": "直接说教,读者看不到具体如何学会。", "transformation_rule": "Lesson -> Change in Judgment", }, { "pattern_id": "P08", "name": "Circular Ending", "category": "structure", "affected_paragraphs": ["p4"], "evidence": ["Now, I sit at my desk again."], "why_ai_like": "首尾呼应结构容易讨巧。", "human_impact": "只剩结构对称,没有新信息。", "transformation_rule": "Close on a Concrete Action", }, ], "paragraph_briefs": [ { "paragraph_id": "p1", "confirmed_meaning": "铅笔静止暗示思考停滞,内心渴望答案。", "rewrite_goal": "把静止的铅笔转化为具体的场景与内心活动。", "ai_focus": "抽象陈述;缺乏具体细节", "must_preserve": [], "annotations": [ { "pattern_id": "P01", "category": "rhetoric", "kind_label": "修辞包装", "title": "象征开头", "evidence": ["The pencil in my hand was still."], "observation": "用静止铅笔暗示停滞,但没给任何具体场景。", "rewrite_action": "补一个真实瞬间:笔停在纸上的位置、当时我在做什么。", "why_ai_like": "象征开头是生成模型高频起笔方式。", } ], "context_hint": "上一段已完成 0 个反射,这一段只需建立画面。", "scaffold": "The pencil stayed in my hand because ______.", "reference_snippet": "The pencil had stopped halfway across the page.", }, { "paragraph_id": "p2", "confirmed_meaning": "明确点出问题的存在,且无法立即解决。", "rewrite_goal": "把问题具体化,写出具体内容和无法解决的原因。", "ai_focus": "抽象陈述", "must_preserve": [], "annotations": [ { "pattern_id": "P01", "category": "rhetoric", "kind_label": "修辞包装", "title": "泛化问题", "evidence": ["a problem that I could not solve"], "observation": "问题本身是空洞的泛指。", "rewrite_action": "写出问题具体是什么。", "why_ai_like": "泛化名词让生成模型无需真实素材。", } ], "context_hint": "上一段已建立画面,这一段写具体困境。", "scaffold": "The problem was ______.", "reference_snippet": "", }, { "paragraph_id": "p3", "confirmed_meaning": "核心教训是耐心,但未说明如何学到。", "rewrite_goal": "把耐心转化为具体行为,并保留 secret code。", "ai_focus": "总结式说教", "must_preserve": ["secret code"], "annotations": [ { "pattern_id": "P02", "category": "growth", "kind_label": "抽象总结", "title": "直接点题", "evidence": ["I learned to be patient."], "observation": "用一句总结构束教训。", "rewrite_action": "写出耐心在行为上如何体现,保留 secret code。", "why_ai_like": "教训总结是生成模型收束习惯。", } ], "context_hint": "上一段已写具体困境,这一段写困境中的一次具体选择。", "scaffold": "Each day I ______ instead of ______.", "reference_snippet": "", }, { "paragraph_id": "p4", "confirmed_meaning": "回到起点,暗示重新面对问题,但未说明结果。", "rewrite_goal": "把回到起点转化为具体行动,暗示改变但不总结。", "ai_focus": "结构对称", "must_preserve": [], "annotations": [ { "pattern_id": "P08", "category": "structure", "kind_label": "结构路标", "title": "首尾呼应", "evidence": ["Now, I sit at my desk again."], "observation": "用呼应结构收尾,没有新内容。", "rewrite_action": "写一个具体的下一步动作。", "why_ai_like": "首尾对称让文章看起来完整。", } ], "context_hint": "上一段已写具体选择,这一段以行动收尾,不写总结。", "scaffold": "This time, I ______.", "reference_snippet": "", }, ], "optional_suggestion": "若有真实素材,可补当时问题发生的具体场景。", } RECHECK_PASS = { "status": "pass", "checked_rewrite_version": "rv_1", "global_checks": { "semantic_preservation": "pass", "voice_consistency": "pass", "pattern_reduction": "pass", "new_pattern": "pass", "coherence": "pass", "reference_copying": "pass", "word_limit": "pass", }, "revision_targets": [], } SCAFFOLD = {"paragraph_id": "p1", "scaffold": "The pencil stayed in my hand because ______."} REFERENCE = {"paragraph_id": "p1", "starter": "The pencil had stopped halfway across the page.", "reference_snippet": "The pencil had stopped halfway across the page."} RECHECK_REVISION = { "status": "revision_required", "checked_rewrite_version": "rv_1", "global_checks": { "semantic_preservation": "pass", "voice_consistency": "pass", "pattern_reduction": "pass", "new_pattern": "pass", "coherence": "pass", "reference_copying": "pass", "word_limit": "pass", }, "revision_targets": [ { "paragraph_id": "p3", "blocking_issue": "新版本仍是 Explicit Lesson:直接总结学到了什么。", "evidence": ["gradually came to realize"], "single_revision_goal": "不要总结我学到了什么,写判断怎么变化。", } ], } class StubCompleter: def complete_json(self, system, user, validate=None): # NOTE: system prompts share a _TASK_CHAIN prefix (contains 全文复检 etc.) # so dispatch must use stage-unique keywords, most specific first. if "给我一个写作起点" in system: return SCAFFOLD if "Level 2" in system: return REFERENCE if "Human Voice Diagnosis" in system: return DIAGNOSE if "最终阶段" in system: # mockFillFail injects revision-case wording; detect it to serve the # revision_required path, otherwise the golden-path pass response. # NOTE: cannot use "gradually came to realize" — previous_revision_target # is embedded in the prompt (with its evidence quoting that phrase), so it # matches on every resubmission even when the current text is fixed. if "uncertainty is an important part of growth" in user: return RECHECK_REVISION return RECHECK_PASS return ANALYZE main_mod.get_client = lambda: StubCompleter() if __name__ == "__main__": uvicorn.run(main_mod.app, host="127.0.0.1", port=8000, log_level="warning")