Files
Jyotisha/tests/test_consultation_birth_accuracy.py
T
Jesse_Chen 56daf6be49 fix(consult): pass birth-time accuracy instead of treating every chart as unrectified
Hospital records and adopted rectification times were still fed to the model as not_auto_rectified because the chart request omitted declared_accuracy/time_source and mastra hardcoded the boundary. Map profile truth into the engine request, keep rectified for accepted/confirmed active times only, and leave window/general guards unchanged.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-02 01:21:40 +08:00

129 lines
4.9 KiB
Python

#!/usr/bin/env python3
"""Consultation birth-time accuracy truth-passing invariants."""
from __future__ import annotations
import os
import re
import sys
SCRIPTS = os.path.join(os.path.dirname(__file__), '..', 'scripts')
if SCRIPTS not in sys.path:
sys.path.insert(0, SCRIPTS)
from birth_time_rectifier import get_effective_accuracy, get_enabled_vargas # noqa: E402
from jyotish_api_server import JyotishAPIHandler, _build_consumer_context # noqa: E402
CANDIDATE_WINDOW_COPY = re.compile(
r'candidate windows|not_auto_rectified|按时间范围|建议先做事件反验',
re.I,
)
PRECISION_ANNOTATION_COPY = re.compile(r'填报时间|标注精度|precision', re.I)
def _handler() -> JyotishAPIHandler:
handler = JyotishAPIHandler.__new__(JyotishAPIHandler)
handler._compute_vedastro_gateway_run = lambda body: { # type: ignore[method-assign]
'status': 'official_blocked',
'official_closure_reason': 'accuracy_contract_probe',
}
return handler
def _gate(declared_accuracy: str, time_source: str) -> dict:
return _handler()._compute_rectification_gate({
'planets': {
'Sun': {'lon': 80.0},
'Moon': {'lon': 123.0},
'Mars': {'lon': 210.0},
'Mercury': {'lon': 75.0},
'Jupiter': {'lon': 15.0},
'Venus': {'lon': 102.0},
'Saturn': {'lon': 330.0},
'Rahu': {'lon': 5.0},
'Ketu': {'lon': 185.0},
},
'ascendant': {'lon': 15.0},
'declared_accuracy': declared_accuracy,
'time_source': time_source,
})
def _consumer(rectification: dict) -> dict:
return _build_consumer_context(
question='我的事业接下来怎么走',
route_packet={'question_type': 'career', 'primary_theme': 'career'},
chart={'success': True},
rectification=rectification,
machine_evidence_packet={'sections': {'D1': {'status': 'used'}, 'D9': {'status': 'used'}, 'D10': {'status': 'used'}}},
vedastro_official={'status': 'blocked'},
)
def _joined_boundaries(context: dict) -> str:
return ' '.join(str(value) for value in context.get('domain_boundaries', {}).values())
def test_hospital_gate_is_minute_with_d9_d10_and_no_uncertainty_boundary() -> None:
result = _gate('minute', 'hospital')
context = _consumer(result)
assert result['effective_accuracy'] == 'minute'
assert result['lagna_boundary']['is_sensitive'] is False
assert result['enabled_vargas']['D9'] == 'enabled'
assert result['enabled_vargas']['D10'] == 'enabled'
assert 'birth_time_uncertainty_boundary' not in context['domain_boundaries']
assert CANDIDATE_WINDOW_COPY.search(_joined_boundaries(context)) is None
def test_family_exact_and_approximate_use_precision_annotation_not_candidate_windows() -> None:
family = _gate('minute', 'family_clear')
approximate = _gate('15min', 'family_vague')
assert family['effective_accuracy'] == '5min'
assert approximate['effective_accuracy'] == '15min'
assert '按填报时间排盘' in family['summary']['headline']
assert '按填报时间排盘' in approximate['summary']['headline']
assert CANDIDATE_WINDOW_COPY.search(family['summary']['headline']) is None
assert CANDIDATE_WINDOW_COPY.search(approximate['summary']['headline']) is None
family_context = _consumer(family)
approximate_context = _consumer(approximate)
for context in (family_context, approximate_context):
joined = _joined_boundaries(context)
assert CANDIDATE_WINDOW_COPY.search(joined) is None
assert PRECISION_ANNOTATION_COPY.search(joined)
assert 'not_auto_rectified' not in context.get('domain_context_layers', [])
def test_five_min_and_fifteen_min_vargas_are_not_unknown() -> None:
unknown = get_enabled_vargas('unknown')
five = get_enabled_vargas('5min')
fifteen = get_enabled_vargas('15min')
assert five != unknown
assert fifteen != unknown
assert five['D1'] == 'enabled'
assert five['D9'] == 'enabled'
assert five['D10'] == 'enabled'
assert five['D5'] == 'enabled_with_warning'
assert five['D4'] == 'enabled_with_warning'
assert five['D7'] == 'enabled_with_warning'
assert five['D30'] == 'disabled'
assert five['D60'] == 'disabled'
assert fifteen['D1'] == 'enabled'
assert fifteen['D9'] == 'enabled'
def test_rectified_and_hospital_effective_accuracy_stay_precise() -> None:
assert get_effective_accuracy('rectified', 'rectified') == 'rectified'
assert get_effective_accuracy('minute', 'hospital') == 'minute'
assert get_effective_accuracy('minute', 'family_clear') == '5min'
assert get_effective_accuracy('15min', 'family_vague') == '15min'
rectified = _gate('rectified', 'rectified')
context = _consumer(rectified)
assert rectified['effective_accuracy'] == 'rectified'
assert 'birth_time_uncertainty_boundary' not in context['domain_boundaries']
assert CANDIDATE_WINDOW_COPY.search(_joined_boundaries(context)) is None