# Project export: Astro-LLM — The LLM Almanac (大模型黄历)

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: UC Berkeley AI Hackathon 2026
- Tagline: The LLM almanac: which model is sharp today, which one shares your values, and whether to spend or save your tokens — a real eval harness in astrology's clothing.
- Devpost: https://devpost.com/software/astro-llm-the-llm-almanac
- GitHub: https://github.com/EthanPany/astro-llm
- Demo: https://2-actual-hack.vercel.app/
- Team: 0 GitHub contributor(s) — 

## Devpost submission (written by the team)

### Inspiration

Coding plans like Claude Code and Codex feel slightly different day to day — some days the same model thinks longer, some days it's sharper, some days worse. And which model "gets" your taste is deeply personal. We wanted one place that turns both into a daily reading you'd actually open — like an old almanac (黄历) that tells you what's auspicious today.

### What it does

A single dashboard with four organs hanging off one personal eval harness: 运势 — Capability fortune. 18 deterministic, auto-graded tasks run against every model → a daily IQ, latency, cost, and an 8-day trend. It tells you whether today is a "spend tokens" or "save tokens" day. 缘分 — Value match. A short questionnaire of subjective dilemmas (rank car brands, recline the seat?, tabs vs spaces…). We force a real verdict out of each model, then match them to you — revealing your "AI soulmate" on a radar chart. Astrology skin. Each model is charted from its release date + provider HQ; you enter your birthday. Fun on the surface, honest underneath. 调和 — Harmonize loop. Inject your taste into the least-aligned model, then re-score on held-out dilemmas it never saw — a gain means generalization, not memorization (the guardrail against a sycophancy machine). Plus an MCP server so any agent can score itself, 5 completely different themed layouts, and 12 languages.

### How we built it

Backend: Python + FastAPI, async httpx to Claude / GPT / DeepSeek. A deterministic astrology engine, footrule + agreement matching, an autograder, and an MCP server. 40 passing tests. Frontend: React + Vite + TypeScript. Five bespoke layouts (not recolors): a woodblock almanac, a celestial natal-chart oracle, a brutalist court docket, a zen-ink scroll, and a modern glass dashboard. Real provider brand SVGs, Recharts, Framer Motion. Deploy: Vercel (FastAPI serverless + static SPA), runs keyless from committed seed data. Built start-to-finish with Claude Code.

### Challenges we ran into

Making models commit — they're tuned to hedge, so we force strict JSON verdicts with no abstaining. Separating honest measurement from noise (a held-out set keeps "alignment" from becoming flattery). Building five real layouts rather than five palettes. And provider quirks: Codex needs the Responses API, Opus rejects temperature, GPT-5 and DeepSeek-v4 are reasoners.

### Accomplishments we're proud of

Five genuinely distinct, animated UIs; a real personal eval harness with a held-out honesty guardrail; an MCP surface; 12-language support; and a keyless public deploy judges can click.

### What we learned

Models hedge unless you constrain them; small daily benchmarks are mostly noise unless you're honest about it; and a fun skin can carry a serious idea — model value-alignment — further than a dry chart.

### What's next

Crowdsource the daily probe so nobody pays the full token cost, hunt for real weekly periodicity near quota resets, add Gemini / Qwen / Grok live, and expand the harmonize loop.

## README (from the GitHub repository)

# 大模型黄历 · Astro-LLM

> **The LLM almanac.** One dashboard that tells you, every day, **(1)** which model is
> *sharp today* and whether you should spend or save your tokens, and **(2)** which
> model actually *shares your values* — all wrapped in an astrology / weather-report
> skin that's fun on the surface and a real personal eval harness underneath.

It looks like a fortune-telling almanac. It's actually a benchmark.

**▶ Live demo:** https://2-actual-hack.vercel.app · **Repo:** https://github.com/EthanPany/astro-llm

![Astro-LLM showcase](docs/demo.gif)

> Five completely different themed layouts (almanac · oracle · court · ink · aurora), a step-by-step value-match quiz, and a live model-fortune board. Full walkthrough: [`docs/showcase.mp4`](docs/showcase.mp4).

---

## Why

Coding plans (Claude Code, Codex, …) feel slightly different day to day — sometimes
the same model thinks longer, sometimes it's sharper, sometimes worse. And which model
"gets" your taste is deeply personal. Astro-LLM turns both into a daily reading:

| Organ | Astrology name | What it really measures |
|------|----------------|--------------------------|
| **Capability probe** | 运势 *fortune* | Is the model sharp today? — 18 deterministic, auto-graded tasks → an IQ, latency, cost, refusal rate, trend. |
| **Value matcher** | 缘分 *affinity* | Does it judge like *you*? — a bank of subjective dilemmas; we measure agreement (footrule + verdict match). |
| **MCP server** | — | How an agent *runs* the test on itself. |
| **Harmonize loop** | 调和 *harmony* | Can we nudge a model toward you? — inject your taste, re-test on **held-out** dilemmas. |

The "luck" is the skin; the numbers underneath are real. See the honesty note at the
bottom of the dashboard.

---

## Quickstart (local)

Requirements: Python 3.11+, [uv](https://docs.astral.sh/uv/), Node 20+.

```bash
cp .env.example .env        # paste your API keys (all optional — see below)
make install                # backend venv (uv) + frontend deps
make seed                   # probe live models, write committed seed JSON  (optional)
make dev-backend            # API on http://localhost:8000   (terminal 1)
make dev-frontend           # UI  on http://localhost:5173   (terminal 2)
```

Open **http://localhost:5173**.

**No keys?** It still works. The repo ships committed seed data, so the dashboard,
verdict grid and value-match all render offline. Keys unlock live re-probes
(`↻ 实时巡检`), the live custom-dilemma panel, and the harmonize loop.

### Providers
`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `DEEPSEEK_API_KEY` are wired and verified.
`GEMINI_API_KEY` / `QWEN_API_KEY` are implemented; without them those models show
clearly-labelled simulated data. Adding a key flips them to live on the next `make seed`.

---

## Deploy (one command)

```bash
make up           # docker compose: builds the frontend, serves SPA + API on :8000
# or:
docker build -t astro-llm . && docker run -p 8000:8000 --env-file .env astro-llm
```

In production FastAPI serves the built SPA and the API from the **same origin**, so
there's nothing else to configure. The image bakes in the seed data, so it renders
even with zero keys.

---

## Architecture

```
backend/   FastAPI + httpx + pydantic + SQLite     (uv-managed)
  app/
    providers/        one adapter per provider (OpenAI/Anthropic/DeepSeek/Gemini/Qwen)
    benchmarks/       dilemma bank, capability tasks, judge + capability runners,
                      matching (footrule + agreement), scoring (IQ + suggestion)
    astrology/        deterministic zodiac / numerology / fortune / compatibility
    loop/             harmonize (train/test guardrail)
    storage/          SQLite DAL + seed generator
    service.py        assembly: runs + astrology -> AlmanacCards + headline
    api/routes.py     REST API   |   mcp_server.py   MCP tools   |   scheduler.py
frontend/  React + Vite + TypeScript, handwritten CSS, GSAP, hand-drawn SVG charts
```

Full data contracts: [`ARCHITECTURE.md`](./ARCHITECTURE.md).

### Earlier prompt harness

`experiments/astro-twin-v1` preserves the earlier AstroTwin dual-pipeline
prompt-testing harness. It remains runnable as a standalone Python project and
records its exact source revision in `SOURCE_REPOSITORIES.md`.

### API
`GET /api/dashboard` · `GET /api/models` · `GET /api/dilemmas` ·
`POST /api/match` · `POST /api/harmonize` · `POST /api/judge` · `GET /api/health`

### MCP
```bash
make mcp        # python -m app.mcp_server  (stdio)
```
Tools: `list_models`, `get_almanac`, `judge_dilemma`, `match_me`,
`score_model_against_user`. Point any MCP client (e.g. Claude Desktop) at it and an
agent can score itself against the benchmark.

### Hourly almanac
Set `ENABLE_SCHEDULER=true` to probe available models on an interval (`PROBE_INTERVAL_MINUTES`),
persisting real runs to SQLite so the trend becomes measured data over time.

---

## Tests
```bash
make test       # 34 tests: astrology, matching, scoring, graders, judge parsing,
                # harmonize, API (offline via TestClient + monkeypatched seed)
```

## Design
Handwritten CSS only — no Tailwind, no component library, no chart library. Charts are
hand-drawn SVG. Type: Fraunces (display) + Space Grotesk (UI) + Space Mono (numbers).
GSAP drives entrance reveals, number count-ups, and chart draw-on.

---

## Honesty note
The capability score is a real stopwatch + autograder. The value-match measures how a
model was *tuned to present itself*, not a hidden soul. The harmonize loop is scored on
**held-out** dilemmas the model never saw during tuning — the guardrail that keeps
"alignment" from collapsing into a flattery machine. The astrology is for fun; we say so.

> ⚠ Rotate any API keys that were shared in plaintext before making this repo public.


## Detected evidence (automated analysis)

Indexed codebase: 110 recognized source files, 304 KB.
- CSS (language) — detected in the code
- FastAPI (technology) — detected in the code
- HTML (language) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- TypeScript (language) — detected in the code
- Anthropic (technology) — claimed on Devpost, not found in the code
- OpenAI (technology) — claimed on Devpost, not found in the code
- Vercel (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (120 of 136)

```
.dockerignore
.env.example
.gitignore
.vercelignore
api/index.py
ARCHITECTURE.md
backend/app/__init__.py
backend/app/api/__init__.py
backend/app/api/routes.py
backend/app/astrology/__init__.py
backend/app/astrology/copy.py
backend/app/astrology/engine.py
backend/app/benchmarks/__init__.py
backend/app/benchmarks/capability_tasks.py
backend/app/benchmarks/capability.py
backend/app/benchmarks/dilemmas.py
backend/app/benchmarks/judge.py
backend/app/benchmarks/matching.py
backend/app/benchmarks/scoring.py
backend/app/config.py
backend/app/loop/__init__.py
backend/app/loop/harmonize.py
backend/app/main.py
backend/app/match_service.py
backend/app/mcp_server.py
backend/app/models_registry.py
backend/app/providers/__init__.py
backend/app/providers/anthropic_provider.py
backend/app/providers/base.py
backend/app/providers/deepseek_provider.py
backend/app/providers/gemini_provider.py
backend/app/providers/openai_provider.py
backend/app/providers/qwen_provider.py
backend/app/providers/registry.py
backend/app/scheduler.py
backend/app/schemas.py
backend/app/seed_loader.py
backend/app/service.py
backend/app/storage/__init__.py
backend/app/storage/db.py
backend/app/storage/seed.py
backend/data/seed/capability.json
backend/data/seed/dashboard.json
backend/data/seed/meta.json
backend/data/seed/models.json
backend/data/seed/verdicts.json
backend/pyproject.toml
backend/tests/test_api.py
backend/tests/test_astrology.py
backend/tests/test_harmonize.py
backend/tests/test_judge_parse.py
backend/tests/test_matching.py
backend/tests/test_review_fixes.py
backend/tests/test_scoring_and_grading.py
docker-compose.yml
Dockerfile
frontend/index.html
frontend/package.json
frontend/src/api.ts
frontend/src/App.css
frontend/src/App.tsx
frontend/src/components/AstrologyInput.css
frontend/src/components/AstrologyInput.tsx
frontend/src/components/BottomBar.css
frontend/src/components/BottomBar.tsx
frontend/src/components/CompareTable.css
frontend/src/components/CompareTable.tsx
frontend/src/components/Dashboard.css
frontend/src/components/Dashboard.tsx
frontend/src/components/DetailsModal.tsx
frontend/src/components/HarmonizePanel.css
frontend/src/components/HarmonizePanel.tsx
frontend/src/components/HeroPanel.css
frontend/src/components/HeroPanel.tsx
frontend/src/components/MatchOverlay.css
frontend/src/components/MatchOverlay.tsx
frontend/src/components/McpModal.css
frontend/src/components/McpModal.tsx
frontend/src/components/ModelCard.css
frontend/src/components/ModelCard.tsx
frontend/src/components/Reveal.tsx
frontend/src/components/TopBar.css
frontend/src/components/TopBar.tsx
frontend/src/components/TrendChart.css
frontend/src/components/TrendChart.tsx
frontend/src/components/TrendPanel.css
frontend/src/components/TrendPanel.tsx
frontend/src/components/ui/AnimatedBar.tsx
frontend/src/components/ui/AnimatedNumber.tsx
frontend/src/components/ui/Background.css
frontend/src/components/ui/Background.tsx
frontend/src/components/ui/Dropdown.css
frontend/src/components/ui/Dropdown.tsx
frontend/src/components/ui/Modal.css
frontend/src/components/ui/Modal.tsx
frontend/src/components/ui/ProviderIcon.tsx
frontend/src/components/ui/TiltCard.css
frontend/src/components/ui/TiltCard.tsx
frontend/src/i18n/I18nProvider.tsx
frontend/src/i18n/locales/ar.json
frontend/src/i18n/locales/de.json
frontend/src/i18n/locales/es.json
frontend/src/i18n/locales/fr.json
frontend/src/i18n/locales/hi.json
frontend/src/i18n/locales/ja.json
frontend/src/i18n/locales/ko.json
frontend/src/i18n/locales/pt.json
frontend/src/i18n/locales/ru.json
frontend/src/i18n/locales/zh-TW.json
frontend/src/i18n/strings.ts
frontend/src/layouts/AlmanacLayout.css
frontend/src/layouts/AlmanacLayout.tsx
frontend/src/layouts/CourtLayout.css
frontend/src/layouts/CourtLayout.tsx
frontend/src/layouts/index.ts
frontend/src/layouts/InkLayout.css
frontend/src/layouts/InkLayout.tsx
frontend/src/layouts/OracleLayout.css
frontend/src/layouts/OracleLayout.tsx
frontend/src/layouts/shared.ts
[16 more files omitted for size]
```

### Dependencies

- backend/pyproject.toml: apscheduler@>=3.10, fastapi@>=0.115, httpx@>=0.27, mcp@>=1.2, pydantic@>=2.9, pydantic-settings@>=2.6, pytest@>=8.3, pytest-asyncio@>=0.24, python-dotenv@>=1.0, ruff@>=0.7, uvicorn[standard]@>=0.32
- frontend/package.json: @types/react@^18.3.12, @types/react-dom@^18.3.1, @vitejs/plugin-react@^4.3.4, framer-motion@^12.40.0, gsap@^3.12.5, lucide-react@^1.21.0, react@^18.3.1, react-dom@^18.3.1, react-icons@^5.6.0, recharts@^3.8.1, typescript@^5.6.3, vite@^5.4.11
- requirements.txt: apscheduler@>=3.10, fastapi@>=0.115, httpx@>=0.27, pydantic@>=2.9, pydantic-settings@>=2.6, python-dotenv@>=1.0

### Recent commits (newest first)

- default theme: Classic Almanac (通勝)
- docs: Devpost submission writeup + showcase video/gif in README
- deploy: Vercel config (FastAPI serverless + static SPA, keyless seed) — live
- frontend: full English in English mode, step-by-step quiz wizard, remove refresh btn + Vercel config
- themes are now distinct LAYOUTS (per spec), trimmed to 5
- redesign foundation: real provider icons, premium primitives, themes, i18n, overlays
- review fixes (30 findings) + live custom-dilemma panel
- fix: Docker build (install deps directly, not editable project) + summary split
- deploy: Dockerfile (multi-stage), docker-compose, Makefile, README
- frontend + live data: full single-page dashboard, provider fixes, real seed
- backend: full engine — astrology, benchmarks, matching, API, MCP, scheduler, tests
- scaffold: project structure, contracts, config, provider layer

## Key source files (fetched from GitHub, selected and truncated for size)

### SUBMISSION.md

```markdown
# Devpost submission — Astro-LLM (大模型黄历)

Paste these into the Devpost form for **UC Berkeley AI Hackathon 2026**.
Repo: https://github.com/EthanPany/astro-llm · Live: https://2-actual-hack.vercel.app

---

## Project name
Astro-LLM · 大模型黄历 (The LLM Almanac)

## Elevator pitch (≤200 chars)
The LLM almanac: a daily reading of which model is sharp today, which one shares your values, and whether to spend or save your tokens — a real eval harness in astrology's clothing.

## Tracks
- **Primary: Ddoski's Toolbox** (a tool that helps devs/creators decide *which model to trust today* and whether to spend their weekly token budget).
- Also a strong fit for **Ddoski's Playground** (the astrology skin / generative fun) and **Best UI/UX** (5 fully distinct themed interfaces).
- Sponsor: **Anthropic** & **The Token Company** — built end-to-end with Claude Code; it benchmarks model behavior and value-alignment.

## Built with
python · fastapi · react · typescript · vite · recharts · framer-motion · gsap ·
mcp · anthropic-claude · openai · deepseek · vercel · claude-code

---

## Inspiration
Coding plans like Claude Code and Codex feel slightly different day to day — some days the same model thinks longer, some days it's sharper, some days worse. And *which* model "gets" your taste is deeply personal. We wanted one place that turns both into a daily reading you'd actually open — like an old almanac (黄历) that tells you what's auspicious today.

## What it does
A single dashboard with four organs hanging off one **personal eval harness**:
- **运势 — Capability fortune.** 18 deterministic, auto-graded tasks run against every model → a daily IQ, latency, cost, and an 8-day trend. Tells you if today is a "spend tokens" or "save tokens" day.
- **缘分 — Value match.** A short questionnaire of subjective dilemmas (rank car brands, recline the seat?, tabs vs spaces…). We force a real verdict out of each model, then match them to *you* — revealing your "AI soulmate" on a radar chart.
- **Astrology skin.** Each model is charted from its release date + provider HQ; you enter your birthday. Fun on the surface, honest underneath (we say so).
- **调和 — Harmonize loop.** Inject your taste into the least-aligned model, then re-score on **held-out** dilemmas it never saw — a gain means generalization, not memorization (the guardrail against a sycophancy machine).
- Plus an **MCP server** so any agent can score itself against the benchmark, **5 completely different themed layouts**, and **12 languages**.

## How we built it
- **Backend:** Python + FastAPI, async httpx to Claude / GPT / DeepSeek (Gemini/Qwen ready). A deterministic astrology engine, footrule + agreement matching, an autograder, and an MCP server. 40 passing tests.
- **Frontend:** React + Vite + TypeScript. Five *bespoke* layouts (not recolors): a woodblock almanac, a celestial natal-chart oracle, a brutalist court docket, a zen-ink scroll, and a modern glass dashboard — each its own components/CSS. Real provider bran
[truncated — 2311 more characters]
```

### ARCHITECTURE.md

```markdown
# Astro-LLM — Architecture & Contracts

> **The LLM 老黄历 (almanac) for the AI age.** One dashboard that tells you, every
> day, (1) which model is *sharp* today and whether you should spend or save your
> tokens, and (2) which model actually *shares your values* — wrapped in an
> astrology/weather-report skin that is fun on the surface and a real personal
> eval harness underneath.

This file is the single source of truth for data shapes and module
responsibilities. Every backend module and frontend component builds against the
contracts below. **Do not change a contract without updating this file.**

---

## 1. The spine (one product, four organs)

All four features hang off a single **personal eval harness**:

| Organ | Astrology name | What it measures | Honest framing |
|-------|---------------|------------------|----------------|
| Capability probe | 运势 (fortune) | Is the model sharp *today*? (graded mini-benchmark: latency, pass-rate, tokens) | A stopwatch + autograder. Real, measured. |
| Value matcher | 缘分 (affinity) | Does it judge like *you*? (Spearman/agreement over a dilemma bank) | Measures how a model was *tuned to present itself*, not an inner soul. |
| MCP server | — | How an agent *runs* the test | Standard MCP tools. |
| Harmonize loop | 调和 (harmony) | Can we nudge a model toward you? (inject values → rerun on **holdout**) | Holdout split is the guardrail against a sycophancy machine. |

The astrology layer is a deterministic, date-seeded *skin*. We are explicit that
the "luck" is for fun; the benchmark numbers underneath are real.

---

## 2. Tech stack

- **Backend:** Python 3.13, FastAPI + uvicorn, httpx (async), pydantic v2,
  APScheduler, SQLite (stdlib `sqlite3`). Managed with **uv + venv**. Tests: pytest.
- **MCP:** official `mcp` Python SDK (stdio transport).
- **Frontend:** React 18 + Vite + TypeScript. **Handwritten CSS only** — no Tailwind,
  no component libraries, no pre-built UI kits. **GSAP** for animation (the one
  allowed lib). Charts are **handwritten SVG**. Font: "Space Grotesk" (display) +
  "Fraunces" (serif accents) + "Geist Mono"/"Space Mono" (numbers), self-hosted.
- **Deploy:** Dockerfile (multi-stage: build frontend → serve static from FastAPI),
  docker-compose, Makefile. One command: `docker compose up`.

---

## 3. Providers

`backend/app/providers/` — each provider implements the `Provider` protocol in
`base.py`. A `ChatResult` is returned for every call.

```
ChatResult:
  text: str                 # assistant content (reasoning stripped)
  latency_ms: int
  prompt_tokens: int
  completion_tokens: int
  reasoning_tokens: int     # 0 if N/A
  cost_usd: float           # computed from registry pricing
  ok: bool
  error: str | None
  raw_finish: str | None
```

Provider quirks (verified live 2026-06-21):
- **openai_provider**: chat/completions. For `gpt-5*`/`o*` models use
  `max_completion_tokens` and OMIT temperature (reasoning models only accept
  default). For `gpt-4*` use `max_tokens` + temperat
[truncated — 9841 more characters]
```

### requirements.txt

```
fastapi>=0.115
httpx>=0.27
pydantic>=2.9
pydantic-settings>=2.6
python-dotenv>=1.0
apscheduler>=3.10

```

### docker-compose.yml

```yaml
services:
  app:
    build: .
    image: astro-llm:latest
    ports:
      - "8000:8000"
    # API keys are optional — without them the committed seed data is served.
    # Drop a .env next to this file (see .env.example) to enable live runs.
    env_file:
      - path: .env
        required: false
    environment:
      # let the container fall back gracefully if .env is absent
      ENABLE_SCHEDULER: "false"
    volumes:
      # persist the SQLite DB of live runs across restarts
      - astro_data:/app/backend/data
    restart: unless-stopped

volumes:
  astro_data:

```

### Dockerfile

```
# ---- stage 1: build the frontend ------------------------------------------
FROM node:22-slim AS frontend
WORKDIR /build
COPY frontend/package.json frontend/package-lock.json* ./
RUN npm install
COPY frontend/ ./
# same-origin /api in prod, so no VITE_API_BASE needed
RUN npm run build

# ---- stage 2: python backend serving the built SPA ------------------------
FROM python:3.13-slim AS app
WORKDIR /app

# uv for fast, reproducible installs
RUN pip install --no-cache-dir uv

# install runtime deps as a cached layer (the app runs as a local package from
# the workdir, so we never need to build/install the project itself)
RUN uv pip install --system \
    "fastapi>=0.115" "uvicorn[standard]>=0.32" "httpx>=0.27" "pydantic>=2.9" \
    "pydantic-settings>=2.6" "python-dotenv>=1.0" "apscheduler>=3.10" "mcp>=1.2"

# app code + committed seed data
COPY backend/ /app/backend/
# built frontend where main.py expects it (../frontend/dist relative to backend)
COPY --from=frontend /build/dist /app/frontend/dist

WORKDIR /app/backend
EXPOSE 8000
ENV ENABLE_SCHEDULER=false
CMD ["python", "-m", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]

```

### frontend/package.json

```
{
  "name": "astro-llm-frontend",
  "private": true,
  "version": "0.1.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc -b && vite build",
    "preview": "vite preview",
    "typecheck": "tsc -b --noEmit"
  },
  "dependencies": {
    "framer-motion": "^12.40.0",
    "gsap": "^3.12.5",
    "lucide-react": "^1.21.0",
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "react-icons": "^5.6.0",
    "recharts": "^3.8.1"
  },
  "devDependencies": {
    "@types/react": "^18.3.12",
    "@types/react-dom": "^18.3.1",
    "@vitejs/plugin-react": "^4.3.4",
    "typescript": "^5.6.3",
    "vite": "^5.4.11"
  }
}

```

### backend/pyproject.toml

```
[project]
name = "astro-llm-backend"
version = "0.1.0"
description = "Astro-LLM — the LLM almanac: capability fortune + value affinity, with an astrology skin."
requires-python = ">=3.11"
dependencies = [
    "fastapi>=0.115",
    "uvicorn[standard]>=0.32",
    "httpx>=0.27",
    "pydantic>=2.9",
    "pydantic-settings>=2.6",
    "python-dotenv>=1.0",
    "apscheduler>=3.10",
    "mcp>=1.2",
]

[build-system]
requires = ["setuptools>=61"]
build-backend = "setuptools.build_meta"

[project.optional-dependencies]
dev = [
    "pytest>=8.3",
    "pytest-asyncio>=0.24",
    "ruff>=0.7",
]

[tool.pytest.ini_options]
testpaths = ["tests"]
asyncio_mode = "auto"
pythonpath = ["."]

[tool.ruff]
line-length = 100
target-version = "py311"

[tool.setuptools]
packages = ["app"]

```

### api/index.py

```python
"""Vercel serverless entrypoint — exposes the FastAPI app as an ASGI function.

Runs keyless on Vercel: serves the committed seed data (dashboard, dilemmas,
match) with no API keys. Add ANTHROPIC/OPENAI/DEEPSEEK keys as Vercel env vars to
enable the live features (harmonize, custom-dilemma judge, ?live=1).
"""
import os
import sys

# make the backend package importable
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "backend"))
# serverless fs is read-only except /tmp
os.environ.setdefault("DATABASE_URL", "sqlite:////tmp/astro_llm.db")
os.environ.setdefault("ENABLE_SCHEDULER", "false")

from app.main import app  # noqa: E402

# Vercel's @vercel/python runtime serves the module-level ASGI `app`.

```

### frontend/src/main.tsx

```typescript
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App";
import { ThemeProvider } from "./theme/ThemeProvider";
import { I18nProvider } from "./i18n/I18nProvider";
import "./styles/global.css";

createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <ThemeProvider>
      <I18nProvider>
        <App />
      </I18nProvider>
    </ThemeProvider>
  </StrictMode>,
);

```

### backend/app/main.py

```python
"""FastAPI entrypoint. Serves the API and (in production) the built frontend."""
from __future__ import annotations

import contextlib

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles

from .api.routes import router
from .config import BACKEND_DIR, settings
from .storage.db import init_db

FRONTEND_DIST = BACKEND_DIR.parent / "frontend" / "dist"


@contextlib.asynccontextmanager
async def lifespan(app: FastAPI):
    init_db()
    scheduler = None
    if settings.enable_scheduler:
        from .scheduler import start_scheduler

        scheduler = start_scheduler()
    yield
    if scheduler:
        scheduler.shutdown(wait=False)


app = FastAPI(title="Astro-LLM", version="0.1.0", lifespan=lifespan)

app.add_middleware(
    CORSMiddleware,
    allow_origins=settings.cors_list,
    allow_origin_regex=r"https?://localhost(:\d+)?",
    allow_methods=["*"],
    allow_headers=["*"],
)

app.include_router(router)


# ---- serve the built SPA in production (if frontend/dist exists) -------------
if FRONTEND_DIST.exists():
    app.mount("/assets", StaticFiles(directory=FRONTEND_DIST / "assets"), name="assets")

    @app.get("/")
    async def spa_root():
        return FileResponse(FRONTEND_DIST / "index.html")

    @app.get("/{full_path:path}")
    async def spa_catch_all(full_path: str):
        candidate = FRONTEND_DIST / full_path
        if candidate.is_file():
            return FileResponse(candidate)
        return FileResponse(FRONTEND_DIST / "index.html")
else:

    @app.get("/")
    async def root():
        return {
            "name": "Astro-LLM API",
            "docs": "/docs",
            "hint": "Build the frontend (make build) to serve the dashboard here.",
        }

```

[103 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]