← Builds
LIVE

Kundali — Grounded Vedic Readings from a Computed Chart

A multi-account Jyotish app that computes a real sidereal birth chart with Swiss Ephemeris, then has an LLM read that exact chart — specific placements, not generic horoscope filler — with a model-comparison harness and an LLM judge to keep it honest.

Problem

AI horoscope apps hallucinate cheerful, generic text untethered from any actual chart, so a "reading" says nothing a fortune cookie couldn't. Real Jyotish is the opposite — it depends on precise sidereal astronomy (ayanamsa, the ascendant to the degree, whole-sign houses, Vimshottari dashas) that a language model cannot do reliably in its head. The value is in grounding an interpretation in a correctly computed chart; that is exactly the part the model is worst at.

Constraint

The astronomy has to be exact where it is checkable — a wrong ascendant silently invalidates every downstream claim about career, marriage and timing — while the reading itself is irreducibly interpretive. On top of that, the production engine is a free, rate-limited, open reasoning model (NVIDIA Nemotron 550B) whose chain-of-thought leaks into the answer and whose length balloons — so a chunk of the work is reliability engineering around a flaky free model, not prompting.

Architecture decision

Split the work by what each side is actually good at. Deterministic Python (Swiss Ephemeris, Lahiri ayanamsa) owns the entire chart — positions, houses, dashas — locked by a reference-birth test suite so the astronomy can never quietly drift; the LLM is scoped to interpretation only and is handed the computed chart as ground truth inside a cached system block. No fine-tuning, no vector DB, no agent framework. Reasoning is excluded at the OpenRouter API layer (and defensively stripped) so chain-of-thought never reaches the reader, and an LLM-as-judge scores rival readings against the same chart on groundedness. One Docker image serves the built React SPA and the FastAPI backend, over a single store abstraction that runs SQLite locally and Postgres (Neon) in production.

Notes

The same two-tier split as the invoice agent, in a softer domain: deterministic where it’s checkable, LLM where it’s genuinely interpretive. A birth chart is a math object — given date, time and place, the ascendant and every graha position are exact — so that entire computation is Swiss Ephemeris behind a test that pins a reference birth. The model never computes; it only reads what was computed, with the chart JSON injected as ground truth in the prompt.

The interesting engineering is around the free reasoning model. Left alone it dumped its private deliberation into the reading and ran on for thousands of words; the fix was to exclude reasoning at the API layer, refuse to ever surface thinking as an answer, and stream through a <think>-tag filter — turning a leaky 550B into a clean 900-word reading. Two honesty mechanisms sit on top: an LLM-as-judge that scores readings against the actual chart on groundedness, classical accuracy and specificity (admin-only), and contextual follow-up questions after every reply so the reading stays a conversation instead of a dead end.

Live demo

Open demo ↗https://kundali-rn54.onrender.com

Still unfinished

  • Licensing ship-blocker — Swiss Ephemeris (pyswisseph) is AGPL-or-commercial; a real public launch needs the license resolved (buy the Astrodienst license, publish the source under AGPL, or swap the ephemeris backend).
  • The free reading engine rate-limits — on an exhausted OpenRouter day, readings fail with a plain "free usage finished" message rather than silently falling back to a paid model (a deliberate call, but a real limit).
  • Charts with an unknown birth time fall back to noon, so the ascendant and every house placement become approximate — flagged honestly in the reading, but it weakens the career, marriage and property sections.
  • Accounts are email + password only — no password reset or email verification yet — and the Render free tier sleeps after ~15 min idle, so the first visit after a quiet spell takes 30–60s to wake.