The second review still failed on missing database and route coverage, an unmarked relationship structure, a stuck English lease, and a sidebar that fetched the product switch itself. Co-Authored-By: Grok (agent loop) Co-authored-by: Cursor <cursoragent@cursor.com>
190 lines
8.4 KiB
Python
190 lines
8.4 KiB
Python
"""Relationship-language gate. The guide's example, padded to the length floor, passes."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import copy
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
SCRIPTS = ROOT / "scripts"
|
|
if str(SCRIPTS) not in sys.path:
|
|
sys.path.insert(0, str(SCRIPTS))
|
|
|
|
from marriage_match_service.synastry_full_reader_gate import validate_synastry_reader
|
|
|
|
GUIDE = (
|
|
"你们这段关系的底色是有吸引,但节奏不同。刚靠近时,彼此容易觉得对方身上有自己缺的东西,所以会有自然的牵引。"
|
|
"真正进入日常后,差异会浮出来:一个更习惯用行动表达关心,一个更需要语言和情绪回应;压力大的时候,一个想立刻解决,一个想先躲开。"
|
|
"这不是谁对谁错,而是你们的安全感开关不在同一个位置。你们的优势在于,只要愿意把话说明白,理解力是在的;"
|
|
"难点在于,情绪上来时容易把我需要空间听成你不在乎我。长期看,这段关系不是靠感觉自动顺滑的类型,"
|
|
"而是靠两个人把日常回应、冲突修复和现实安排一点点对齐。如果愿意练,它会长出很稳的默契;如果回避差异,摩擦会反复出现在同一个地方。"
|
|
)
|
|
FILLER = "你们可以慢慢把彼此的节奏说清楚,把靠近、停顿和修复都留在两个人都能接受的距离里。"
|
|
|
|
|
|
def _prose(seed: str) -> str:
|
|
text = seed
|
|
while len(text.replace(" ", "")) < 280:
|
|
text += FILLER
|
|
return text
|
|
|
|
|
|
def _reason(plain: str, basis: str, evidence: str) -> dict:
|
|
return {
|
|
"plain": plain,
|
|
"basis": basis,
|
|
"evidence": evidence,
|
|
"label": "single_system_inference",
|
|
}
|
|
|
|
|
|
def _passing(language: str = "zh") -> tuple[dict, dict]:
|
|
evidence = "金星落在第七宫,同时木星大运覆盖接下来这一段"
|
|
inputs = {
|
|
section: {"quote": evidence, "note": "双方都需要把靠近说出来"}
|
|
for section in (
|
|
"climate", "emotion", "communication", "safety", "conflict", "daily", "direction", "practices",
|
|
)
|
|
}
|
|
inputs["timeline"] = {"quote": "程序分好的三段"}
|
|
reasons = [
|
|
_reason("所以你们靠近时会先感到彼此补上了一块缺口。", evidence, evidence),
|
|
_reason("所以压力一大,一个想说开,一个想先留出空间。", evidence, evidence),
|
|
]
|
|
titles = ("关系基调", "情感节奏", "沟通与理解", "安全感与亲密", "冲突与修复", "生活节律与现实协作", "长期方向")
|
|
sections = []
|
|
for index, section_id in enumerate(("climate", "emotion", "communication", "safety", "conflict", "daily", "direction")):
|
|
text = _prose(GUIDE if index == 0 else f"你们在{titles[index]}上需要彼此对齐。{GUIDE}")
|
|
if language == "en":
|
|
text = " ".join(["You two meet a different pace and can still name what each person needs."] * 90)
|
|
sections.append({
|
|
"id": section_id,
|
|
"title": titles[index],
|
|
"text": text,
|
|
"reasons": reasons if language == "zh" else [
|
|
_reason("So closeness asks both of you to say what care looks like.", "Venus in the 7th house during Jupiter dasha", evidence),
|
|
_reason("So pressure sends one person toward talk and the other toward room.", "Venus in the 7th house during Jupiter dasha", evidence),
|
|
],
|
|
})
|
|
if language == "en":
|
|
evidence = "Venus in the 7th house during Jupiter dasha"
|
|
for section_id, payload in inputs.items():
|
|
if section_id == "timeline":
|
|
continue
|
|
payload["quote"] = evidence
|
|
for section in sections:
|
|
if section["id"] == "timeline":
|
|
continue
|
|
for reason in section["reasons"]:
|
|
reason["evidence"] = evidence
|
|
reason["basis"] = evidence
|
|
sections.append({
|
|
"id": "timeline",
|
|
"segments": [
|
|
{"id": "s1", "phase": "靠近", "text": _prose("这段时间你们适合慢慢认识,把节奏说清楚。") if language == "zh" else "This stretch is for learning each other's pace. " * 20, "basis": evidence},
|
|
{"id": "s2", "phase": "对齐", "text": _prose("这段时间适合把日常安排摊开谈。") if language == "zh" else "This stretch is for naming daily arrangements. " * 20, "basis": evidence},
|
|
{"id": "s3", "phase": "稳住", "text": _prose("这段时间关系看你们是否把差异留在桌上。") if language == "zh" else "This stretch shows whether differences stay discussable. " * 20, "basis": evidence},
|
|
],
|
|
})
|
|
practice = "挑一件日常回应,这周各自说一次自己需要的靠近方式。" if language == "zh" else "Pick one daily response and each say what closeness you need."
|
|
sections.append({
|
|
"id": "practices",
|
|
"items": [{"text": practice, "evidence": evidence if language == "en" else "金星落在第七宫,同时木星大运覆盖接下来这一段"}],
|
|
})
|
|
return {"sections": sections}, inputs
|
|
|
|
|
|
def test_guide_example_passes_when_length_and_reasons_are_filled():
|
|
delivery, inputs = _passing("zh")
|
|
result = validate_synastry_reader(delivery, inputs, "zh")
|
|
assert result["ok"], result["failures"]
|
|
|
|
|
|
def test_english_guide_shape_passes():
|
|
delivery, inputs = _passing("en")
|
|
result = validate_synastry_reader(delivery, inputs, "en")
|
|
assert result["ok"], result["failures"]
|
|
|
|
|
|
def _one(delivery: dict, inputs: dict, language: str = "zh") -> set[str]:
|
|
result = validate_synastry_reader(delivery, inputs, language)
|
|
assert result["ok"] is False
|
|
return {row["code"] for row in result["failures"]}
|
|
|
|
|
|
def test_basis_house_that_is_not_in_the_evidence_fails():
|
|
delivery, inputs = _passing("zh")
|
|
delivery["sections"][0]["reasons"][0]["basis"] = "金星落在第七宫,改成第八宫"
|
|
assert "basis_mismatch" in _one(delivery, inputs)
|
|
|
|
|
|
def test_each_banned_insertion_fails_once():
|
|
samples = {
|
|
"金星": "banned_term",
|
|
"七宫": "banned_term",
|
|
"22 分": "score",
|
|
"注定": "banned_judgement",
|
|
"2027 年结婚": "dated_event",
|
|
"宿曜": "supplementary_method",
|
|
"execution_receipt": "private_term",
|
|
}
|
|
for snippet, code in samples.items():
|
|
delivery, inputs = _passing("zh")
|
|
delivery["sections"][1]["text"] += snippet
|
|
assert code in _one(delivery, inputs), snippet
|
|
|
|
|
|
def test_analytic_opening_missing_section_and_missing_claim_fail():
|
|
delivery, inputs = _passing("zh")
|
|
delivery["sections"][0]["text"] = "这段关系的核心不是顺滑。" + delivery["sections"][0]["text"]
|
|
assert "analytic_opening" in _one(delivery, inputs)
|
|
|
|
delivery, inputs = _passing("zh")
|
|
delivery["sections"] = [row for row in delivery["sections"] if row["id"] != "safety"]
|
|
assert "section_order" in _one(delivery, inputs)
|
|
|
|
delivery, inputs = _passing("zh")
|
|
delivery["sections"][2]["reasons"] = delivery["sections"][2]["reasons"][:1]
|
|
assert "reason_count" in _one(delivery, inputs)
|
|
|
|
|
|
def test_evidence_must_be_a_complete_field():
|
|
delivery, inputs = _passing("zh")
|
|
delivery["sections"][0]["reasons"][0]["evidence"] = "金"
|
|
assert "evidence_missing" in _one(delivery, inputs)
|
|
|
|
|
|
def test_status_only_field_is_not_evidence():
|
|
delivery, inputs = _passing("zh")
|
|
inputs["climate"]["status"] = "executed"
|
|
delivery["sections"][0]["reasons"][0]["evidence"] = "executed"
|
|
assert "evidence_missing" in _one(delivery, inputs)
|
|
|
|
|
|
def test_d_chart_beside_chinese_fails():
|
|
delivery, inputs = _passing("zh")
|
|
delivery["sections"][1]["text"] += "看D9"
|
|
assert "banned_term" in _one(delivery, inputs)
|
|
|
|
|
|
def test_timeline_phase_is_scanned():
|
|
delivery, inputs = _passing("zh")
|
|
delivery["sections"][7]["segments"][0]["phase"] = "大运"
|
|
assert "banned_term" in _one(delivery, inputs)
|
|
|
|
|
|
def test_english_kuja_fails():
|
|
delivery, inputs = _passing("en")
|
|
delivery["sections"][1]["text"] += " kuja "
|
|
assert "banned_term" in _one(delivery, inputs, "en")
|
|
|
|
|
|
def test_english_banned_word_fails():
|
|
delivery, inputs = _passing("en")
|
|
delivery["sections"][1]["text"] += " Venus "
|
|
assert "banned_term" in _one(delivery, inputs, "en")
|
|
delivery, inputs = _passing("en")
|
|
delivery["sections"][0]["text"] = "The core of this relationship is not ease. " + delivery["sections"][0]["text"]
|
|
assert "analytic_opening" in _one(delivery, inputs, "en")
|