ai-cost-tracker/backend/app/main.py
Vadym Samoilenko 2f070ce503 feat: initial implementation of Oliver AI Cost Tracker
Complete Phase 1 implementation:

Backend (FastAPI + MongoDB + Celery):
- Core: config, DB with indexes, JWT security, API key auth middleware
- Models: org hierarchy (workspace/team/project), user mirror, pricing,
  usage events/rollups, budgets, alert log, audit log
- Services: pricing engine (LiteLLM/YAML/override priority), budget check
  with preflight, email alerts at 50/80/100%, analytics aggregations,
  audit logger
- API routes: public (preflight/record/upsert), admin CRUD, pricing
  management, budget management, analytics (summary/timeseries/breakdown/pivot),
  Microsoft SSO auth
- Celery tasks: daily LiteLLM price sync with change notifications,
  daily rollup aggregation, 5-minute alert evaluator
- Pricing catalogue: ElevenLabs + Google Cloud TTS in models.yaml

SDK (oliver-cost-tracker Python package):
- CostTracker client with httpx + exponential backoff (3 retries)
- SQLite outbox with 30s background flusher (never blocks AI pipeline)
- Estimators: token/char estimation per provider
- BudgetExceeded / CostTrackerUnavailable exceptions

Frontend (React 18 + Vite + TypeScript):
- Dashboard with KPI cards, daily cost timeseries, top-model/top-user charts
- Pivot Explorer with multi-dim row/col selection + stacked bar chart + table
- Admin pages: Workspaces, Pricing (with LiteLLM sync + override), Budgets
  (with live spend bar), API Keys (show-once), Users (mirror), Audit Log
- Microsoft SSO login flow

Infra: docker-compose.yml (mongo + redis + api + celery worker + beat + frontend)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 11:26:08 +01:00

57 lines
1.8 KiB
Python

"""FastAPI application entry-point."""
from contextlib import asynccontextmanager
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from .core.config import settings
from .core.database import connect_db, create_indexes, disconnect_db, get_db_instance
from .core.logging import get_logger
from .services.pricing_engine import load_yaml_prices
logger = get_logger(__name__)
@asynccontextmanager
async def lifespan(app: FastAPI):
# Startup
logger.info("Connecting to MongoDB…")
await connect_db()
db = get_db_instance()
await create_indexes(db)
logger.info("Creating indexes… done")
loaded = await load_yaml_prices(db)
logger.info(f"YAML prices loaded: {loaded} new records")
yield
# Shutdown
await disconnect_db()
logger.info("MongoDB disconnected")
app = FastAPI(
title="Oliver AI Cost Tracker",
version="1.0.0",
lifespan=lifespan,
)
app.add_middleware(
CORSMiddleware,
allow_origins=[o.strip() for o in settings.cors_origins.split(",")],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
from .api.v1.routes_public import router as public_router
from .api.v1.routes_admin import router as admin_router
from .api.v1.routes_pricing import router as pricing_router
from .api.v1.routes_budgets import router as budgets_router
from .api.v1.routes_analytics import router as analytics_router
from .api.v1.routes_auth import router as auth_router
app.include_router(public_router, prefix="/v1", tags=["public"])
app.include_router(auth_router, prefix="/v1", tags=["auth"])
app.include_router(admin_router, prefix="/admin", tags=["admin"])
app.include_router(pricing_router, prefix="/admin", tags=["pricing"])
app.include_router(budgets_router, prefix="/admin", tags=["budgets"])
app.include_router(analytics_router, prefix="/admin", tags=["analytics"])