Harden skill rigor constraints and sync reading quality tools
This commit is contained in:
@@ -26,6 +26,8 @@ FRONTS = {
|
||||
],
|
||||
"operator_card": "docs/benchmark/dasha_steve_jobs_first_packet_operator_card.md",
|
||||
"packet_template": "references/oracle/evidence_packet_templates/dasha_steve_jobs_lahiri_first_packet_only.json",
|
||||
"oracle_file": "references/oracle/dasha_shadbala_oracle_cases.json",
|
||||
"apply_script": "scripts/oracle_collection_queue.py",
|
||||
"external_sources": [
|
||||
"JHora Vimshottari Dasha screen",
|
||||
"PyJHora black-box dasha output",
|
||||
@@ -43,6 +45,8 @@ FRONTS = {
|
||||
],
|
||||
"operator_card": "docs/benchmark/tajika_einstein_1905_first_packet_operator_card.md",
|
||||
"packet_template": "references/oracle/artifacts/pending_packets/external_template_einstein_varshaphala_1905_lahiri.json",
|
||||
"oracle_file": "references/oracle/tajika_annual_oracle_cases.json",
|
||||
"apply_script": "scripts/tajika_annual_oracle_queue.py",
|
||||
"external_sources": [
|
||||
"JHora Varshaphala/Tajika annual chart screen",
|
||||
"PyJHora black-box annual output",
|
||||
@@ -60,6 +64,8 @@ FRONTS = {
|
||||
],
|
||||
"operator_card": "docs/benchmark/shadbala_redacted_place_raman_first_packet_operator_card.md",
|
||||
"packet_template": "references/oracle/evidence_packet_templates/shadbala_redacted_place_raman_first_packet.json",
|
||||
"oracle_file": "references/oracle/dasha_shadbala_oracle_cases.json",
|
||||
"apply_script": "scripts/oracle_collection_queue.py",
|
||||
"external_sources": [
|
||||
"JHora Shadbala component table screenshot",
|
||||
"PyJHora black-box shadbala output",
|
||||
@@ -69,6 +75,24 @@ FRONTS = {
|
||||
}
|
||||
|
||||
|
||||
def _fallback_apply_command(config: dict[str, Any]) -> str:
|
||||
return (
|
||||
f"python3 {config['apply_script']} "
|
||||
f"--oracle-file {config['oracle_file']} "
|
||||
f"--apply-packet {config['packet_template']} "
|
||||
"--format json"
|
||||
)
|
||||
|
||||
|
||||
def _fallback_validate_command(config: dict[str, Any]) -> str:
|
||||
queue_path = "/tmp/jyotish_oracle_queue_filled.json"
|
||||
return (
|
||||
f"python3 scripts/oracle_collection_queue.py --oracle-file {config['oracle_file']} "
|
||||
f"--format json > {queue_path} && "
|
||||
f"python3 scripts/oracle_evidence_validator.py --queue-file {queue_path}"
|
||||
)
|
||||
|
||||
|
||||
def _group_missing_fields(missing_fields: list[str]) -> dict[str, Any]:
|
||||
metadata_fields = [field for field in missing_fields if field.startswith("metadata.")]
|
||||
target_fields = [field for field in missing_fields if field.startswith("target.")]
|
||||
@@ -177,8 +201,8 @@ def build_report(front: str) -> dict[str, Any]:
|
||||
first = status.get("first_priority") or {
|
||||
"case_id": packet.get("case_id"),
|
||||
"capture_id": packet.get("capture_id"),
|
||||
"apply_command": "",
|
||||
"validate_command": "",
|
||||
"apply_command": _fallback_apply_command(config),
|
||||
"validate_command": _fallback_validate_command(config),
|
||||
"packet_path": config["packet_template"],
|
||||
}
|
||||
packet_missing = _packet_missing(config["packet_template"])
|
||||
|
||||
@@ -189,9 +189,21 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
path = urlparse(self.path).path
|
||||
try:
|
||||
if path == '/api/health':
|
||||
swisseph_available = False
|
||||
swisseph_version = None
|
||||
try:
|
||||
import swisseph as swe
|
||||
swisseph_available = True
|
||||
swisseph_version = getattr(swe, 'version', None) or getattr(swe, '__version__', None)
|
||||
except Exception:
|
||||
swisseph_available = False
|
||||
swisseph_version = None
|
||||
self._json({
|
||||
'status': 'ok',
|
||||
'version': '6.9.14',
|
||||
'swisseph_available': swisseph_available,
|
||||
'swisseph_version': swisseph_version,
|
||||
'ayanamsa_default': 'lahiri',
|
||||
'modules': 'Chart/KP/Synastry/Prashna/Remedies/Dasha/Varga/Jaimini/Ashtakavarga/Shadbala/Yoga/Aspects/Tajika/Muhurta/BhavaChalit/BhavaBala/Sudarshana/Nakshatra/Transit/RectificationGate/CaseValidation/DivisionalYoga/Kakshya',
|
||||
})
|
||||
elif path == '/api/cities':
|
||||
@@ -359,6 +371,19 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
self._check_range(key, number, min_value, max_value)
|
||||
return number
|
||||
|
||||
|
||||
def _parse_timezone(self, body, lat, lon, year, month, day, hour, minute, second):
|
||||
tz = body.get('tz')
|
||||
if tz is not None and tz != "":
|
||||
return self._get_float(body, 'tz', 8, -14, 14)
|
||||
from timezone_utils import infer_timezone
|
||||
from datetime import datetime
|
||||
try:
|
||||
dt = datetime(int(year), int(month), int(day), int(hour), int(minute), int(second))
|
||||
except Exception:
|
||||
dt = datetime.utcnow()
|
||||
return infer_timezone(lat, lon, dt)
|
||||
|
||||
def _get_float(self, body, key, default, min_value=None, max_value=None):
|
||||
value = body.get(key, default)
|
||||
try:
|
||||
@@ -1355,7 +1380,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
second = self._get_birth_second(body)
|
||||
lat = self._get_float(body, 'lat', 39.9, -90, 90)
|
||||
lon = self._get_float(body, 'lon', 116.4, -180, 180)
|
||||
tz = self._get_float(body, 'tz', 8, -14, 14)
|
||||
tz = self._parse_timezone(body, lat, lon, year, month, day, hour, minute, second)
|
||||
try:
|
||||
datetime(year, month, day, int(hour), int(minute), int(second))
|
||||
except ValueError as e:
|
||||
@@ -1555,7 +1580,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
second = self._get_birth_second(body)
|
||||
lat = self._get_float(body, 'lat', 39.9, -90, 90)
|
||||
lon = self._get_float(body, 'lon', 116.4, -180, 180)
|
||||
tz = self._get_float(body, 'tz', 8, -14, 14)
|
||||
tz = self._parse_timezone(body, lat, lon, year, month, day, hour, minute, second)
|
||||
try:
|
||||
datetime(year, month, day, int(hour), int(minute), int(second))
|
||||
except ValueError as e:
|
||||
@@ -1800,6 +1825,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
planets = chart.get('planets') or {}
|
||||
dasha = chart.get('dasha') or {}
|
||||
shadbala = chart.get('shadbala') or {}
|
||||
functional_layer = self._functional_benefic_malefic_snapshot(planets, ascendant)
|
||||
top_strength = sorted(
|
||||
[
|
||||
{
|
||||
@@ -1868,6 +1894,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
'strength': {
|
||||
'shadbala_ranking': top_strength,
|
||||
},
|
||||
'functional_benefic_malefic': functional_layer,
|
||||
'quality_boundary': {
|
||||
'external_oracle_status': 'D1/D9/VedAstro longitude boundary covered; Dasha/Shadbala external absolute calibration still requires multi-source oracle expansion.',
|
||||
},
|
||||
@@ -1892,6 +1919,34 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
},
|
||||
}
|
||||
|
||||
def _functional_benefic_malefic_snapshot(self, planets, ascendant):
|
||||
try:
|
||||
from yoga_engine import YogaContext
|
||||
asc_sign = ascendant.get('sign')
|
||||
if not asc_sign or not isinstance(planets, dict):
|
||||
raise ValueError('missing ascendant sign or planets')
|
||||
context = YogaContext(planets, asc_sign)
|
||||
benefics = context.functional_benefics()
|
||||
malefics = context.functional_malefics()
|
||||
return {
|
||||
'status': 'used',
|
||||
'ascendant': asc_sign,
|
||||
'functional_benefics': benefics,
|
||||
'functional_malefics': malefics,
|
||||
'effect_on_confidence': (
|
||||
'高严谨模式下必须叠加功能性吉凶星;若与自然吉凶属性冲突,'
|
||||
'应降低置信度并在 Technique Audit Table 中显式说明。'
|
||||
),
|
||||
}
|
||||
except Exception as exc:
|
||||
return {
|
||||
'status': 'blocked',
|
||||
'ascendant': ascendant.get('sign'),
|
||||
'functional_benefics': [],
|
||||
'functional_malefics': [],
|
||||
'effect_on_confidence': f'未完成功能性吉凶星判定,需降低高严谨结论置信度: {exc}',
|
||||
}
|
||||
|
||||
def _detect_yogas(self, planets, asc_idx):
|
||||
yogas = []
|
||||
KENDRA = {1,4,7,10}
|
||||
@@ -2870,7 +2925,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
day = self._get_int(body, 'day', 1, 1, 31)
|
||||
lat = self._get_float(body, 'lat', body.get('birth_lat', 0), -90, 90)
|
||||
lon = self._get_float(body, 'lon', body.get('birth_lon', 0), -180, 180)
|
||||
tz = self._get_float(body, 'tz', body.get('birth_tz', 0), -14, 14)
|
||||
tz = self._parse_timezone(body, lat, lon, year, month, day, hour, minute, second)
|
||||
hour_decimal = self._birth_hour_decimal(birth_hour, birth_minute, birth_second)
|
||||
solar_lon = planets.get('Sun', {}).get('lon', 0)
|
||||
base_planets = base_result.get('planets', {}) if isinstance(base_result, dict) else {}
|
||||
@@ -2925,7 +2980,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
birth_dt = self._parse_birth_datetime(body)
|
||||
lat = self._get_float(body, 'lat', body.get('birth_lat', 0), -90, 90)
|
||||
lon = self._get_float(body, 'lon', body.get('birth_lon', 0), -180, 180)
|
||||
tz = self._get_float(body, 'tz', body.get('birth_tz', 0), -14, 14)
|
||||
tz = self._parse_timezone(body, lat, lon, year, month, day, hour, minute, second)
|
||||
solar_return = _load_local_module('solar_return')
|
||||
report = solar_return.solar_return_full_report(
|
||||
birth_dt.year,
|
||||
@@ -3539,7 +3594,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
second = self._get_birth_second(body)
|
||||
lat = self._get_float(body, 'lat', body.get('birth_lat', 0), -90, 90)
|
||||
lon = self._get_float(body, 'lon', body.get('birth_lon', 0), -180, 180)
|
||||
tz = self._get_float(body, 'tz', body.get('birth_tz', 0), -14, 14)
|
||||
tz = self._parse_timezone(body, lat, lon, year, month, day, hour, minute, second)
|
||||
datetime(year, month, day, int(hour), int(minute), int(second))
|
||||
import swisseph as swe
|
||||
hour_ut = self._birth_hour_decimal(hour, minute, second) - tz
|
||||
|
||||
+166
-16
@@ -701,17 +701,152 @@ def _planet_snapshot(planets, planet_name):
|
||||
|
||||
|
||||
def _oracle_progress_snapshot():
|
||||
return {
|
||||
'scope': 'external_oracle_evidence_validation',
|
||||
'collection_queue': 'external_oracle_collection_queue',
|
||||
'total_packets': 5,
|
||||
'valid_packets': 0,
|
||||
'ready_for_calibration': 0,
|
||||
'production_tuning_allowed': False,
|
||||
'artifact_policy': 'references/oracle/artifacts/',
|
||||
'promotion_rule': 'external_verified requires source_artifact, filled target values, and non-local-engine external evidence.',
|
||||
'boundary': 'Dasha/Shadbala absolute values are not externally calibrated until enough packets pass validation.',
|
||||
}
|
||||
try:
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
root = Path(__file__).resolve().parents[1]
|
||||
oracle_file = root / 'references' / 'oracle' / 'dasha_shadbala_oracle_cases.json'
|
||||
with tempfile.NamedTemporaryFile('w+', suffix='.json', delete=True, encoding='utf-8') as handle:
|
||||
queue = subprocess.run(
|
||||
[sys.executable, 'scripts/oracle_collection_queue.py', '--oracle-file', str(oracle_file), '--format', 'json'],
|
||||
cwd=root,
|
||||
text=True,
|
||||
capture_output=True,
|
||||
timeout=90,
|
||||
check=False,
|
||||
)
|
||||
if queue.returncode != 0:
|
||||
raise RuntimeError(queue.stderr.strip() or queue.stdout.strip())
|
||||
handle.write(queue.stdout)
|
||||
handle.flush()
|
||||
validation = subprocess.run(
|
||||
[sys.executable, 'scripts/oracle_evidence_validator.py', '--queue-file', handle.name],
|
||||
cwd=root,
|
||||
text=True,
|
||||
capture_output=True,
|
||||
timeout=90,
|
||||
check=False,
|
||||
)
|
||||
if validation.returncode != 0:
|
||||
raise RuntimeError(validation.stderr.strip() or validation.stdout.strip())
|
||||
report = json.loads(validation.stdout)
|
||||
summary = report.get('summary', {})
|
||||
return {
|
||||
'scope': 'external_oracle_evidence_validation',
|
||||
'collection_queue': 'external_oracle_collection_queue',
|
||||
'total_packets': summary.get('total_packets', 0),
|
||||
'valid_packets': summary.get('valid_packets', 0),
|
||||
'ready_for_calibration': summary.get('ready_for_calibration', 0),
|
||||
'production_tuning_allowed': summary.get('production_tuning_allowed', False),
|
||||
'artifact_policy': 'references/oracle/artifacts/',
|
||||
'promotion_rule': 'external_verified requires source_artifact, filled target values, and non-local-engine external evidence.',
|
||||
'boundary': report.get('boundary', 'Dasha/Shadbala absolute values are not externally calibrated until enough packets pass validation.'),
|
||||
}
|
||||
except Exception as exc:
|
||||
return {
|
||||
'scope': 'external_oracle_evidence_validation',
|
||||
'collection_queue': 'external_oracle_collection_queue',
|
||||
'total_packets': 0,
|
||||
'valid_packets': 0,
|
||||
'ready_for_calibration': 0,
|
||||
'production_tuning_allowed': False,
|
||||
'artifact_policy': 'references/oracle/artifacts/',
|
||||
'promotion_rule': 'external_verified requires source_artifact, filled target values, and non-local-engine external evidence.',
|
||||
'boundary': f'Oracle progress unavailable: {exc}',
|
||||
}
|
||||
|
||||
|
||||
def _functional_benefic_malefic_snapshot(planets, ascendant):
|
||||
try:
|
||||
from yoga_engine import YogaContext
|
||||
asc_sign = ascendant.get('sign') if isinstance(ascendant, dict) else None
|
||||
if not asc_sign or not isinstance(planets, dict):
|
||||
raise ValueError('missing ascendant sign or planets')
|
||||
context = YogaContext(planets, asc_sign)
|
||||
benefics = context.functional_benefics()
|
||||
malefics = context.functional_malefics()
|
||||
return {
|
||||
'status': 'used',
|
||||
'ascendant': asc_sign,
|
||||
'functional_benefics': benefics,
|
||||
'functional_malefics': malefics,
|
||||
'effect_on_confidence': (
|
||||
'高严谨模式下必须叠加功能性吉凶星;若与自然吉凶属性冲突,'
|
||||
'应降低置信度并在 Technique Audit Table 中显式说明。'
|
||||
),
|
||||
}
|
||||
except Exception as exc:
|
||||
return {
|
||||
'status': 'blocked',
|
||||
'ascendant': ascendant.get('sign') if isinstance(ascendant, dict) else None,
|
||||
'functional_benefics': [],
|
||||
'functional_malefics': [],
|
||||
'effect_on_confidence': f'未完成功能性吉凶星判定,需降低高严谨结论置信度: {exc}',
|
||||
}
|
||||
|
||||
|
||||
def _build_technique_audit_table(functional_layer, oracle_progress, modules):
|
||||
dasha = modules.get('dasha') if isinstance(modules, dict) else {}
|
||||
narayana = modules.get('narayana_dasha') if isinstance(modules, dict) else {}
|
||||
d9 = modules.get('d9_navamsa_expanded') if isinstance(modules, dict) else {}
|
||||
shadbala = modules.get('shadbala') if isinstance(modules, dict) else {}
|
||||
ashtakavarga = modules.get('ashtakavarga') if isinstance(modules, dict) else {}
|
||||
vimsopaka = modules.get('vimsopaka') if isinstance(modules, dict) else {}
|
||||
dasa_convergence = modules.get('dasa_convergence') if isinstance(modules, dict) else {}
|
||||
|
||||
rows = [
|
||||
{
|
||||
'technique': 'Functional Benefic/Malefic',
|
||||
'status': functional_layer.get('status', 'blocked'),
|
||||
'source': 'scripts/yoga_engine.py::YogaContext',
|
||||
'note': functional_layer.get('effect_on_confidence', '高严谨模式缺少功能性吉凶星判定。'),
|
||||
},
|
||||
{
|
||||
'technique': 'External Oracle Progress',
|
||||
'status': 'used' if oracle_progress.get('total_packets', 0) else 'blocked',
|
||||
'source': 'scripts/oracle_collection_queue.py + scripts/oracle_evidence_validator.py',
|
||||
'note': (
|
||||
f"当前 external oracle 进度 {oracle_progress.get('ready_for_calibration', 0)}/"
|
||||
f"{oracle_progress.get('total_packets', 0)} ready,"
|
||||
f"production_tuning_allowed={oracle_progress.get('production_tuning_allowed', False)}。"
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
rows.append({
|
||||
'technique': 'Vimshottari + Narayana Cross-check',
|
||||
'status': 'used' if isinstance(dasha, dict) and isinstance(narayana, dict) and dasha and narayana else 'blocked',
|
||||
'source': 'modules.dasha + modules.narayana_dasha + modules.dasa_convergence',
|
||||
'note': (
|
||||
f"Vimshottari 当前主运={((dasha.get('current_dasha') or {}).get('lord')) if isinstance(dasha, dict) else None}; "
|
||||
f"Narayana 当前主运={narayana.get('current_dasha') if isinstance(narayana, dict) else None}; "
|
||||
f"多系统收敛摘要={dasa_convergence.get('top_convergent_domains') if isinstance(dasa_convergence, dict) else None}。"
|
||||
),
|
||||
})
|
||||
rows.append({
|
||||
'technique': 'Relevant Vargas',
|
||||
'status': 'used' if isinstance(d9, dict) and d9 else 'blocked',
|
||||
'source': 'modules.d9_navamsa_expanded',
|
||||
'note': (
|
||||
f"D9 已接入;可见键={list(d9.keys())[:5] if isinstance(d9, dict) else []}。"
|
||||
"高严谨解读至少应交叉 D1 + D9,婚姻/关系主题不得跳过 D9。"
|
||||
),
|
||||
})
|
||||
rows.append({
|
||||
'technique': 'Strength Layers',
|
||||
'status': 'used' if isinstance(shadbala, dict) and isinstance(ashtakavarga, dict) and isinstance(vimsopaka, dict) else 'blocked',
|
||||
'source': 'modules.shadbala + modules.ashtakavarga + modules.vimsopaka',
|
||||
'note': (
|
||||
f"Shadbala status={shadbala.get('status') if isinstance(shadbala, dict) else None}; "
|
||||
f"Ashtakavarga SAV={((ashtakavarga.get('sav') or {}).get('total')) if isinstance(ashtakavarga, dict) else None}; "
|
||||
f"Vimsopaka status={vimsopaka.get('status') if isinstance(vimsopaka, dict) else None}。"
|
||||
),
|
||||
})
|
||||
return rows
|
||||
|
||||
|
||||
def _build_ai_prompt_pack(report):
|
||||
@@ -722,11 +857,16 @@ def _build_ai_prompt_pack(report):
|
||||
birth_info = chart.get('birth_info', {}) if isinstance(chart, dict) else {}
|
||||
dasha = modules.get('dasha') or {}
|
||||
current_dasha = dasha.get('current_dasha') if isinstance(dasha, dict) else {}
|
||||
narayana = modules.get('narayana_dasha') or {}
|
||||
dasa_convergence = modules.get('dasa_convergence') or {}
|
||||
shadbala = modules.get('shadbala') or {}
|
||||
shadbala_planets = shadbala.get('planets', {}) if isinstance(shadbala, dict) else {}
|
||||
ashtakavarga = modules.get('ashtakavarga') or {}
|
||||
sav = ashtakavarga.get('sav') if isinstance(ashtakavarga, dict) else {}
|
||||
d9 = modules.get('d9_navamsa_expanded') or {}
|
||||
functional_layer = _functional_benefic_malefic_snapshot(planets, chart.get('ascendant', {}))
|
||||
oracle_progress = _oracle_progress_snapshot()
|
||||
technique_audit_table = _build_technique_audit_table(functional_layer, oracle_progress, modules)
|
||||
|
||||
shadbala_ranking = []
|
||||
for planet_name, pdata in sorted(
|
||||
@@ -763,10 +903,18 @@ def _build_ai_prompt_pack(report):
|
||||
'Ketu': _planet_snapshot(planets, 'Ketu'),
|
||||
},
|
||||
'timing': {
|
||||
'current_mahadasha': current_dasha.get('lord') if isinstance(current_dasha, dict) else None,
|
||||
'current_antardasha': current_ad.get('lord') if isinstance(current_ad, dict) else None,
|
||||
'current_md_start': current_dasha.get('start') if isinstance(current_dasha, dict) else None,
|
||||
'current_md_end': current_dasha.get('end') if isinstance(current_dasha, dict) else None,
|
||||
'vimshottari': {
|
||||
'mahadasha': current_dasha.get('lord') if isinstance(current_dasha, dict) else None,
|
||||
'antardasha': current_ad.get('lord') if isinstance(current_ad, dict) else None,
|
||||
'start': current_dasha.get('start') if isinstance(current_dasha, dict) else None,
|
||||
'end': current_dasha.get('end') if isinstance(current_dasha, dict) else None,
|
||||
},
|
||||
'narayana': {
|
||||
'current_dasha': narayana.get('current_dasha') if isinstance(narayana, dict) else None,
|
||||
'current_year': narayana.get('current_year') if isinstance(narayana, dict) else None,
|
||||
'current_age': narayana.get('current_age') if isinstance(narayana, dict) else None,
|
||||
},
|
||||
'convergence_top_domains': dasa_convergence.get('top_convergent_domains', []) if isinstance(dasa_convergence, dict) else [],
|
||||
},
|
||||
'strength': {
|
||||
'shadbala_ranking': shadbala_ranking[:7],
|
||||
@@ -787,7 +935,9 @@ def _build_ai_prompt_pack(report):
|
||||
'warnings': report.get('warnings', []),
|
||||
'external_oracle_status': 'D1/D9/VedAstro longitude boundary covered; Dasha/Shadbala external absolute calibration still requires multi-source oracle expansion.',
|
||||
},
|
||||
'oracle_progress': _oracle_progress_snapshot(),
|
||||
'oracle_progress': oracle_progress,
|
||||
'functional_benefic_malefic': functional_layer,
|
||||
'technique_audit_table': technique_audit_table,
|
||||
}
|
||||
|
||||
prompt_lines = [
|
||||
|
||||
@@ -64,11 +64,12 @@ def load_real_case_revalidation() -> dict[str, Any]:
|
||||
def load_yoga_logic_benchmark() -> dict[str, Any]:
|
||||
report = json.loads((ROOT / "references/validation_logic_report.json").read_text(encoding="utf-8"))
|
||||
summary = report["summary"]
|
||||
external_benchmark_total = summary.get("external_benchmark_total", summary.get("pyjhora_total", 0))
|
||||
return {
|
||||
"charts_tested": summary["charts_tested"],
|
||||
"comparable_rules": summary["comparable_rules"],
|
||||
"skill_total": summary["skill_total"],
|
||||
"pyjhora_total": summary["pyjhora_total"],
|
||||
"external_benchmark_total": external_benchmark_total,
|
||||
"agreements": summary["agreements"],
|
||||
"false_positives": summary["false_positives"],
|
||||
"false_negatives": summary["false_negatives"],
|
||||
@@ -162,7 +163,11 @@ def build_skill_matrix(checks: dict[str, Any]) -> list[dict[str, str]]:
|
||||
{
|
||||
"area": "Dasha and timing",
|
||||
"local_status": "usable with boundary warning",
|
||||
"accuracy_signal": "Local tests pass, but oracle evidence packets are 0 ready.",
|
||||
"accuracy_signal": (
|
||||
f"Local tests pass; external oracle packets ready "
|
||||
f"{checks['dasha_shadbala_oracle_evidence']['ready_for_calibration']}/"
|
||||
f"{checks['dasha_shadbala_oracle_evidence']['total_packets']}."
|
||||
),
|
||||
"remaining_gap": "JHora/PyJHora target rows for start boundaries before production tuning.",
|
||||
},
|
||||
{
|
||||
|
||||
+29
-12
@@ -22,6 +22,7 @@ v6.9.12 修复:
|
||||
import math
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
from typing import Dict, Tuple
|
||||
|
||||
# v6.1.10: 导入实际Varga计算器(支持脚本和包两种调用方式)
|
||||
@@ -42,24 +43,40 @@ SIGN_LORDS = {
|
||||
'Sagittarius': 'Jupiter', 'Capricorn': 'Saturn', 'Aquarius': 'Saturn', 'Pisces': 'Jupiter'
|
||||
}
|
||||
|
||||
SHADBALA_CONSTANTS_PATH = os.path.join(_script_dir, '..', 'references', 'shat_bala_constants.json')
|
||||
|
||||
|
||||
def _to_longitude(entry: Dict[str, float]) -> float:
|
||||
return float(entry['sign']) * 30 + float(entry['degree'])
|
||||
|
||||
|
||||
def _load_shadbala_constants() -> Dict:
|
||||
with open(SHADBALA_CONSTANTS_PATH, 'r', encoding='utf-8') as handle:
|
||||
return json.load(handle)
|
||||
|
||||
|
||||
_SHADBALA_CONSTANTS = _load_shadbala_constants()
|
||||
|
||||
# 入庙度数(sign_idx * 30 + degree)
|
||||
EXALTATION_DEG = {
|
||||
'Sun': 10.0, 'Moon': 33.0, 'Mars': 298.0, 'Mercury': 165.0,
|
||||
'Jupiter': 95.0, 'Venus': 357.0, 'Saturn': 200.0
|
||||
planet: _to_longitude(entry)
|
||||
for planet, entry in _SHADBALA_CONSTANTS['exaltation_degrees'].items()
|
||||
}
|
||||
|
||||
# 落陷度数(入庙 + 180°)
|
||||
DEBILITATION_DEG = {p: (d + 180) % 360 for p, d in EXALTATION_DEG.items()}
|
||||
# 落陷度数(由参考 JSON 直接提供,避免隐式推导与外部真值漂移)
|
||||
DEBILITATION_DEG = {
|
||||
planet: _to_longitude(entry)
|
||||
for planet, entry in _SHADBALA_CONSTANTS['debilitation_degrees'].items()
|
||||
}
|
||||
|
||||
# 行星友好/敌对关系
|
||||
# 行星友好/敌对关系(静态真值表)
|
||||
FRIENDSHIP = {
|
||||
'Sun': {'friend': ['Moon', 'Mars', 'Jupiter'], 'enemy': ['Saturn', 'Venus'], 'neutral': ['Mercury']},
|
||||
'Moon': {'friend': ['Sun', 'Mercury'], 'enemy': [], 'neutral': ['Mars', 'Jupiter', 'Venus', 'Saturn']},
|
||||
'Mars': {'friend': ['Sun', 'Moon', 'Jupiter'], 'enemy': ['Mercury'], 'neutral': ['Venus', 'Saturn']},
|
||||
'Mercury': {'friend': ['Sun', 'Venus'], 'enemy': ['Moon'], 'neutral': ['Mars', 'Jupiter', 'Saturn']},
|
||||
'Jupiter': {'friend': ['Sun', 'Moon', 'Mars'], 'enemy': ['Mercury', 'Venus'], 'neutral': ['Saturn']},
|
||||
'Venus': {'friend': ['Mercury', 'Saturn'], 'enemy': ['Sun', 'Moon'], 'neutral': ['Mars', 'Jupiter']},
|
||||
'Saturn': {'friend': ['Mercury', 'Venus'], 'enemy': ['Sun', 'Moon', 'Mars'], 'neutral': ['Jupiter']},
|
||||
planet: {
|
||||
'friend': relation['friends'],
|
||||
'enemy': relation['enemies'],
|
||||
'neutral': relation['neutrals'],
|
||||
}
|
||||
for planet, relation in _SHADBALA_CONSTANTS['natural_relationships'].items()
|
||||
}
|
||||
|
||||
# Dig Bala 最强宫位
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="/Users/wuyongnaren/Documents/印度占星"
|
||||
|
||||
FILES=(
|
||||
"AGENTS.md"
|
||||
"SKILL.md"
|
||||
"references/technique_registry.json"
|
||||
"references/quick-reference-guide.md"
|
||||
"references/strict-workflow-router.md"
|
||||
"skills/jyotish-engine-modules/SKILL.md"
|
||||
"skills/jyotish-full-reading-integration/SKILL.md"
|
||||
)
|
||||
|
||||
echo "About to stage only these skill-truth files:"
|
||||
for file in "${FILES[@]}"; do
|
||||
echo " - $file"
|
||||
done
|
||||
|
||||
echo
|
||||
echo "Diff vs origin/main:"
|
||||
git -C "$ROOT" diff --stat origin/main -- "${FILES[@]}"
|
||||
|
||||
echo
|
||||
git -C "$ROOT" add -- "${FILES[@]}"
|
||||
echo "Staged whitelist files only."
|
||||
|
||||
echo
|
||||
echo "Current staged diff summary:"
|
||||
git -C "$ROOT" diff --cached --stat -- "${FILES[@]}"
|
||||
@@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="/Users/wuyongnaren/Documents/印度占星"
|
||||
|
||||
FILES=(
|
||||
"AGENTS.md"
|
||||
"SKILL.md"
|
||||
"references/technique_registry.json"
|
||||
"references/quick-reference-guide.md"
|
||||
"references/strict-workflow-router.md"
|
||||
"skills/jyotish-engine-modules/SKILL.md"
|
||||
"skills/jyotish-full-reading-integration/SKILL.md"
|
||||
)
|
||||
|
||||
DRY_RUN="${1:-}"
|
||||
|
||||
echo "Skill truth whitelist sync set:"
|
||||
for file in "${FILES[@]}"; do
|
||||
echo " - $file"
|
||||
done
|
||||
|
||||
echo
|
||||
echo "Diff vs origin/main:"
|
||||
git -C "$ROOT" diff --stat origin/main -- "${FILES[@]}"
|
||||
|
||||
if [[ "$DRY_RUN" == "--dry-run" ]]; then
|
||||
echo
|
||||
echo "Dry run only. No files copied or pushed."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "This script is audit-only by default."
|
||||
echo "Use it to inspect the exact whitelist before any manual git add/commit/push."
|
||||
+50
-3
@@ -15,6 +15,8 @@ Tajika/Varshaphala年运盘模块 v1.0
|
||||
from typing import Dict, List, Optional
|
||||
from datetime import datetime
|
||||
import math
|
||||
import json
|
||||
import os
|
||||
|
||||
SIGNS = ['Aries','Taurus','Gemini','Cancer','Leo','Virgo',
|
||||
'Libra','Scorpio','Sagittarius','Capricorn','Aquarius','Pisces']
|
||||
@@ -29,6 +31,41 @@ DEBIL_SIGN = {'Sun': 6, 'Moon': 7, 'Mars': 3, 'Mercury': 11, 'Jupiter': 9, 'Venu
|
||||
OWN_SIGNS = {'Sun': [4], 'Moon': [3], 'Mars': [0, 7], 'Mercury': [2, 5],
|
||||
'Jupiter': [8, 11], 'Venus': [1, 6], 'Saturn': [9, 10]}
|
||||
|
||||
SAHAM_RULES_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'references', 'saham_rules.json')
|
||||
|
||||
|
||||
def _load_saham_rules() -> Dict:
|
||||
with open(SAHAM_RULES_PATH, 'r', encoding='utf-8') as handle:
|
||||
return json.load(handle)
|
||||
|
||||
|
||||
_SAHAM_RULES = _load_saham_rules()
|
||||
|
||||
|
||||
def _resolve_saham_operand(operand: str, planet_lons: Dict[str, float], asc_lon: float, computed: Dict[str, float]) -> float:
|
||||
if operand == 'Ascendant':
|
||||
return float(asc_lon) % 360
|
||||
if operand in computed:
|
||||
return float(computed[operand]) % 360
|
||||
if operand in planet_lons:
|
||||
return float(planet_lons[operand]) % 360
|
||||
raise KeyError(f"Unsupported saham operand: {operand}")
|
||||
|
||||
|
||||
def _calc_formula_saham(
|
||||
rule_name: str,
|
||||
planet_lons: Dict[str, float],
|
||||
asc_lon: float,
|
||||
is_day: bool,
|
||||
computed: Dict[str, float],
|
||||
) -> float:
|
||||
rule = _SAHAM_RULES['sahams'][rule_name]
|
||||
formula = rule['formula_day'] if is_day else rule['formula_night']
|
||||
first = _resolve_saham_operand(formula[0], planet_lons, asc_lon, computed)
|
||||
second = _resolve_saham_operand(formula[1], planet_lons, asc_lon, computed)
|
||||
third = _resolve_saham_operand(formula[2], planet_lons, asc_lon, computed)
|
||||
return (third + (first - second)) % 360
|
||||
|
||||
|
||||
def calc_muntha(birth_asc_idx: int, age: int) -> Dict:
|
||||
"""
|
||||
@@ -833,9 +870,11 @@ def calc_all_sahams(planet_lons: Dict[str, float],
|
||||
is_day = _is_daytime(birth_dt, sun_lon, asc_lon)
|
||||
|
||||
results = {}
|
||||
computed_formula_sahams: Dict[str, float] = {}
|
||||
|
||||
# 1. Punya Saham(福德点):Moon - Sun + Asc
|
||||
p_lon = _saham(sun_lon, moon_lon)
|
||||
p_lon = _calc_formula_saham('Punya_Saham', planet_lons, asc_lon, is_day, computed_formula_sahams)
|
||||
computed_formula_sahams['Punya_Saham'] = p_lon
|
||||
results['punya_saham'] = _saham_dict(p_lon, 'Punya Saham', '福德点——善业、精神成长、父系福德')
|
||||
|
||||
# 2. Karya Saham(事业点):
|
||||
@@ -883,11 +922,19 @@ def calc_all_sahams(planet_lons: Dict[str, float],
|
||||
results['raja_saham'] = _saham_dict(raja_lon, 'Raja Saham', '王权点——领导力、权威')
|
||||
|
||||
# 11. Yasha Saham(名声点):Jupiter - Sun + Asc
|
||||
yasha_lon = _saham(sun_lon, jupiter_lon)
|
||||
try:
|
||||
yasha_lon = _calc_formula_saham('Yashas_Saham', planet_lons, asc_lon, is_day, computed_formula_sahams)
|
||||
computed_formula_sahams['Yashas_Saham'] = yasha_lon
|
||||
except KeyError:
|
||||
yasha_lon = _saham(sun_lon, jupiter_lon)
|
||||
results['yasha_saham'] = _saham_dict(yasha_lon, 'Yasha Saham', '名声点——声誉、社会地位')
|
||||
|
||||
# 12. Karma Saham(业力点):Saturn - Mars + Asc
|
||||
karma_lon = _saham(mars_lon, saturn_lon)
|
||||
try:
|
||||
karma_lon = _calc_formula_saham('Karma_Saham', planet_lons, asc_lon, is_day, computed_formula_sahams)
|
||||
computed_formula_sahams['Karma_Saham'] = karma_lon
|
||||
except KeyError:
|
||||
karma_lon = _saham(mars_lon, saturn_lon)
|
||||
results['karma_saham'] = _saham_dict(karma_lon, 'Karma Saham', '业力点——前世因果、责任')
|
||||
|
||||
# 13. Bandhu Saham(兄弟点):Mars - Mercury + Asc
|
||||
|
||||
Reference in New Issue
Block a user