feat(upstream): merge a6f47abd engine, MCP, and orchestrator
Independent Staging Quality Gate / validate (push) Has been cancelled
Independent Staging Quality Gate / publish (push) Has been cancelled

Three-way-merge calculation modules and pl9-export into the product fork while keeping commercial API routes, Raman ayanamsa, and the consultation contract as a keypath superset.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jesse_Chen
2026-09-03 20:28:57 +08:00
parent 45d132588f
commit f224146348
30 changed files with 17151 additions and 248 deletions
+58 -26
View File
@@ -70,6 +70,24 @@ def _estimate_sun_sign(birth_month: int, birth_day: int) -> int:
return 0 # fallback
def _muntha_from_natal_ascendant(natal_asc_sign_idx: int, completed_years: int) -> Dict:
"""Advance Muntha from the natal ascendant, one sign per completed year."""
muntha_sign_idx = (int(natal_asc_sign_idx) + int(completed_years)) % 12
return {
'status': 'used',
'muntha_sign_idx': muntha_sign_idx,
'muntha_sign': SIGNS[muntha_sign_idx],
'muntha_lord': SIGN_LORDS[SIGNS[muntha_sign_idx]],
'natal_asc_sign_idx': int(natal_asc_sign_idx),
'natal_asc_sign': SIGNS[int(natal_asc_sign_idx)],
'age': int(completed_years),
'formula': (
f'(natal ascendant {SIGNS[int(natal_asc_sign_idx)]} + '
f'completed years {int(completed_years)}) mod 12 = {SIGNS[muntha_sign_idx]}'
),
}
# =========================================================================
# 工具函数
# =========================================================================
@@ -115,7 +133,7 @@ def _datetime_to_jd_ut(dt: datetime) -> float:
def _get_sun_lon_jd(jd_ut: float, ayanamsa_name: str = 'lahiri') -> Optional[float]:
"""计算给定 JD (UT) 的太阳恒星黄经(Lahiri"""
"""计算给定 JD (UT) 的太阳恒星黄经(默认 Raman"""
if not HAS_SWE:
return None
try:
@@ -138,7 +156,7 @@ def find_solar_return_ut(
tz: float = 0.0,
max_iter: int = 30,
tol_deg: float = 0.0003, # ~1 arcsec
ayanamsa_name: str = 'lahiri',
ayanamsa_name: str = 'raman',
) -> Dict:
"""
计算太阳返照(Solar Return)精确 UT 时刻。
@@ -176,7 +194,7 @@ def _find_solar_return_swe(
target_year: int,
max_iter: int,
tol_deg: float,
ayanamsa_name: str = 'lahiri',
ayanamsa_name: str = 'raman',
) -> Dict:
"""使用 swisseph 精确计算太阳返照时刻(Newton 迭代法)"""
# 近似起始点:出生日期在目标年份的同一天
@@ -278,7 +296,7 @@ def calc_solar_return_chart(
birth_hour: int, birth_minute: int,
birth_lat: float, birth_lon: float, birth_tz: float,
target_year: int,
ayanamsa_name: str = 'lahiri',
ayanamsa_name: str = 'raman',
) -> Dict:
"""
计算太阳返照盘(Varshaphala)。
@@ -402,7 +420,7 @@ def solar_return_full_report(
birth_hour: int, birth_minute: int,
birth_lat: float, birth_lon: float, birth_tz: float,
target_year: int,
ayanamsa_name: str = 'lahiri',
ayanamsa_name: str = 'raman',
) -> Dict:
"""
太阳返照盘完整报告(Varshaphala 年运分析)。
@@ -456,27 +474,31 @@ def solar_return_full_report(
'age': sr['age'],
}
planet_lons = sr['planet_lons']
sr_planets = sr.get('chart', {}).get('planets', {})
tajika_planets = {
pn: {
'longitude': pd.get('degree_raw', pd.get('degree')),
'speed': pd.get('speed'),
}
for pn, pd in sr_planets.items()
if pn in ('Sun', 'Moon', 'Mars', 'Mercury', 'Jupiter', 'Venus', 'Saturn')
and isinstance(pd, dict)
and pd.get('degree_raw', pd.get('degree')) is not None
and pd.get('speed') is not None
}
asc_sign_idx = sr['asc_sign_idx']
# 2. Muntha(正确算法:从返照盘太阳星座开始数)
# 2. Muntha advances from the natal ascendant, not the solar sign.
try:
# Solar return sun sign = birth sun sign (by definition of solar return)
sr_sun_sign = int(planet_lons.get('Sun', 0) / 30) % 12
muntha_sign = (sr_sun_sign + age) % 12
muntha_note = ''
if degraded:
muntha_note = '(基于查表法估算太阳星座,实际Muntha可能偏差±1星座)'
result['muntha'] = {
'muntha_sign_idx': muntha_sign,
'muntha_sign': SIGNS[muntha_sign],
'muntha_lord': SIGN_LORDS[SIGNS[muntha_sign]],
'sr_sun_sign_idx': sr_sun_sign,
'sr_sun_sign': SIGNS[sr_sun_sign],
'age': age,
'formula': f'(返照盘太阳星座{sr_sun_sign} + 年龄{age}) mod 12 = {muntha_sign}',
'interpretation': _interpret_muntha_sign(muntha_sign),
'note': muntha_note,
}
natal_asc = sr.get('birth_asc_sign_idx') if not degraded else None
if natal_asc is None:
result['muntha'] = {
'status': 'blocked',
'reason': 'natal_ascendant_required_for_muntha',
}
else:
result['muntha'] = _muntha_from_natal_ascendant(natal_asc, age)
result['muntha']['interpretation'] = _interpret_muntha_sign(result['muntha']['muntha_sign_idx'])
except Exception as e:
result['muntha'] = {'error': str(e)}
@@ -500,7 +522,7 @@ def solar_return_full_report(
else:
try:
from tajika import calc_tajika_yogas
yogas = calc_tajika_yogas(planet_lons, chart_type='varsha')
yogas = calc_tajika_yogas(tajika_planets, chart_type='varsha')
result['tajika_yogas'] = yogas
except Exception as e:
result['tajika_yogas'] = {'error': str(e)}
@@ -512,8 +534,18 @@ def solar_return_full_report(
try:
from tajika import calc_all_sahams
asc_lon = sr['ascendant'].get('lon', sr['ascendant'].get('degree', 0))
sr_dt_ut = sr['solar_return']['dt_ut']
sahams = calc_all_sahams(planet_lons, asc_lon, sr_dt_ut, chart_type='varsha')
sr_dt_local = sr['solar_return'].get('dt_local')
if sr_dt_local is None:
raise ValueError('solar_return_local_datetime_required_for_saham_daynight')
sahams = calc_all_sahams(
planet_lons,
asc_lon,
sr_dt_local,
chart_type='varsha',
lat=birth_lat,
lon=birth_lon,
tz=birth_tz,
)
result['sahams'] = sahams
except Exception as e:
result['sahams'] = {'error': str(e)}