# Project export: AiOn

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: TreeHacks 2026
- Tagline: Hi, I’m Smartyy. I built AiOn—an AI-native global intelligence layer unifying 30+ sectors , every country and delivering real-time, market-aware insights in every language of the world .
- Devpost: https://devpost.com/software/aion-n75vj3
- GitHub: https://github.com/shikhar0777/anion-rdy
- Demo: https://hackathon-deploy-tau.vercel.app/
- Video: https://www.youtube.com/embed/Y46hZFxaEHE?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 2 GitHub contributor(s) — Shikharpandey07 (1 commits), Shikhar Pandey (1 commits)

## Devpost submission (written by the team)

No Devpost description available.

## README (from the GitHub repository)

# AiON — AI-Powered Global News Discovery Platform with advance algo and latency

A real-time, multi-source news intelligence platform with AI-powered summaries, trending detection, story clustering, and deep analysis.

## Architecture

```
┌─────────────────────────────────────────────────────────────┐
│                    Next.js Frontend                         │
│  Country/Category filters │ Feed (Trending/Latest) │ Story  │
│  SSE real-time updates    │ Framer Motion animations        │
└─────────────┬───────────────────────────────┬───────────────┘
              │ REST API                      │ SSE Stream
┌─────────────▼───────────────────────────────▼───────────────┐
│                     FastAPI Backend                          │
│  Provider Router │ AI Router │ Feed Service │ SSE Endpoint   │
│  Circuit Breaker │ Cache (stale-while-revalidate)           │
└───────┬─────────┬──────────┬────────────────────────────────┘
        │         │          │
   ┌────▼──┐ ┌───▼───┐ ┌───▼────┐
   │NewsAPI│ │Guardian│ │ GDELT  │   ← News Providers (failover chain)
   └───────┘ └───────┘ └────────┘
        │         │          │
┌───────▼─────────▼──────────▼────────────────────────────────┐
│                   Background Worker                         │
│  Ingest Loop │ Clustering │ Trending Scores │ AI Enrichment │
└───────┬──────────┬──────────────────────────────────────────┘
        │          │
   ┌────▼──┐  ┌───▼─────┐
   │Postgres│  │  Redis   │   ← Storage + Cache + Pub/Sub
   └───────┘  └─────────┘
```

## Quick Start

### 1. Prerequisites

- Docker & Docker Compose
- Node.js 18+ (for frontend)
- Python 3.11+ (for running API/worker locally, or use Docker)

### 2. Clone & Configure

```bash
cp .env.example .env
# Edit .env with your API keys (all are optional — GDELT works without any keys)
```

### 3. Start Infrastructure

```bash
# Start Postgres + Redis
docker compose up postgres redis -d
```

### 4. Start Backend API

```bash
# Create virtual environment
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

# Start API server
uvicorn apps.api.main:app --host 0.0.0.0 --port 8000 --reload
```

### 5. Start Worker

```bash
# In a new terminal
source .venv/bin/activate
python -m apps.worker.main
```

### 6. Start Frontend

```bash
cd apps/web
npm install
npm run dev
# Open http://localhost:3000
```

### All-in-One Docker (API + Worker + Postgres + Redis)

```bash
docker compose up --build
# Then start frontend separately:
cd apps/web && npm install && npm run dev
```

## API Keys

| Provider | Key | Required? | Free Tier |
|----------|-----|-----------|-----------|
| GDELT | None needed | Built-in | Unlimited |
| NewsAPI | `NEWSAPI_KEY` | Optional | 100 req/day |
| Guardian | `GUARDIAN_KEY` | Optional | 500 req/day |
| OpenAI | `OPENAI_KEY` | Optional | Pay-as-you-go |
| Anthropic | `ANTHROPIC_KEY` | Optional | Pay-as-you-go |
| Perplexity | `PERPLEXITY_KEY` | Optional | Pay-as-you-go |
| Hygen | `PHYGEN_KEY` | Optional | Pay-as-you-go |


**The platform works with zero API keys** — GDELT provides free global news data, and AI features gracefully degrade to deterministic summaries.

## How Real-Time Works

1. **Worker** polls news providers every 2 minutes (configurable via `INGEST_INTERVAL_SECONDS`)
2. New articles are deduplicated by normalized title hash and stored in Postgres
3. Articles are clustered by title similarity (SequenceMatcher, threshold 0.75)
4. Trending scores are recomputed: `score = w1*log(1+sources) + w2*recency + w3*velocity`
5. Worker publishes update events to Redis pub/sub
6. **SSE endpoint** subscribes to Redis channels and pushes events to connected browsers
7. **Frontend** receives SSE events and refreshes the feed with smooth animations

```
Browser ←SSE← API ←pub/sub← Redis ←publish← Worker
                                               ↓
                                          Postgres (articles, clusters)
```

## How Failover Works

### News Provider Failover
- **Headlines chain**: NewsAPI → Guardian → GDELT
- **Trending chain**: GDELT → Guardian → NewsAPI
- Each provider has a **circuit breaker** (Redis-backed):
  - 3 consecutive failures → circuit opens for 60s
  - After cooldown → half-open state (1 trial request)
  - Success resets the circuit

### AI Provider Failover
- **Summarization**: OpenAI → Anthropic → deterministic fallback
- **Deep Explain**: Perplexity Sonar → OpenAI → Anthropic → snippet fallback
- Structured JSON output with retry on malformed response

### Cache Strategy
- **Stale-while-revalidate**: Fresh cache (2min TTL) + stale cache (10min TTL)
- On provider failure: serve stale data + show "Last updated Xs ago"
- **Single-flight refresh**: Only one worker refreshes a given cache key at a time

## Configuration

### Polling Intervals (`.env`)
```
INGEST_INTERVAL_SECONDS=120   # How often to fetch news
ENRICH_INTERVAL_SECONDS=30    # How often to run AI enrichment
TRENDING_INTERVAL_SECONDS=60  # How often to recompute scores
```

### Cache TTLs (`packages/shared/constants.py`)
```python
CACHE_TTL_FEED = 120      # Feed results: 2 min
CACHE_TTL_STORY = 300     # Story detail: 5 min
CACHE_TTL_CLUSTER = 180   # Cluster data: 3 min
CACHE_TTL_EXPLAIN = 600   # AI explanations: 10 min
```

### Trending Weights (`packages/shared/constants.py`)
```python
TRENDING_W_SOURCES = 3.0  # Weight for unique source count
TRENDING_W_RECENCY = 2.0  # Weight for recency boost
TRENDING_W_VELOCITY = 1.5 # Weight for velocity (articles/30min)
```

## API Endpoints

| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/meta/countries` | List available countries |
| GET | `/api/meta/categories` | List available categories |
| GET | `/api/feed?country=US&category=technology&mode=trending` | Get feed items |
| GET | `/api/story/{article_id}` | Get story intelligence |
| GET | `/api/cluster/{cluster_id}` | Get cluster detail |
| GET | `/api/explain?cluster_id=1` | Get AI deep explanation |
| GET | `/api/stream?country=US&category=general&mode=trending` | SSE stream |
| GET | `/health` | Health check |
| GET | `/health/providers` | Provider status + circuit breakers |

## Project Structure

```
aion/
├── apps/
│   ├── api/                    # FastAPI backend
│   │   ├── main.py             # App entry point + lifespan
│   │   ├── config.py           # Settings from env
│   │   ├── database.py         # SQLAlchemy models + engine
│   │   ├── redis_client.py     # Cache, pub/sub, circuit breaker
│   │   ├── providers/          # News data providers
│   │   │   ├── base.py         # Abstract provider interface
│   │   │   ├── newsapi.py      # NewsAPI implementation
│   │   │   ├── guardian.py     # Guardian API implementation
│   │   │   ├── gdelt.py        # GDELT implementation (free)
│   │   │   └── router.py       # Provider failover router
│   │   ├── ai/                 # AI layer
│   │   │   └── router.py       # AI model router + failover
│   │   ├── services/           # Business logic
│   │   │   ├── clustering.py   # Dedup + clustering
│   │   │   ├── trending.py     # Trending score computation
│   │   │   ├── feed.py         # Feed assembly
│   │   │   └── story.py        # Story intelligence
│   │   └── routes/             # API route handlers
│   │       ├── meta.py         # Countries + categories
│   │       ├── feed.py         # Feed endpoint
│   │       ├── story.py        # Story + explain endpoints
│   │       ├── stream.py       # SSE streaming
│   │       └── health.py       # Health checks
│   ├── worker/                 # Background worker
│   │   ├── main.py             # Worker entry + loops
│   │   └── tasks/
│   │       ├── ingest.py       # News ingestion task
│   │       └── enrich.py       # AI enrichment task
│   └── web/                    # Next.js frontend
│       ├── app/
│       │   ├── layout.tsx
│       │   ├── page.tsx        # Main page (split-screen)
│       │   └── globals.css     # Glassmorphism + nebula theme
│       ├── co

[README truncated for size]

## Detected evidence (automated analysis)

Indexed codebase: 91 recognized source files, 454 KB.
- CSS (language) — detected in the code
- FastAPI (technology) — detected in the code
- Next.js (technology) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- Docker (technology) — claimed on Devpost, not found in the code
- OpenAI (technology) — claimed on Devpost, not found in the code
- AI coding agent: Claude Code — evidence: config files committed to the repository

## Codebase structure (from repository index)

### Files (106 of 106)

```
.dockerignore
.env.example
.github/workflows/nextjs.yml
.gitignore
apps/__init__.py
apps/api/__init__.py
apps/api/ai/__init__.py
apps/api/ai/router.py
apps/api/auth.py
apps/api/config.py
apps/api/database.py
apps/api/Dockerfile
apps/api/main.py
apps/api/middleware/__init__.py
apps/api/middleware/caching.py
apps/api/providers/__init__.py
apps/api/providers/base.py
apps/api/providers/gdelt.py
apps/api/providers/guardian.py
apps/api/providers/newsapi.py
apps/api/providers/router.py
apps/api/redis_client.py
apps/api/routes/__init__.py
apps/api/routes/auth.py
apps/api/routes/chat.py
apps/api/routes/feed.py
apps/api/routes/health.py
apps/api/routes/heygen.py
apps/api/routes/meta.py
apps/api/routes/notifications.py
apps/api/routes/preferences.py
apps/api/routes/search.py
apps/api/routes/story.py
apps/api/routes/stream.py
apps/api/routes/translate.py
apps/api/routes/visa.py
apps/api/services/__init__.py
apps/api/services/clustering.py
apps/api/services/embeddings.py
apps/api/services/feed.py
apps/api/services/heygen.py
apps/api/services/search.py
apps/api/services/story.py
apps/api/services/trending.py
apps/api/services/visa.py
apps/web/.gitignore
apps/web/app/globals.css
apps/web/app/layout.tsx
apps/web/app/page.tsx
apps/web/components/AuthModal.tsx
apps/web/components/AvatarCreatorModal.tsx
apps/web/components/CategoryChips.tsx
apps/web/components/ChatSection.tsx
apps/web/components/ChatWidget.tsx
apps/web/components/CountrySelector.tsx
apps/web/components/CountrySidebar.tsx
apps/web/components/FeedCard.tsx
apps/web/components/FeedList.tsx
apps/web/components/Header.tsx
apps/web/components/ICCBanner.tsx
apps/web/components/LatestWorldMarquee.tsx
apps/web/components/LiveNewsVideo.tsx
apps/web/components/MarketDashboard.tsx
apps/web/components/NotificationBell.tsx
apps/web/components/PreferencesModal.tsx
apps/web/components/SearchBar.tsx
apps/web/components/StoryPanel.tsx
apps/web/components/WeatherWidget.tsx
apps/web/Dockerfile
apps/web/eslint.config.mjs
apps/web/hooks/useAuth.tsx
apps/web/hooks/useNotifications.ts
apps/web/hooks/useSSE.ts
apps/web/lib/api.ts
apps/web/lib/utils.ts
apps/web/next.config.ts
apps/web/package.json
apps/web/postcss.config.mjs
apps/web/README.md
apps/web/tsconfig.json
apps/web/types/index.ts
apps/web/vercel.json
apps/worker/__init__.py
apps/worker/Dockerfile
apps/worker/main.py
apps/worker/tasks/__init__.py
apps/worker/tasks/enrich.py
apps/worker/tasks/ingest.py
apps/worker/tasks/notify.py
CLAUDE.md
docker-compose.yml
packages/shared/__init__.py
packages/shared/constants.py
packages/shared/schemas.py
pyproject.toml
railway.json
README.md
render.yaml
requirements.txt
SESSION_RESUME.md
tests/__init__.py
tests/conftest.py
tests/test_clustering.py
tests/test_provider_router.py
tests/test_schemas.py
tests/test_trending.py
```

### Dependencies

- apps/web/package.json: @tailwindcss/postcss@^4, @types/node@^20, @types/react@^19, @types/react-dom@^19, date-fns@^4.1.0, eslint@^9, eslint-config-next@16.1.6, framer-motion@^12.34.0, next@16.1.6, react@19.2.3, react-dom@19.2.3, tailwindcss@^4, typescript@^5
- requirements.txt: aiohttp@>=3.9, aiosqlite@>=0.20, asyncpg@>=0.30, bcrypt@>=4.0, fastapi@>=0.115, httpx@>=0.28, psycopg2-binary@>=2.9, pydantic@>=2.10, pydantic-settings@>=2.7, pytest@>=8.3, pytest-asyncio@>=0.25, python-dotenv@>=1.0, python-jose[cryptography]@>=3.3, python-multipart@>=0.0.17, redis[hiredis]@>=5.2, sqlalchemy[asyncio]@>=2.0.36, uvicorn[standard]@>=0.34

### Recent commits (newest first)

- Add GitHub Actions workflow for Next.js deployment
- Update README.md
- Update README to include advanced algorithm mention
- Add Hygen API key information to README
- anion-rdy

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

### SESSION_RESUME.md

```markdown
# Session Resume Instructions

## For continuing in a new Claude Code session:

Just open Claude Code in this directory:
```bash
cd /Users/ashok/Desktop/hackathon
claude
```

Claude will automatically read CLAUDE.md and have full project context.

Then say:
```
Start all services (docker, API, worker, frontend) and test that all 8 categories work
and AI enrichment uses Claude for summaries, OpenAI for trending, and Perplexity for deep explain.
```

## What was just completed:
1. ✅ AI router rewritten — Claude/OpenAI/Perplexity each have dedicated roles
2. ✅ NewsAPI politics category fixed (uses /everything endpoint)
3. ✅ Worker enrichment does dual AI calls (Claude + OpenAI)
4. ✅ Category validation added to feed route
5. ✅ Frontend shows AI provider badges (Claude=orange, OpenAI=green, Perplexity=blue)
6. ✅ All 41 tests passing
7. ✅ Frontend builds successfully
8. ✅ DB schema updated (metadata_json column added to clusters)
9. ✅ Old AI enrichment data cleared (443 clusters ready for re-enrichment)

## What still needs to be done:
- Start all services and do a live test
- Verify each category returns articles
- Verify AI enrichment logs show "Claude" and "OpenAI" being used
- Verify Deep Explain button uses Perplexity

```

### CLAUDE.md

```markdown
# Aion — AI-Powered Global News Discovery Platform

## Project State (saved 2026-02-15)

This is a complete hackathon MVP with user auth, notifications, and preferences.

## Architecture

```
/Users/ashok/Desktop/hackathon/
├── apps/
│   ├── api/          # FastAPI backend (port 8000)
│   │   ├── ai/router.py        # AI provider routing (Claude, OpenAI, Perplexity)
│   │   ├── auth.py              # JWT auth: hash_password, verify_password, create_token, decode_token, get_current_user dependency
│   │   ├── config.py            # Pydantic Settings from .env (includes jwt_secret, jwt_algorithm, jwt_expire_hours)
│   │   ├── database.py          # SQLAlchemy async models (Article, Cluster, ClusterMember, User, UserPreference, Notification)
│   │   ├── redis_client.py      # Cache, circuit breaker, Redis Streams (SSE)
│   │   ├── main.py              # FastAPI app entry + ETag middleware + all routers
│   │   ├── middleware/           # ETag + Cache-Control middleware
│   │   │   └── caching.py
│   │   ├── providers/           # NewsAPI, Guardian, GDELT news providers
│   │   │   ├── base.py, newsapi.py, guardian.py, gdelt.py, router.py
│   │   ├── routes/              # feed.py, story.py, stream.py, meta.py, health.py, auth.py, preferences.py, notifications.py
│   │   └── services/            # clustering.py, trending.py, feed.py, story.py, embeddings.py
│   ├── web/          # Next.js 16 frontend (port 3000)
│   │   ├── app/page.tsx         # Main page with SSE, filters, AuthProvider wrapper, modals
│   │   ├── app/globals.css      # Pure B&W editorial design tokens
│   │   ├── components/          # Header, CategoryChips (with country selector), FeedList, FeedCard, StoryPanel, ChatSection, AuthModal, PreferencesModal, NotificationBell
│   │   ├── hooks/useSSE.ts      # Server-sent events hook (auto-resume via Last-Event-ID)
│   │   ├── hooks/useAuth.tsx    # Auth context + provider (JWT in localStorage)
│   │   ├── hooks/useNotifications.ts # Notification polling at user's interval
│   │   ├── lib/api.ts           # Backend API client (includes auth, preferences, notifications functions)
│   │   ├── types/index.ts       # TypeScript interfaces (includes User, AuthResponse, UserPreferences, NotificationItem)
│   │   └── public/aion.png      # Aion logo (cropped, no whitespace)
│   └── worker/       # Background worker
│       ├── main.py              # 4 async loops: ingest, cluster, enrich, notify
│       └── tasks/               # ingest.py, enrich.py, notify.py
├── packages/shared/             # schemas.py (includes auth/prefs/notification schemas), constants.py
├── tests/                       # 41 tests (all passing)
├── docker-compose.yml           # Postgres (port 5433), Redis
├── .env                         # API keys (real keys configured)
└── requirements.txt             # Python deps (Python 3.13 venv at .venv/)
```

## Recent Changes (2026-02-15)

### 1. User Authentication + Notification System (NEW)

Full user system with JWT auth,
[truncated — 9319 more characters]
```

### pyproject.toml

```
[project]
name = "aion"
version = "1.0.0"
description = "AI-powered global news discovery platform"
requires-python = ">=3.11"

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

```

### requirements.txt

```
fastapi>=0.115
uvicorn[standard]>=0.34
pydantic>=2.10
pydantic-settings>=2.7
sqlalchemy[asyncio]>=2.0.36
asyncpg>=0.30
psycopg2-binary>=2.9
redis[hiredis]>=5.2
httpx>=0.28
python-dotenv>=1.0
pytest>=8.3
pytest-asyncio>=0.25
aiosqlite>=0.20
python-jose[cryptography]>=3.3
bcrypt>=4.0
python-multipart>=0.0.17
aiohttp>=3.9

```

### docker-compose.yml

```yaml
version: "3.9"

services:
  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: pulse
      POSTGRES_PASSWORD: pulse
      POSTGRES_DB: pulse
    ports:
      - "5433:5432"
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U pulse"]
      interval: 5s
      timeout: 3s
      retries: 5

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5

  api:
    build:
      context: .
      dockerfile: apps/api/Dockerfile
    ports:
      - "8000:8000"
    env_file: .env
    environment:
      DATABASE_URL: postgresql+asyncpg://pulse:pulse@postgres:5432/pulse
      DATABASE_URL_SYNC: postgresql://pulse:pulse@postgres:5432/pulse
      REDIS_URL: redis://redis:6379/0
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy

  worker:
    build:
      context: .
      dockerfile: apps/worker/Dockerfile
    env_file: .env
    environment:
      DATABASE_URL: postgresql+asyncpg://pulse:pulse@postgres:5432/pulse
      DATABASE_URL_SYNC: postgresql://pulse:pulse@postgres:5432/pulse
      REDIS_URL: redis://redis:6379/0
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy

  web:
    build:
      context: .
      dockerfile: apps/web/Dockerfile
      args:
        NEXT_PUBLIC_API_URL: http://api:8000
    ports:
      - "3000:3000"
    depends_on:
      - api

volumes:
  pgdata:

```

### apps/worker/Dockerfile

```
FROM python:3.13-slim

WORKDIR /app

# System deps for asyncpg and bcrypt
RUN apt-get update && apt-get install -y --no-install-recommends \
    gcc libpq-dev && \
    rm -rf /var/lib/apt/lists/*

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY packages/ packages/
COPY apps/__init__.py apps/__init__.py
COPY apps/api/ apps/api/
COPY apps/worker/ apps/worker/

EXPOSE 9000

CMD ["python", "-m", "apps.worker.main"]

```

### apps/api/Dockerfile

```
FROM python:3.13-slim

WORKDIR /app

# System deps for asyncpg and bcrypt
RUN apt-get update && apt-get install -y --no-install-recommends \
    gcc libpq-dev && \
    rm -rf /var/lib/apt/lists/*

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY packages/ packages/
COPY apps/__init__.py apps/__init__.py
COPY apps/api/ apps/api/

EXPOSE 8000

CMD ["uvicorn", "apps.api.main:app", "--host", "0.0.0.0", "--port", "8000"]

```

### apps/web/package.json

```
{
  "name": "web",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "eslint"
  },
  "dependencies": {
    "date-fns": "^4.1.0",
    "framer-motion": "^12.34.0",
    "next": "16.1.6",
    "react": "19.2.3",
    "react-dom": "19.2.3"
  },
  "devDependencies": {
    "@tailwindcss/postcss": "^4",
    "@types/node": "^20",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "eslint": "^9",
    "eslint-config-next": "16.1.6",
    "tailwindcss": "^4",
    "typescript": "^5"
  }
}

```

### apps/web/Dockerfile

```
FROM node:20-alpine AS base

# ── Install dependencies ─────────────────────────────────────
FROM base AS deps
WORKDIR /app
COPY apps/web/package.json apps/web/package-lock.json* ./
RUN npm ci

# ── Build ────────────────────────────────────────────────────
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY apps/web/ ./

ARG NEXT_PUBLIC_API_URL
ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL

RUN npm run build

# ── Production ───────────────────────────────────────────────
FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production

RUN addgroup --system --gid 1001 nodejs && \
    adduser --system --uid 1001 nextjs

COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"

CMD ["node", "server.js"]

```

### apps/api/main.py

```python
"""AiON API — FastAPI application entry point."""

from __future__ import annotations

import logging
import sys
from contextlib import asynccontextmanager

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

from apps.api.config import get_settings
from apps.api.database import init_db, close_db
from apps.api.middleware.caching import ETagMiddleware
from apps.api.redis_client import close_redis
from apps.api.routes import auth, chat, feed, health, heygen, meta, notifications, preferences, search, story, stream, translate, visa

# ── Logging ──────────────────────────────────────────────────────
logging.basicConfig(
    level=logging.INFO,
    format='{"time":"%(asctime)s","level":"%(levelname)s","logger":"%(name)s","msg":"%(message)s"}',
    handlers=[logging.StreamHandler(sys.stdout)],
)
logger = logging.getLogger("aion")


# ── Lifespan ─────────────────────────────────────────────────────
@asynccontextmanager
async def lifespan(app: FastAPI):
    logger.info("Starting AiON API...")
    await init_db()
    logger.info("Database initialized")
    yield
    await close_db()
    await close_redis()
    logger.info("AiON API shut down")


# ── App ──────────────────────────────────────────────────────────
app = FastAPI(
    title="AiON API",
    description="AI-powered global news discovery platform",
    version="1.0.0",
    lifespan=lifespan,
)

# Middleware (order matters: CORS first, then ETag)
settings = get_settings()
app.add_middleware(
    CORSMiddleware,
    allow_origins=settings.cors_origins.split(","),
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)
app.add_middleware(ETagMiddleware)

# Routes
app.include_router(health.router)
app.include_router(meta.router)
app.include_router(feed.router)
app.include_router(story.router)
app.include_router(stream.router)
app.include_router(chat.router)
app.include_router(translate.router)
app.include_router(auth.router)
app.include_router(preferences.router)
app.include_router(notifications.router)
app.include_router(search.router)
app.include_router(visa.router)
app.include_router(heygen.router)


@app.get("/")
async def root():
    return {
        "name": "AiON API",
        "version": "1.0.0",
        "docs": "/docs",
    }

```

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