kundali-chart-mcp 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -0
- package/azure-function/function_app.py +93 -0
- package/azure-function/host.json +15 -0
- package/azure-function/kundali_bridge.py +952 -0
- package/azure-function/python/kundali_lib/__init__.py +1 -0
- package/azure-function/python/kundali_lib/__pycache__/__init__.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/__pycache__/ephemeris.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/__pycache__/geocoder.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/__pycache__/vedicastro_bridge.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/ephemeris.py +30 -0
- package/azure-function/python/kundali_lib/geocoder.py +82 -0
- package/azure-function/python/kundali_lib/vedic/__init__.py +1 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/__init__.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/arishta.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/ashtakavarga.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/avasthas.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/ayanamsa.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/bhava_chalit.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/char_dasha.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/chart.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/chart_types.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/compatibility.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/constants.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/dasha_extended.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/dasha_systems.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/doshas.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/gandanta.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/gochara.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/hora.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/houses.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/jaimini.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/kalachakra.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/kartari.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/kurmachakra.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/lunar_return.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/muhurta.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/nabhasha.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/nakshatra_details.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/panchanga.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/planets.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/shadbala.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/special_conditions.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/sudarshana.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/tajaka.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/upagrahas.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/varshaphal.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/yogas.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/zodiac.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/arishta.py +465 -0
- package/azure-function/python/kundali_lib/vedic/ashtakavarga.py +213 -0
- package/azure-function/python/kundali_lib/vedic/avasthas.py +292 -0
- package/azure-function/python/kundali_lib/vedic/ayanamsa.py +106 -0
- package/azure-function/python/kundali_lib/vedic/bhava_chalit.py +137 -0
- package/azure-function/python/kundali_lib/vedic/char_dasha.py +308 -0
- package/azure-function/python/kundali_lib/vedic/chart.py +126 -0
- package/azure-function/python/kundali_lib/vedic/chart_types.py +338 -0
- package/azure-function/python/kundali_lib/vedic/compatibility.py +705 -0
- package/azure-function/python/kundali_lib/vedic/constants.py +108 -0
- package/azure-function/python/kundali_lib/vedic/dasha_extended.py +262 -0
- package/azure-function/python/kundali_lib/vedic/dasha_systems.py +439 -0
- package/azure-function/python/kundali_lib/vedic/doshas.py +453 -0
- package/azure-function/python/kundali_lib/vedic/gandanta.py +213 -0
- package/azure-function/python/kundali_lib/vedic/gochara.py +277 -0
- package/azure-function/python/kundali_lib/vedic/hora.py +263 -0
- package/azure-function/python/kundali_lib/vedic/houses.py +30 -0
- package/azure-function/python/kundali_lib/vedic/jaimini.py +361 -0
- package/azure-function/python/kundali_lib/vedic/kalachakra.py +226 -0
- package/azure-function/python/kundali_lib/vedic/kartari.py +243 -0
- package/azure-function/python/kundali_lib/vedic/kurmachakra.py +383 -0
- package/azure-function/python/kundali_lib/vedic/lunar_return.py +402 -0
- package/azure-function/python/kundali_lib/vedic/muhurta.py +414 -0
- package/azure-function/python/kundali_lib/vedic/nabhasha.py +349 -0
- package/azure-function/python/kundali_lib/vedic/nakshatra_details.py +945 -0
- package/azure-function/python/kundali_lib/vedic/panchanga.py +297 -0
- package/azure-function/python/kundali_lib/vedic/planets.py +55 -0
- package/azure-function/python/kundali_lib/vedic/shadbala.py +500 -0
- package/azure-function/python/kundali_lib/vedic/special_conditions.py +319 -0
- package/azure-function/python/kundali_lib/vedic/sudarshana.py +232 -0
- package/azure-function/python/kundali_lib/vedic/tajaka.py +482 -0
- package/azure-function/python/kundali_lib/vedic/upagrahas.py +229 -0
- package/azure-function/python/kundali_lib/vedic/varshaphal.py +185 -0
- package/azure-function/python/kundali_lib/vedic/yogas.py +935 -0
- package/azure-function/python/kundali_lib/vedic/zodiac.py +42 -0
- package/azure-function/python/kundali_lib/vedicastro_bridge.py +198 -0
- package/azure-function/requirements.txt +9 -0
- package/index.js +747 -0
- package/kundali-chart-mcp.js +159 -0
- package/kundali_bridge.py +952 -0
- package/package.json +41 -0
- package/python/kundali_lib/__init__.py +1 -0
- package/python/kundali_lib/__pycache__/__init__.cpython-313.pyc +0 -0
- package/python/kundali_lib/__pycache__/ephemeris.cpython-313.pyc +0 -0
- package/python/kundali_lib/__pycache__/geocoder.cpython-313.pyc +0 -0
- package/python/kundali_lib/__pycache__/vedicastro_bridge.cpython-313.pyc +0 -0
- package/python/kundali_lib/ephemeris.py +30 -0
- package/python/kundali_lib/geocoder.py +82 -0
- package/python/kundali_lib/vedic/__init__.py +1 -0
- package/python/kundali_lib/vedic/__pycache__/__init__.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/arishta.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/ashtakavarga.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/avasthas.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/ayanamsa.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/bhava_chalit.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/char_dasha.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/chart.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/chart_types.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/compatibility.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/constants.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/dasha_extended.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/dasha_systems.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/doshas.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/gandanta.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/gochara.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/hora.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/houses.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/jaimini.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/kalachakra.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/kartari.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/kurmachakra.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/lunar_return.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/muhurta.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/nabhasha.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/nakshatra_details.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/panchanga.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/planets.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/shadbala.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/special_conditions.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/sudarshana.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/tajaka.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/upagrahas.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/varshaphal.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/yogas.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/zodiac.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/arishta.py +465 -0
- package/python/kundali_lib/vedic/ashtakavarga.py +213 -0
- package/python/kundali_lib/vedic/avasthas.py +292 -0
- package/python/kundali_lib/vedic/ayanamsa.py +106 -0
- package/python/kundali_lib/vedic/bhava_chalit.py +137 -0
- package/python/kundali_lib/vedic/char_dasha.py +308 -0
- package/python/kundali_lib/vedic/chart.py +126 -0
- package/python/kundali_lib/vedic/chart_types.py +338 -0
- package/python/kundali_lib/vedic/compatibility.py +705 -0
- package/python/kundali_lib/vedic/constants.py +108 -0
- package/python/kundali_lib/vedic/dasha_extended.py +262 -0
- package/python/kundali_lib/vedic/dasha_systems.py +439 -0
- package/python/kundali_lib/vedic/doshas.py +453 -0
- package/python/kundali_lib/vedic/gandanta.py +213 -0
- package/python/kundali_lib/vedic/gochara.py +277 -0
- package/python/kundali_lib/vedic/hora.py +263 -0
- package/python/kundali_lib/vedic/houses.py +30 -0
- package/python/kundali_lib/vedic/jaimini.py +361 -0
- package/python/kundali_lib/vedic/kalachakra.py +226 -0
- package/python/kundali_lib/vedic/kartari.py +243 -0
- package/python/kundali_lib/vedic/kurmachakra.py +383 -0
- package/python/kundali_lib/vedic/lunar_return.py +402 -0
- package/python/kundali_lib/vedic/muhurta.py +414 -0
- package/python/kundali_lib/vedic/nabhasha.py +349 -0
- package/python/kundali_lib/vedic/nakshatra_details.py +945 -0
- package/python/kundali_lib/vedic/panchanga.py +297 -0
- package/python/kundali_lib/vedic/planets.py +55 -0
- package/python/kundali_lib/vedic/shadbala.py +500 -0
- package/python/kundali_lib/vedic/special_conditions.py +319 -0
- package/python/kundali_lib/vedic/sudarshana.py +232 -0
- package/python/kundali_lib/vedic/tajaka.py +482 -0
- package/python/kundali_lib/vedic/upagrahas.py +229 -0
- package/python/kundali_lib/vedic/varshaphal.py +185 -0
- package/python/kundali_lib/vedic/yogas.py +935 -0
- package/python/kundali_lib/vedic/zodiac.py +42 -0
- package/python/kundali_lib/vedicastro_bridge.py +198 -0
- package/remote-server.js +590 -0
- package/requirements.txt +8 -0
- package/setup.sh +218 -0
|
@@ -0,0 +1,952 @@
|
|
|
1
|
+
"""Python bridge used by the Node MCP server to call the bundled Kundali library.
|
|
2
|
+
|
|
3
|
+
This is not an MCP server. It only receives one JSON payload on stdin, calls the
|
|
4
|
+
standalone chart/geocoder services bundled in this package, and prints JSON on stdout.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
import sys
|
|
12
|
+
from datetime import datetime, timezone
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
import certifi
|
|
17
|
+
|
|
18
|
+
PACKAGE_DIR = Path(__file__).resolve().parent
|
|
19
|
+
PYTHON_DIR = PACKAGE_DIR / "python"
|
|
20
|
+
if str(PYTHON_DIR) not in sys.path:
|
|
21
|
+
sys.path.insert(0, str(PYTHON_DIR))
|
|
22
|
+
|
|
23
|
+
os.environ.setdefault("SSL_CERT_FILE", certifi.where())
|
|
24
|
+
os.environ.setdefault("REQUESTS_CA_BUNDLE", certifi.where())
|
|
25
|
+
|
|
26
|
+
from kundali_lib.ephemeris import EphemerisService # noqa: E402
|
|
27
|
+
from kundali_lib.geocoder import GeocoderService # noqa: E402
|
|
28
|
+
from kundali_lib.vedic.arishta import get_arishta_yogas # noqa: E402
|
|
29
|
+
from kundali_lib.vedic.ashtakavarga import get_ashtakavarga # noqa: E402
|
|
30
|
+
from kundali_lib.vedic.avasthas import get_avasthas # noqa: E402
|
|
31
|
+
from kundali_lib.vedic.ayanamsa import ( # noqa: E402
|
|
32
|
+
list_ayanamsa_modes,
|
|
33
|
+
list_house_systems,
|
|
34
|
+
)
|
|
35
|
+
from kundali_lib.vedic.bhava_chalit import get_bhava_chalit # noqa: E402
|
|
36
|
+
from kundali_lib.vedic.char_dasha import ( # noqa: E402
|
|
37
|
+
get_char_dasha,
|
|
38
|
+
get_narayana_dasha,
|
|
39
|
+
)
|
|
40
|
+
from kundali_lib.vedic.chart import (
|
|
41
|
+
get_transit_positions as calculate_transit_positions, # noqa: E402
|
|
42
|
+
)
|
|
43
|
+
from kundali_lib.vedic.chart_types import ( # noqa: E402
|
|
44
|
+
available_chart_types,
|
|
45
|
+
derive_chart,
|
|
46
|
+
)
|
|
47
|
+
from kundali_lib.vedic.compatibility import get_ashtakoot_milan # noqa: E402
|
|
48
|
+
from kundali_lib.vedic.dasha_extended import ( # noqa: E402
|
|
49
|
+
get_pratyantar_dasha,
|
|
50
|
+
get_yogini_dasha,
|
|
51
|
+
)
|
|
52
|
+
from kundali_lib.vedic.dasha_systems import ( # noqa: E402
|
|
53
|
+
get_ashtottari_dasha,
|
|
54
|
+
get_prana_dasha,
|
|
55
|
+
get_sookshma_dasha,
|
|
56
|
+
)
|
|
57
|
+
from kundali_lib.vedic.doshas import ( # noqa: E402
|
|
58
|
+
check_combustion,
|
|
59
|
+
check_graha_yuddha,
|
|
60
|
+
check_kaal_sarp_dosha,
|
|
61
|
+
check_mangal_dosha,
|
|
62
|
+
check_sade_sati,
|
|
63
|
+
)
|
|
64
|
+
from kundali_lib.vedic.gandanta import check_gandanta, get_gandanta_info # noqa: E402
|
|
65
|
+
from kundali_lib.vedic.gochara import get_gochara # noqa: E402
|
|
66
|
+
from kundali_lib.vedic.hora import get_hora_chart, get_planetary_hour # noqa: E402
|
|
67
|
+
from kundali_lib.vedic.jaimini import ( # noqa: E402
|
|
68
|
+
get_jaimini_aspects,
|
|
69
|
+
get_jaimini_karakas,
|
|
70
|
+
get_karakamsha,
|
|
71
|
+
)
|
|
72
|
+
from kundali_lib.vedic.kalachakra import get_kalachakra_dasha # noqa: E402
|
|
73
|
+
from kundali_lib.vedic.kartari import check_kartari_yoga # noqa: E402
|
|
74
|
+
from kundali_lib.vedic.kurmachakra import ( # noqa: E402
|
|
75
|
+
get_full_kurmachakra_chart,
|
|
76
|
+
get_kurmachakra,
|
|
77
|
+
get_travel_direction_score,
|
|
78
|
+
)
|
|
79
|
+
from kundali_lib.vedic.lunar_return import ( # noqa: E402
|
|
80
|
+
get_lunar_return,
|
|
81
|
+
get_prasna_chart,
|
|
82
|
+
)
|
|
83
|
+
from kundali_lib.vedic.muhurta import ( # noqa: E402
|
|
84
|
+
get_best_muhurta_in_range,
|
|
85
|
+
get_muhurta_score,
|
|
86
|
+
)
|
|
87
|
+
from kundali_lib.vedic.nabhasha import get_nabhasha_yogas # noqa: E402
|
|
88
|
+
from kundali_lib.vedic.nakshatra_details import ( # noqa: E402
|
|
89
|
+
get_nakshatra_compatibility,
|
|
90
|
+
get_nakshatra_details,
|
|
91
|
+
get_planet_nakshatra_analysis,
|
|
92
|
+
)
|
|
93
|
+
from kundali_lib.vedic.panchanga import get_panchanga # noqa: E402
|
|
94
|
+
from kundali_lib.vedic.shadbala import get_shadbala # noqa: E402
|
|
95
|
+
from kundali_lib.vedic.special_conditions import ( # noqa: E402
|
|
96
|
+
check_pushkara_navamsha,
|
|
97
|
+
check_vargottama,
|
|
98
|
+
get_dig_bala,
|
|
99
|
+
)
|
|
100
|
+
from kundali_lib.vedic.special_conditions import (
|
|
101
|
+
get_jaimini_karakas as sc_jaimini_karakas,
|
|
102
|
+
)
|
|
103
|
+
from kundali_lib.vedic.sudarshana import get_sudarshana_chakra # noqa: E402
|
|
104
|
+
from kundali_lib.vedic.tajaka import get_tajaka_analysis # noqa: E402
|
|
105
|
+
from kundali_lib.vedic.upagrahas import get_upagrahas # noqa: E402
|
|
106
|
+
from kundali_lib.vedic.varshaphal import get_varshaphal # noqa: E402 # noqa: E402
|
|
107
|
+
from kundali_lib.vedic.yogas import get_yogas # noqa: E402
|
|
108
|
+
from kundali_lib.vedicastro_bridge import ( # noqa: E402
|
|
109
|
+
get_full_chart as vc_full_chart,
|
|
110
|
+
)
|
|
111
|
+
from kundali_lib.vedicastro_bridge import (
|
|
112
|
+
get_planetary_aspects as vc_aspects,
|
|
113
|
+
)
|
|
114
|
+
from kundali_lib.vedicastro_bridge import (
|
|
115
|
+
get_significators as vc_significators,
|
|
116
|
+
)
|
|
117
|
+
from kundali_lib.vedicastro_bridge import (
|
|
118
|
+
get_vimshottari_dasha as vc_dasha,
|
|
119
|
+
)
|
|
120
|
+
from kundali_lib.vedicastro_bridge import (
|
|
121
|
+
list_vedicastro_options as vc_options,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
ephemeris_service = EphemerisService()
|
|
125
|
+
geocoder_service = GeocoderService()
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def parse_birth_datetime(date_of_birth: str, time_of_birth: str) -> datetime:
|
|
129
|
+
date_text = date_of_birth.strip()
|
|
130
|
+
time_text = time_of_birth.strip()
|
|
131
|
+
|
|
132
|
+
try:
|
|
133
|
+
if "T" in date_text:
|
|
134
|
+
date_part = datetime.fromisoformat(date_text).date()
|
|
135
|
+
else:
|
|
136
|
+
date_part = datetime.strptime(date_text, "%Y-%m-%d").date()
|
|
137
|
+
except ValueError as exc:
|
|
138
|
+
raise ValueError("date_of_birth must be in YYYY-MM-DD format") from exc
|
|
139
|
+
|
|
140
|
+
for fmt in ("%H:%M", "%H:%M:%S"):
|
|
141
|
+
try:
|
|
142
|
+
time_part = datetime.strptime(time_text, fmt).time()
|
|
143
|
+
return datetime.combine(date_part, time_part)
|
|
144
|
+
except ValueError:
|
|
145
|
+
continue
|
|
146
|
+
|
|
147
|
+
raise ValueError("time_of_birth must be in HH:MM or HH:MM:SS format")
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def resolve_location(
|
|
151
|
+
city: str,
|
|
152
|
+
latitude: float | None,
|
|
153
|
+
longitude: float | None,
|
|
154
|
+
timezone: str | None,
|
|
155
|
+
) -> tuple[float, float, str]:
|
|
156
|
+
if (latitude is None) != (longitude is None):
|
|
157
|
+
raise ValueError("latitude and longitude must be provided together")
|
|
158
|
+
|
|
159
|
+
if latitude is not None and longitude is not None:
|
|
160
|
+
resolved_timezone = (
|
|
161
|
+
timezone or geocoder_service.get_timezone(latitude, longitude) or "UTC"
|
|
162
|
+
)
|
|
163
|
+
return latitude, longitude, resolved_timezone
|
|
164
|
+
|
|
165
|
+
city_info = geocoder_service.get_city_info(city)
|
|
166
|
+
if not city_info:
|
|
167
|
+
raise ValueError(
|
|
168
|
+
f"Could not find coordinates for city '{city}'. "
|
|
169
|
+
"Provide latitude, longitude, and optionally timezone to avoid geocoding."
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
return (
|
|
173
|
+
float(city_info["latitude"]),
|
|
174
|
+
float(city_info["longitude"]),
|
|
175
|
+
str(city_info.get("timezone") or "UTC"),
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def generate_kundali(payload: dict[str, Any]) -> dict[str, Any]:
|
|
180
|
+
birth_datetime = parse_birth_datetime(
|
|
181
|
+
payload["date_of_birth"], payload["time_of_birth"]
|
|
182
|
+
)
|
|
183
|
+
latitude, longitude, timezone = resolve_location(
|
|
184
|
+
city=payload["city"],
|
|
185
|
+
latitude=payload.get("latitude"),
|
|
186
|
+
longitude=payload.get("longitude"),
|
|
187
|
+
timezone=payload.get("timezone"),
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
base_chart = ephemeris_service.calculate_chart(
|
|
191
|
+
name=payload["name"],
|
|
192
|
+
birth_datetime=birth_datetime,
|
|
193
|
+
latitude=latitude,
|
|
194
|
+
longitude=longitude,
|
|
195
|
+
timezone=timezone,
|
|
196
|
+
ayanamsa_mode=payload.get("ayanamsa_mode"),
|
|
197
|
+
house_system=payload.get("house_system"),
|
|
198
|
+
)
|
|
199
|
+
return derive_chart(base_chart, payload.get("chart_type"))
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def list_available_chart_types(payload: dict[str, Any]) -> dict[str, Any]:
|
|
203
|
+
return available_chart_types()
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def list_ayanamsa_modes_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
207
|
+
return list_ayanamsa_modes()
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def list_house_systems_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
211
|
+
return list_house_systems()
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def search_birth_places(payload: dict[str, Any]) -> list[dict[str, Any]]:
|
|
215
|
+
limit = int(payload.get("limit") or 8)
|
|
216
|
+
safe_limit = max(1, min(limit, 10))
|
|
217
|
+
return geocoder_service.search_cities(payload["query"], limit=safe_limit)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def get_timezone_for_coordinates(payload: dict[str, Any]) -> dict[str, Any]:
|
|
221
|
+
latitude = float(payload["latitude"])
|
|
222
|
+
longitude = float(payload["longitude"])
|
|
223
|
+
timezone_name = geocoder_service.get_timezone(latitude, longitude) or "UTC"
|
|
224
|
+
return {
|
|
225
|
+
"latitude": latitude,
|
|
226
|
+
"longitude": longitude,
|
|
227
|
+
"timezone": timezone_name,
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def parse_transit_datetime(payload: dict[str, Any]) -> datetime:
|
|
232
|
+
dt_text = (payload.get("datetime_utc") or "").strip()
|
|
233
|
+
if not dt_text:
|
|
234
|
+
return datetime.now(timezone.utc)
|
|
235
|
+
|
|
236
|
+
try:
|
|
237
|
+
normalized = dt_text.replace("Z", "+00:00")
|
|
238
|
+
parsed = datetime.fromisoformat(normalized)
|
|
239
|
+
except ValueError as exc:
|
|
240
|
+
raise ValueError(
|
|
241
|
+
"datetime_utc must be an ISO datetime, for example 2026-06-20T12:00:00Z"
|
|
242
|
+
) from exc
|
|
243
|
+
|
|
244
|
+
if parsed.tzinfo is None:
|
|
245
|
+
return parsed.replace(tzinfo=timezone.utc)
|
|
246
|
+
return parsed.astimezone(timezone.utc)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def get_transit_positions(payload: dict[str, Any]) -> dict[str, Any]:
|
|
250
|
+
dt_utc = parse_transit_datetime(payload)
|
|
251
|
+
return {
|
|
252
|
+
"datetime_utc": dt_utc.isoformat(),
|
|
253
|
+
"planetary_positions": calculate_transit_positions(dt_utc),
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def get_extended_chart(payload: dict[str, Any]) -> dict[str, Any]:
|
|
258
|
+
"""Enhanced chart using vedicastro — includes SubLord/KP data."""
|
|
259
|
+
return vc_full_chart(payload)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def get_vimshottari_dasha(payload: dict[str, Any]) -> dict[str, Any]:
|
|
263
|
+
"""Vimshottari Dasha (mahadasha + antardasha) for a birth chart."""
|
|
264
|
+
return vc_dasha(payload)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def get_planetary_aspects_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
268
|
+
"""Planetary aspects (conjunction, sextile, square, trine, opposition, etc.)."""
|
|
269
|
+
return vc_aspects(payload)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def get_significators(payload: dict[str, Any]) -> dict[str, Any]:
|
|
273
|
+
"""Planet-wise and house-wise significators (KP system)."""
|
|
274
|
+
return vc_significators(payload)
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def list_extended_options(payload: dict[str, Any]) -> dict[str, Any]:
|
|
278
|
+
"""List supported ayanamsas and house systems for extended (vedicastro) tools."""
|
|
279
|
+
return vc_options(payload)
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
# ─── New comprehensive tools ───────────────────────────────────────────────
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def _require_base_chart(payload: dict[str, Any]) -> dict[str, Any]:
|
|
286
|
+
"""Generate a base chart from payload (same flow as generate_kundali, lagna type only)."""
|
|
287
|
+
birth_datetime = parse_birth_datetime(
|
|
288
|
+
payload["date_of_birth"], payload["time_of_birth"]
|
|
289
|
+
)
|
|
290
|
+
lat, lon, tz = resolve_location(
|
|
291
|
+
city=payload.get("city", ""),
|
|
292
|
+
latitude=payload.get("latitude"),
|
|
293
|
+
longitude=payload.get("longitude"),
|
|
294
|
+
timezone=payload.get("timezone"),
|
|
295
|
+
)
|
|
296
|
+
return ephemeris_service.calculate_chart(
|
|
297
|
+
name=payload.get("name", "Chart"),
|
|
298
|
+
birth_datetime=birth_datetime,
|
|
299
|
+
latitude=lat,
|
|
300
|
+
longitude=lon,
|
|
301
|
+
timezone=tz,
|
|
302
|
+
ayanamsa_mode=payload.get("ayanamsa_mode"),
|
|
303
|
+
house_system=payload.get("house_system"),
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def get_panchanga_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
308
|
+
"""Get Panchanga (Tithi, Vara, Nakshatra, Yoga, Karana) for a given datetime and location."""
|
|
309
|
+
import pytz
|
|
310
|
+
import swisseph as swe
|
|
311
|
+
|
|
312
|
+
# Parse datetime
|
|
313
|
+
dt_text = (payload.get("datetime") or "").strip()
|
|
314
|
+
tz_name = payload.get("timezone") or "UTC"
|
|
315
|
+
if dt_text:
|
|
316
|
+
dt = datetime.fromisoformat(dt_text.replace("Z", "+00:00"))
|
|
317
|
+
if dt.tzinfo is None:
|
|
318
|
+
dt = pytz.timezone(tz_name).localize(dt)
|
|
319
|
+
else:
|
|
320
|
+
dt = datetime.now(pytz.UTC)
|
|
321
|
+
# Convert to JD
|
|
322
|
+
utc_dt = dt.astimezone(pytz.UTC)
|
|
323
|
+
h = utc_dt.hour + utc_dt.minute / 60.0 + utc_dt.second / 3600.0
|
|
324
|
+
swe.set_ephe_path(None)
|
|
325
|
+
swe.set_sid_mode(swe.SIDM_LAHIRI)
|
|
326
|
+
from kundali_lib.vedic.ayanamsa import _SIDM_MAP
|
|
327
|
+
|
|
328
|
+
ayanamsa_key = (payload.get("ayanamsa_mode") or "lahiri").lower()
|
|
329
|
+
swe.set_sid_mode(_SIDM_MAP.get(ayanamsa_key, swe.SIDM_LAHIRI))
|
|
330
|
+
jd = swe.julday(utc_dt.year, utc_dt.month, utc_dt.day, h)
|
|
331
|
+
flags = swe.FLG_SWIEPH | swe.FLG_SPEED | swe.FLG_SIDEREAL
|
|
332
|
+
sun_xx, _ = swe.calc_ut(jd, swe.SUN, flags)
|
|
333
|
+
moon_xx, _ = swe.calc_ut(jd, swe.MOON, flags)
|
|
334
|
+
panchanga = get_panchanga(sun_xx[0], moon_xx[0], jd)
|
|
335
|
+
panchanga["datetime"] = utc_dt.isoformat()
|
|
336
|
+
panchanga["ayanamsa_mode"] = ayanamsa_key
|
|
337
|
+
return panchanga
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def get_doshas_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
341
|
+
"""Check all major doshas for a birth chart: Mangal, Kaal Sarp, Sade Sati, Graha Yuddha, Combustion."""
|
|
342
|
+
base = _require_base_chart(payload)
|
|
343
|
+
planets = base["planetary_positions"]
|
|
344
|
+
asc = base["ascendant"]
|
|
345
|
+
moon_rashi = base["moon_sign"]
|
|
346
|
+
current_saturn_rashi = payload.get("current_saturn_rashi")
|
|
347
|
+
result = {
|
|
348
|
+
"mangal_dosha": check_mangal_dosha(planets, asc),
|
|
349
|
+
"kaal_sarp_dosha": check_kaal_sarp_dosha(planets),
|
|
350
|
+
"graha_yuddha": check_graha_yuddha(planets),
|
|
351
|
+
"combustion": check_combustion(planets),
|
|
352
|
+
}
|
|
353
|
+
if current_saturn_rashi:
|
|
354
|
+
result["sade_sati"] = check_sade_sati(moon_rashi, current_saturn_rashi)
|
|
355
|
+
else:
|
|
356
|
+
result["sade_sati"] = {
|
|
357
|
+
"note": "Provide current_saturn_rashi to check Sade Sati"
|
|
358
|
+
}
|
|
359
|
+
return result
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def get_yogas_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
363
|
+
"""Identify all major Vedic Yogas in a birth chart."""
|
|
364
|
+
base = _require_base_chart(payload)
|
|
365
|
+
return get_yogas(base)
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def get_compatibility_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
369
|
+
"""Ashtakoot Milan: 36-point Vedic marriage compatibility between two birth charts."""
|
|
370
|
+
# Expect payload to have chart1 and chart2 sub-dicts with birth details
|
|
371
|
+
chart1_payload = payload["chart1"]
|
|
372
|
+
chart2_payload = payload["chart2"]
|
|
373
|
+
chart1 = _require_base_chart_from(chart1_payload)
|
|
374
|
+
chart2 = _require_base_chart_from(chart2_payload)
|
|
375
|
+
return get_ashtakoot_milan(chart1, chart2)
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def _require_base_chart_from(p: dict[str, Any]) -> dict[str, Any]:
|
|
379
|
+
birth_datetime = parse_birth_datetime(p["date_of_birth"], p["time_of_birth"])
|
|
380
|
+
lat, lon, tz = resolve_location(
|
|
381
|
+
city=p.get("city", ""),
|
|
382
|
+
latitude=p.get("latitude"),
|
|
383
|
+
longitude=p.get("longitude"),
|
|
384
|
+
timezone=p.get("timezone"),
|
|
385
|
+
)
|
|
386
|
+
return ephemeris_service.calculate_chart(
|
|
387
|
+
name=p.get("name", "Chart"),
|
|
388
|
+
birth_datetime=birth_datetime,
|
|
389
|
+
latitude=lat,
|
|
390
|
+
longitude=lon,
|
|
391
|
+
timezone=tz,
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
def get_special_conditions_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
396
|
+
"""Check Vargottama, Pushkara Navamsha, Dig Bala, and Jaimini Karakas."""
|
|
397
|
+
from kundali_lib.vedic.chart_types import DIVISIONAL_CHARTS, derive_chart
|
|
398
|
+
|
|
399
|
+
base = _require_base_chart(payload)
|
|
400
|
+
d9 = derive_chart(base, "d9")
|
|
401
|
+
d9_positions = d9.get("planetary_positions", [])
|
|
402
|
+
return {
|
|
403
|
+
"vargottama": check_vargottama(base["planetary_positions"], d9_positions),
|
|
404
|
+
"pushkara_navamsha": check_pushkara_navamsha(base["planetary_positions"]),
|
|
405
|
+
"dig_bala": get_dig_bala(base["planetary_positions"]),
|
|
406
|
+
"jaimini_karakas": sc_jaimini_karakas(base["planetary_positions"]),
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def get_pratyantar_dasha_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
411
|
+
"""Get Pratyantar Dasha (3rd level: Mahadasha → Antardasha → Pratyantar) for a birth chart."""
|
|
412
|
+
birth_dt = parse_birth_datetime(payload["date_of_birth"], payload["time_of_birth"])
|
|
413
|
+
lat, lon, tz = resolve_location(
|
|
414
|
+
city=payload.get("city", ""),
|
|
415
|
+
latitude=payload.get("latitude"),
|
|
416
|
+
longitude=payload.get("longitude"),
|
|
417
|
+
timezone=payload.get("timezone"),
|
|
418
|
+
)
|
|
419
|
+
base = ephemeris_service.calculate_chart("Chart", birth_dt, lat, lon, tz)
|
|
420
|
+
moon = next(p for p in base["planetary_positions"] if p["name"] == "Moon")
|
|
421
|
+
import pytz
|
|
422
|
+
|
|
423
|
+
tz_obj = pytz.timezone(tz)
|
|
424
|
+
birth_dt_aware = tz_obj.localize(birth_dt)
|
|
425
|
+
return get_pratyantar_dasha(birth_dt_aware, moon["longitude"], moon["nakshatra"])
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
def get_yogini_dasha_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
429
|
+
"""Get Yogini Dasha (8-yogini 36-year cycle) for a birth chart."""
|
|
430
|
+
birth_dt = parse_birth_datetime(payload["date_of_birth"], payload["time_of_birth"])
|
|
431
|
+
lat, lon, tz = resolve_location(
|
|
432
|
+
city=payload.get("city", ""),
|
|
433
|
+
latitude=payload.get("latitude"),
|
|
434
|
+
longitude=payload.get("longitude"),
|
|
435
|
+
timezone=payload.get("timezone"),
|
|
436
|
+
)
|
|
437
|
+
base = ephemeris_service.calculate_chart("Chart", birth_dt, lat, lon, tz)
|
|
438
|
+
moon = next(p for p in base["planetary_positions"] if p["name"] == "Moon")
|
|
439
|
+
import pytz
|
|
440
|
+
|
|
441
|
+
tz_obj = pytz.timezone(tz)
|
|
442
|
+
birth_dt_aware = tz_obj.localize(birth_dt)
|
|
443
|
+
return get_yogini_dasha(birth_dt_aware, moon["longitude"], moon["nakshatra"])
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
def get_varshaphal_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
447
|
+
"""Get Varshaphal (Solar Return chart) for a given year of life."""
|
|
448
|
+
birth_dt = parse_birth_datetime(payload["date_of_birth"], payload["time_of_birth"])
|
|
449
|
+
lat, lon, birth_tz = resolve_location(
|
|
450
|
+
city=payload.get("city", ""),
|
|
451
|
+
latitude=payload.get("latitude"),
|
|
452
|
+
longitude=payload.get("longitude"),
|
|
453
|
+
timezone=payload.get("timezone"),
|
|
454
|
+
)
|
|
455
|
+
year_of_life = int(payload["year_of_life"])
|
|
456
|
+
query_lat = float(payload.get("query_latitude") or lat)
|
|
457
|
+
query_lon = float(payload.get("query_longitude") or lon)
|
|
458
|
+
query_tz = payload.get("query_timezone") or birth_tz
|
|
459
|
+
# get_varshaphal expects naive birth_dt and handles tz internally
|
|
460
|
+
return get_varshaphal(
|
|
461
|
+
birth_dt, lat, lon, birth_tz, year_of_life, query_lat, query_lon, query_tz
|
|
462
|
+
)
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
def get_ashtakavarga_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
466
|
+
"""Get Ashtakavarga (benefic point system) for a birth chart."""
|
|
467
|
+
base = _require_base_chart(payload)
|
|
468
|
+
return get_ashtakavarga(base)
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
def get_shadbala_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
472
|
+
"""Get Shadbala (six-fold planetary strength) for a birth chart."""
|
|
473
|
+
base = _require_base_chart(payload)
|
|
474
|
+
return get_shadbala(base)
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
def get_jaimini_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
478
|
+
"""Get full Jaimini astrology analysis: Karakas, aspects, and Karakamsha."""
|
|
479
|
+
from kundali_lib.vedic.chart_types import derive_chart
|
|
480
|
+
|
|
481
|
+
base = _require_base_chart(payload)
|
|
482
|
+
d9 = derive_chart(base, "d9")
|
|
483
|
+
d9_positions = d9.get("planetary_positions", [])
|
|
484
|
+
return {
|
|
485
|
+
"karakas": get_jaimini_karakas(base["planetary_positions"]),
|
|
486
|
+
"aspects": get_jaimini_aspects(base),
|
|
487
|
+
"karakamsha": get_karakamsha(base["planetary_positions"], d9_positions),
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
def get_muhurta_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
492
|
+
"""Score a datetime for Muhurta (auspicious timing) for a given event type."""
|
|
493
|
+
panchanga = get_panchanga_tool(payload)
|
|
494
|
+
# Get planetary positions for the same moment (for aspects context)
|
|
495
|
+
import pytz
|
|
496
|
+
import swisseph as swe
|
|
497
|
+
|
|
498
|
+
swe.set_ephe_path(None)
|
|
499
|
+
swe.set_sid_mode(swe.SIDM_LAHIRI)
|
|
500
|
+
dt_text = (payload.get("datetime") or "").strip()
|
|
501
|
+
tz_name = payload.get("timezone") or "UTC"
|
|
502
|
+
if dt_text:
|
|
503
|
+
dt = datetime.fromisoformat(dt_text.replace("Z", "+00:00"))
|
|
504
|
+
if dt.tzinfo is None:
|
|
505
|
+
dt = pytz.timezone(tz_name).localize(dt)
|
|
506
|
+
else:
|
|
507
|
+
dt = datetime.now(pytz.UTC)
|
|
508
|
+
event_type = payload.get("event_type") or "general"
|
|
509
|
+
return get_muhurta_score(panchanga, [], event_type)
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
def get_best_muhurta_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
513
|
+
"""Find the best Muhurta windows in a date range for an event type."""
|
|
514
|
+
from datetime import datetime as _dt
|
|
515
|
+
|
|
516
|
+
import pytz
|
|
517
|
+
|
|
518
|
+
start_str = payload["start_datetime"]
|
|
519
|
+
end_str = payload["end_datetime"]
|
|
520
|
+
tz_name = payload.get("timezone") or "UTC"
|
|
521
|
+
lat = float(payload.get("latitude") or 28.6)
|
|
522
|
+
lon = float(payload.get("longitude") or 77.2)
|
|
523
|
+
event_type = payload.get("event_type") or "general"
|
|
524
|
+
tz_obj = pytz.timezone(tz_name)
|
|
525
|
+
start_dt = tz_obj.localize(_dt.fromisoformat(start_str))
|
|
526
|
+
end_dt = tz_obj.localize(_dt.fromisoformat(end_str))
|
|
527
|
+
return {
|
|
528
|
+
"best_windows": get_best_muhurta_in_range(
|
|
529
|
+
start_dt, end_dt, lat, lon, event_type
|
|
530
|
+
)
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
# ─── New comprehensive tools batch 2 ──────────────────────────────────────────
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
def get_avasthas_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
538
|
+
"""Get planetary Avasthas (9 states) and Baladi Avasthas for a birth chart."""
|
|
539
|
+
base = _require_base_chart(payload)
|
|
540
|
+
return {
|
|
541
|
+
"avasthas": get_avasthas(base["planetary_positions"], base["ascendant"]),
|
|
542
|
+
"ayanamsa": base.get("ayanamsa_name"),
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
|
|
546
|
+
def get_upagrahas_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
547
|
+
"""Get Upagrahas: Gulika, Mandi, Dhuma, Vyatipata, Parivesha, Indra Chapa, Upaketu."""
|
|
548
|
+
import pytz
|
|
549
|
+
import swisseph as swe
|
|
550
|
+
|
|
551
|
+
birth_dt = parse_birth_datetime(payload["date_of_birth"], payload["time_of_birth"])
|
|
552
|
+
lat, lon, tz = resolve_location(
|
|
553
|
+
city=payload.get("city", ""),
|
|
554
|
+
latitude=payload.get("latitude"),
|
|
555
|
+
longitude=payload.get("longitude"),
|
|
556
|
+
timezone=payload.get("timezone"),
|
|
557
|
+
)
|
|
558
|
+
swe.set_ephe_path(None)
|
|
559
|
+
swe.set_sid_mode(swe.SIDM_LAHIRI)
|
|
560
|
+
tz_obj = pytz.timezone(tz)
|
|
561
|
+
birth_aware = tz_obj.localize(birth_dt)
|
|
562
|
+
utc = birth_aware.astimezone(pytz.UTC)
|
|
563
|
+
h = utc.hour + utc.minute / 60.0 + utc.second / 3600.0
|
|
564
|
+
jd = swe.julday(utc.year, utc.month, utc.day, h)
|
|
565
|
+
ayanamsa = swe.get_ayanamsa_ut(jd)
|
|
566
|
+
return get_upagrahas(jd, lat, lon, ayanamsa)
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
def get_gandanta_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
570
|
+
"""Check which planets (if any) are in Gandanta zones (water-fire sign junctions)."""
|
|
571
|
+
base = _require_base_chart(payload)
|
|
572
|
+
return {
|
|
573
|
+
"gandanta_planets": check_gandanta(base["planetary_positions"]),
|
|
574
|
+
"gandanta_zones_info": get_gandanta_info(),
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
def get_kartari_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
579
|
+
"""Check Kartari Yoga: planets or houses hemmed between benefics (Subha) or malefics (Papa)."""
|
|
580
|
+
base = _require_base_chart(payload)
|
|
581
|
+
# Convert houses dict from {"House_1": "Capricorn", ...} to {1: ["Sun", ...], ...}
|
|
582
|
+
houses_map: dict[int, list[str]] = {}
|
|
583
|
+
for planet in base["planetary_positions"]:
|
|
584
|
+
h = int(planet["house"])
|
|
585
|
+
houses_map.setdefault(h, []).append(planet["name"])
|
|
586
|
+
return {
|
|
587
|
+
"kartari_yogas": check_kartari_yoga(base["planetary_positions"], houses_map)
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
|
|
591
|
+
def get_nakshatra_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
592
|
+
"""Get deep Nakshatra analysis: deity, symbol, gana, element, padas, and keywords for each planet."""
|
|
593
|
+
base = _require_base_chart(payload)
|
|
594
|
+
result = {
|
|
595
|
+
"planet_nakshatra_analysis": get_planet_nakshatra_analysis(
|
|
596
|
+
base["planetary_positions"]
|
|
597
|
+
)
|
|
598
|
+
}
|
|
599
|
+
if payload.get("nakshatra_name"):
|
|
600
|
+
result["nakshatra_details"] = get_nakshatra_details(payload["nakshatra_name"])
|
|
601
|
+
return result
|
|
602
|
+
|
|
603
|
+
|
|
604
|
+
def get_gochara_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
605
|
+
"""Get Gochara (Vedic transit analysis): how current transiting planets affect natal Moon houses."""
|
|
606
|
+
natal = _require_base_chart(payload)
|
|
607
|
+
# Build transit chart for current or specified datetime
|
|
608
|
+
import pytz
|
|
609
|
+
import swisseph as swe
|
|
610
|
+
|
|
611
|
+
dt_text = (payload.get("transit_datetime") or "").strip()
|
|
612
|
+
swe.set_ephe_path(None)
|
|
613
|
+
swe.set_sid_mode(swe.SIDM_LAHIRI)
|
|
614
|
+
if dt_text:
|
|
615
|
+
dt = datetime.fromisoformat(dt_text.replace("Z", "+00:00"))
|
|
616
|
+
if dt.tzinfo is None:
|
|
617
|
+
dt = pytz.UTC.localize(dt)
|
|
618
|
+
else:
|
|
619
|
+
dt = datetime.now(pytz.UTC)
|
|
620
|
+
utc = dt.astimezone(pytz.UTC)
|
|
621
|
+
h = utc.hour + utc.minute / 60.0 + utc.second / 3600.0
|
|
622
|
+
jd = swe.julday(utc.year, utc.month, utc.day, h)
|
|
623
|
+
flags = swe.FLG_SWIEPH | swe.FLG_SPEED | swe.FLG_SIDEREAL
|
|
624
|
+
from kundali_lib.vedic.chart import get_transit_positions as _gtp
|
|
625
|
+
|
|
626
|
+
transit_planets = _gtp(utc)
|
|
627
|
+
return get_gochara(natal, transit_planets)
|
|
628
|
+
|
|
629
|
+
|
|
630
|
+
def get_bhava_chalit_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
631
|
+
"""Get Bhava Chalit chart: house positions based on equal divisions from house cusps."""
|
|
632
|
+
base = _require_base_chart(payload)
|
|
633
|
+
return get_bhava_chalit(base)
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
def get_hora_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
637
|
+
"""Get Planetary Hour (Hora) for a given datetime and location."""
|
|
638
|
+
import pytz
|
|
639
|
+
|
|
640
|
+
dt_text = (payload.get("datetime") or "").strip()
|
|
641
|
+
tz_name = payload.get("timezone") or "UTC"
|
|
642
|
+
lat = float(payload.get("latitude") or 28.6)
|
|
643
|
+
lon = float(payload.get("longitude") or 77.2)
|
|
644
|
+
if dt_text:
|
|
645
|
+
dt = datetime.fromisoformat(dt_text.replace("Z", "+00:00"))
|
|
646
|
+
if dt.tzinfo is None:
|
|
647
|
+
dt = pytz.timezone(tz_name).localize(dt)
|
|
648
|
+
dt_utc = dt.astimezone(pytz.UTC)
|
|
649
|
+
else:
|
|
650
|
+
dt_utc = datetime.now(pytz.UTC)
|
|
651
|
+
return get_planetary_hour(dt_utc, lat, lon)
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
def get_sudarshana_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
655
|
+
"""Get Sudarshana Chakra: triple-layer analysis from Lagna, Moon, and Sun simultaneously."""
|
|
656
|
+
base = _require_base_chart(payload)
|
|
657
|
+
return get_sudarshana_chakra(base)
|
|
658
|
+
|
|
659
|
+
|
|
660
|
+
def get_ashtottari_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
661
|
+
"""Get Ashtottari Dasha (108-year cycle alternative to Vimshottari)."""
|
|
662
|
+
birth_dt = parse_birth_datetime(payload["date_of_birth"], payload["time_of_birth"])
|
|
663
|
+
lat, lon, tz = resolve_location(
|
|
664
|
+
city=payload.get("city", ""),
|
|
665
|
+
latitude=payload.get("latitude"),
|
|
666
|
+
longitude=payload.get("longitude"),
|
|
667
|
+
timezone=payload.get("timezone"),
|
|
668
|
+
)
|
|
669
|
+
base = ephemeris_service.calculate_chart("Chart", birth_dt, lat, lon, tz)
|
|
670
|
+
moon = next(p for p in base["planetary_positions"] if p["name"] == "Moon")
|
|
671
|
+
import pytz
|
|
672
|
+
|
|
673
|
+
birth_aware = pytz.timezone(tz).localize(birth_dt)
|
|
674
|
+
return get_ashtottari_dasha(birth_aware, moon["longitude"], moon["nakshatra"])
|
|
675
|
+
|
|
676
|
+
|
|
677
|
+
def get_sookshma_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
678
|
+
"""Get Sookshma Dasha (4th level: MD→AD→PD→SD) — shows current running period at all 4 levels."""
|
|
679
|
+
birth_dt = parse_birth_datetime(payload["date_of_birth"], payload["time_of_birth"])
|
|
680
|
+
lat, lon, tz = resolve_location(
|
|
681
|
+
city=payload.get("city", ""),
|
|
682
|
+
latitude=payload.get("latitude"),
|
|
683
|
+
longitude=payload.get("longitude"),
|
|
684
|
+
timezone=payload.get("timezone"),
|
|
685
|
+
)
|
|
686
|
+
base = ephemeris_service.calculate_chart("Chart", birth_dt, lat, lon, tz)
|
|
687
|
+
moon = next(p for p in base["planetary_positions"] if p["name"] == "Moon")
|
|
688
|
+
# get_sookshma_dasha expects naive birth_dt
|
|
689
|
+
return get_sookshma_dasha(birth_dt, moon["longitude"], moon["nakshatra"])
|
|
690
|
+
|
|
691
|
+
|
|
692
|
+
def get_prana_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
693
|
+
"""Get Prana Dasha (5th level: MD→AD→PD→SD→PRD) — finest level of Vimshottari Dasha."""
|
|
694
|
+
birth_dt = parse_birth_datetime(payload["date_of_birth"], payload["time_of_birth"])
|
|
695
|
+
lat, lon, tz = resolve_location(
|
|
696
|
+
city=payload.get("city", ""),
|
|
697
|
+
latitude=payload.get("latitude"),
|
|
698
|
+
longitude=payload.get("longitude"),
|
|
699
|
+
timezone=payload.get("timezone"),
|
|
700
|
+
)
|
|
701
|
+
base = ephemeris_service.calculate_chart("Chart", birth_dt, lat, lon, tz)
|
|
702
|
+
moon = next(p for p in base["planetary_positions"] if p["name"] == "Moon")
|
|
703
|
+
# get_prana_dasha expects naive birth_dt
|
|
704
|
+
return get_prana_dasha(birth_dt, moon["longitude"], moon["nakshatra"])
|
|
705
|
+
|
|
706
|
+
|
|
707
|
+
def get_char_dasha_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
708
|
+
"""Get Char Dasha (Jaimini sign-based dasha system)."""
|
|
709
|
+
birth_dt = parse_birth_datetime(payload["date_of_birth"], payload["time_of_birth"])
|
|
710
|
+
lat, lon, tz = resolve_location(
|
|
711
|
+
city=payload.get("city", ""),
|
|
712
|
+
latitude=payload.get("latitude"),
|
|
713
|
+
longitude=payload.get("longitude"),
|
|
714
|
+
timezone=payload.get("timezone"),
|
|
715
|
+
)
|
|
716
|
+
base = ephemeris_service.calculate_chart("Chart", birth_dt, lat, lon, tz)
|
|
717
|
+
import pytz
|
|
718
|
+
|
|
719
|
+
birth_aware = pytz.timezone(tz).localize(birth_dt)
|
|
720
|
+
return get_char_dasha(base, birth_aware)
|
|
721
|
+
|
|
722
|
+
|
|
723
|
+
def get_narayana_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
724
|
+
"""Get Narayana Dasha (Jaimini sign-based dasha for worldly events)."""
|
|
725
|
+
birth_dt = parse_birth_datetime(payload["date_of_birth"], payload["time_of_birth"])
|
|
726
|
+
lat, lon, tz = resolve_location(
|
|
727
|
+
city=payload.get("city", ""),
|
|
728
|
+
latitude=payload.get("latitude"),
|
|
729
|
+
longitude=payload.get("longitude"),
|
|
730
|
+
timezone=payload.get("timezone"),
|
|
731
|
+
)
|
|
732
|
+
base = ephemeris_service.calculate_chart("Chart", birth_dt, lat, lon, tz)
|
|
733
|
+
import pytz
|
|
734
|
+
|
|
735
|
+
birth_aware = pytz.timezone(tz).localize(birth_dt)
|
|
736
|
+
return get_narayana_dasha(base, birth_aware)
|
|
737
|
+
|
|
738
|
+
|
|
739
|
+
def get_tajaka_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
740
|
+
"""Get Tajaka analysis for Varshaphal (annual chart): Muntha, Munthesh, and Tajaka Yogas."""
|
|
741
|
+
birth_dt = parse_birth_datetime(payload["date_of_birth"], payload["time_of_birth"])
|
|
742
|
+
lat, lon, birth_tz = resolve_location(
|
|
743
|
+
city=payload.get("city", ""),
|
|
744
|
+
latitude=payload.get("latitude"),
|
|
745
|
+
longitude=payload.get("longitude"),
|
|
746
|
+
timezone=payload.get("timezone"),
|
|
747
|
+
)
|
|
748
|
+
year_of_life = int(payload["year_of_life"])
|
|
749
|
+
query_lat = float(payload.get("query_latitude") or lat)
|
|
750
|
+
query_lon = float(payload.get("query_longitude") or lon)
|
|
751
|
+
query_tz = payload.get("query_timezone") or birth_tz
|
|
752
|
+
# get_varshaphal expects naive birth_dt
|
|
753
|
+
birth_chart = ephemeris_service.calculate_chart(
|
|
754
|
+
"Chart", birth_dt, lat, lon, birth_tz
|
|
755
|
+
)
|
|
756
|
+
varsha = get_varshaphal(
|
|
757
|
+
birth_dt, lat, lon, birth_tz, year_of_life, query_lat, query_lon, query_tz
|
|
758
|
+
)
|
|
759
|
+
varsha_chart = varsha.get("varshaphal_chart", {})
|
|
760
|
+
return get_tajaka_analysis(birth_chart, varsha_chart, year_of_life)
|
|
761
|
+
|
|
762
|
+
|
|
763
|
+
def get_nabhasha_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
764
|
+
"""Get Nabhasha Yogas: special yogas based on overall planetary distribution pattern."""
|
|
765
|
+
base = _require_base_chart(payload)
|
|
766
|
+
return get_nabhasha_yogas(base["planetary_positions"])
|
|
767
|
+
|
|
768
|
+
|
|
769
|
+
def get_arishta_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
770
|
+
"""Get Arishta Yogas: longevity assessment and affliction indicators."""
|
|
771
|
+
base = _require_base_chart(payload)
|
|
772
|
+
return get_arishta_yogas(base)
|
|
773
|
+
|
|
774
|
+
|
|
775
|
+
def get_lunar_return_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
776
|
+
"""Get Lunar Return (Chandraphal): monthly chart for when Moon returns to natal position."""
|
|
777
|
+
birth_dt = parse_birth_datetime(payload["date_of_birth"], payload["time_of_birth"])
|
|
778
|
+
lat, lon, birth_tz = resolve_location(
|
|
779
|
+
city=payload.get("city", ""),
|
|
780
|
+
latitude=payload.get("latitude"),
|
|
781
|
+
longitude=payload.get("longitude"),
|
|
782
|
+
timezone=payload.get("timezone"),
|
|
783
|
+
)
|
|
784
|
+
base = ephemeris_service.calculate_chart("Chart", birth_dt, lat, lon, birth_tz)
|
|
785
|
+
moon = next(p for p in base["planetary_positions"] if p["name"] == "Moon")
|
|
786
|
+
target_year = int(payload["target_year"])
|
|
787
|
+
target_month = int(payload["target_month"])
|
|
788
|
+
query_lat = float(payload.get("query_latitude") or lat)
|
|
789
|
+
query_lon = float(payload.get("query_longitude") or lon)
|
|
790
|
+
query_tz = payload.get("query_timezone") or birth_tz
|
|
791
|
+
# get_lunar_return expects naive birth_dt (it localizes internally)
|
|
792
|
+
return get_lunar_return(
|
|
793
|
+
{
|
|
794
|
+
"moon_sign": moon["rashi"],
|
|
795
|
+
"planetary_positions": base["planetary_positions"],
|
|
796
|
+
},
|
|
797
|
+
birth_dt,
|
|
798
|
+
target_year,
|
|
799
|
+
target_month,
|
|
800
|
+
query_lat,
|
|
801
|
+
query_lon,
|
|
802
|
+
query_tz,
|
|
803
|
+
)
|
|
804
|
+
|
|
805
|
+
|
|
806
|
+
def get_prasna_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
807
|
+
"""Get Prasna (Horary) chart for the moment a question is asked."""
|
|
808
|
+
import pytz
|
|
809
|
+
|
|
810
|
+
dt_text = (payload.get("datetime") or "").strip()
|
|
811
|
+
tz_name = payload.get("timezone") or "UTC"
|
|
812
|
+
lat = float(payload.get("latitude") or 28.6)
|
|
813
|
+
lon = float(payload.get("longitude") or 77.2)
|
|
814
|
+
question = payload.get("question") or ""
|
|
815
|
+
if dt_text:
|
|
816
|
+
dt = datetime.fromisoformat(dt_text.replace("Z", "+00:00"))
|
|
817
|
+
if dt.tzinfo is None:
|
|
818
|
+
dt = pytz.timezone(tz_name).localize(dt)
|
|
819
|
+
dt_utc = dt.astimezone(pytz.UTC)
|
|
820
|
+
else:
|
|
821
|
+
dt_utc = datetime.now(pytz.UTC)
|
|
822
|
+
return get_prasna_chart(dt_utc, lat, lon, question)
|
|
823
|
+
|
|
824
|
+
|
|
825
|
+
def _moon_nakshatra_from_payload(payload: dict[str, Any]) -> dict[str, Any]:
|
|
826
|
+
"""Helper: build a base chart and return Moon nakshatra + longitude + tz-aware birth_dt."""
|
|
827
|
+
birth_dt = parse_birth_datetime(payload["date_of_birth"], payload["time_of_birth"])
|
|
828
|
+
lat, lon, tz = resolve_location(
|
|
829
|
+
city=payload.get("city", ""),
|
|
830
|
+
latitude=payload.get("latitude"),
|
|
831
|
+
longitude=payload.get("longitude"),
|
|
832
|
+
timezone=payload.get("timezone"),
|
|
833
|
+
)
|
|
834
|
+
base = ephemeris_service.calculate_chart("Chart", birth_dt, lat, lon, tz)
|
|
835
|
+
moon = next(p for p in base["planetary_positions"] if p["name"] == "Moon")
|
|
836
|
+
import pytz
|
|
837
|
+
|
|
838
|
+
return {
|
|
839
|
+
"moon_nakshatra": moon["nakshatra"],
|
|
840
|
+
"moon_longitude": moon["longitude"],
|
|
841
|
+
"birth_aware": pytz.timezone(tz).localize(birth_dt),
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
|
|
845
|
+
def get_kalachakra_dasha_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
846
|
+
"""Get Kalachakra Dasha: 9-group 112-year sign-based cycle (one of the most complex dasha systems)."""
|
|
847
|
+
info = _moon_nakshatra_from_payload(payload)
|
|
848
|
+
return get_kalachakra_dasha(
|
|
849
|
+
info["birth_aware"], info["moon_longitude"], info["moon_nakshatra"]
|
|
850
|
+
)
|
|
851
|
+
|
|
852
|
+
|
|
853
|
+
def get_kurmachakra_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
854
|
+
"""Get Kurmachakra (Tortoise Wheel) directional analysis: best/avoid directions for travel, construction, marriage."""
|
|
855
|
+
info = _moon_nakshatra_from_payload(payload)
|
|
856
|
+
return get_kurmachakra(
|
|
857
|
+
birth_nakshatra=info["moon_nakshatra"],
|
|
858
|
+
current_moon_nakshatra=payload.get("current_moon_nakshatra"),
|
|
859
|
+
event_type=payload.get("event_type", "travel"),
|
|
860
|
+
)
|
|
861
|
+
|
|
862
|
+
|
|
863
|
+
def get_travel_direction_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
864
|
+
"""Score a specific intended travel direction for auspiciousness using Kurmachakra rules."""
|
|
865
|
+
info = _moon_nakshatra_from_payload(payload)
|
|
866
|
+
return get_travel_direction_score(
|
|
867
|
+
birth_nakshatra=info["moon_nakshatra"],
|
|
868
|
+
intended_direction=payload.get("intended_direction", "North"),
|
|
869
|
+
current_moon_nakshatra=payload.get("current_moon_nakshatra"),
|
|
870
|
+
weekday=payload.get("weekday"),
|
|
871
|
+
)
|
|
872
|
+
|
|
873
|
+
|
|
874
|
+
def get_full_kurmachakra_chart_tool(payload: dict[str, Any]) -> dict[str, Any]:
|
|
875
|
+
"""Return the full Kurmachakra chart: all 9 directions + their nakshatras + weekday-avoid rules."""
|
|
876
|
+
return get_full_kurmachakra_chart()
|
|
877
|
+
|
|
878
|
+
|
|
879
|
+
TOOLS = {
|
|
880
|
+
# Core chart tools
|
|
881
|
+
"generate_kundali": generate_kundali,
|
|
882
|
+
"list_available_chart_types": list_available_chart_types,
|
|
883
|
+
"list_ayanamsa_modes": list_ayanamsa_modes_tool,
|
|
884
|
+
"list_house_systems": list_house_systems_tool,
|
|
885
|
+
"search_birth_places": search_birth_places,
|
|
886
|
+
"get_timezone_for_coordinates": get_timezone_for_coordinates,
|
|
887
|
+
"get_transit_positions": get_transit_positions,
|
|
888
|
+
# vedicastro / KP tools
|
|
889
|
+
"get_extended_chart": get_extended_chart,
|
|
890
|
+
"get_vimshottari_dasha": get_vimshottari_dasha,
|
|
891
|
+
"get_planetary_aspects": get_planetary_aspects_tool,
|
|
892
|
+
"get_significators": get_significators,
|
|
893
|
+
"list_extended_options": list_extended_options,
|
|
894
|
+
# New comprehensive tools
|
|
895
|
+
"get_panchanga": get_panchanga_tool,
|
|
896
|
+
"get_doshas": get_doshas_tool,
|
|
897
|
+
"get_yogas": get_yogas_tool,
|
|
898
|
+
"get_compatibility": get_compatibility_tool,
|
|
899
|
+
"get_special_conditions": get_special_conditions_tool,
|
|
900
|
+
"get_pratyantar_dasha": get_pratyantar_dasha_tool,
|
|
901
|
+
"get_yogini_dasha": get_yogini_dasha_tool,
|
|
902
|
+
"get_varshaphal": get_varshaphal_tool,
|
|
903
|
+
"get_ashtakavarga": get_ashtakavarga_tool,
|
|
904
|
+
"get_shadbala": get_shadbala_tool,
|
|
905
|
+
"get_jaimini": get_jaimini_tool,
|
|
906
|
+
"get_muhurta": get_muhurta_tool,
|
|
907
|
+
"get_best_muhurta": get_best_muhurta_tool,
|
|
908
|
+
# Batch 2 — new comprehensive tools
|
|
909
|
+
"get_avasthas": get_avasthas_tool,
|
|
910
|
+
"get_upagrahas": get_upagrahas_tool,
|
|
911
|
+
"get_gandanta": get_gandanta_tool,
|
|
912
|
+
"get_kartari": get_kartari_tool,
|
|
913
|
+
"get_nakshatra_analysis": get_nakshatra_tool,
|
|
914
|
+
"get_gochara": get_gochara_tool,
|
|
915
|
+
"get_bhava_chalit": get_bhava_chalit_tool,
|
|
916
|
+
"get_hora": get_hora_tool,
|
|
917
|
+
"get_sudarshana_chakra": get_sudarshana_tool,
|
|
918
|
+
"get_ashtottari_dasha": get_ashtottari_tool,
|
|
919
|
+
"get_sookshma_dasha": get_sookshma_tool,
|
|
920
|
+
"get_prana_dasha": get_prana_tool,
|
|
921
|
+
"get_char_dasha": get_char_dasha_tool,
|
|
922
|
+
"get_narayana_dasha": get_narayana_tool,
|
|
923
|
+
"get_tajaka": get_tajaka_tool,
|
|
924
|
+
"get_nabhasha_yogas": get_nabhasha_tool,
|
|
925
|
+
"get_arishta": get_arishta_tool,
|
|
926
|
+
"get_lunar_return": get_lunar_return_tool,
|
|
927
|
+
"get_prasna": get_prasna_tool,
|
|
928
|
+
# Kalachakra + Kurmachakra tools (final additions)
|
|
929
|
+
"get_kalachakra_dasha": get_kalachakra_dasha_tool,
|
|
930
|
+
"get_kurmachakra": get_kurmachakra_tool,
|
|
931
|
+
"get_travel_direction_score": get_travel_direction_tool,
|
|
932
|
+
"get_full_kurmachakra_chart": get_full_kurmachakra_chart_tool,
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
|
|
936
|
+
def main() -> int:
|
|
937
|
+
if len(sys.argv) != 2 or sys.argv[1] not in TOOLS:
|
|
938
|
+
print("Usage: kundali_bridge.py <tool-name>", file=sys.stderr)
|
|
939
|
+
return 2
|
|
940
|
+
|
|
941
|
+
try:
|
|
942
|
+
payload = json.loads(sys.stdin.read() or "{}")
|
|
943
|
+
result = TOOLS[sys.argv[1]](payload)
|
|
944
|
+
print(json.dumps(result, ensure_ascii=False))
|
|
945
|
+
return 0
|
|
946
|
+
except Exception as exc:
|
|
947
|
+
print(str(exc), file=sys.stderr)
|
|
948
|
+
return 1
|
|
949
|
+
|
|
950
|
+
|
|
951
|
+
if __name__ == "__main__":
|
|
952
|
+
raise SystemExit(main())
|