mirror of
https://github.com/karpathy/nanochat.git
synced 2026-05-21 15:18:04 +00:00
- FastAPI service that manages conversations and messages in PostgreSQL
(SQLAlchemy 2.0 async + asyncpg) and streams assistant responses back
to the client via sse-starlette, forwarding the inference service SSE
contract unchanged.
- Auth guard validates every request against the auth service
/auth/validate endpoint (X-Internal-API-Key) and caches results in an
in-process TTL cache (5 min, 1024 entries) to absorb request bursts.
- Every query filters by authenticated user_id; cross-user access
returns 404. Message send flow auto-titles the first message,
persists the streamed assistant response after the client disconnects,
and records token_count + inference_time_ms.
- /api/models{,/swap} proxies the inference admin surface; swap
requires is_admin on the validated user.
- Structured JSON logging via structlog with trace_id + user_id
ContextVars attached to every log line.
- Test suite (pytest + aiosqlite + respx) covers CRUD, user scoping,
streaming SSE persistence, regenerate, model proxy admin gate,
and the stream proxy error path. 16/16 passing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
50 lines
1.3 KiB
Python
50 lines
1.3 KiB
Python
"""Async SQLAlchemy engine and session factory."""
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import AsyncIterator
|
|
|
|
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
|
|
from sqlalchemy.orm import DeclarativeBase
|
|
|
|
from .config import get_settings
|
|
|
|
|
|
class Base(DeclarativeBase):
|
|
"""Shared declarative base for all chat-api ORM models."""
|
|
|
|
|
|
_engine = None
|
|
_session_factory: async_sessionmaker[AsyncSession] | None = None
|
|
|
|
|
|
def _build_engine() -> None:
|
|
global _engine, _session_factory
|
|
settings = get_settings()
|
|
_engine = create_async_engine(settings.database_url, pool_pre_ping=True)
|
|
_session_factory = async_sessionmaker(_engine, expire_on_commit=False)
|
|
|
|
|
|
def get_engine():
|
|
if _engine is None:
|
|
_build_engine()
|
|
return _engine
|
|
|
|
|
|
def get_session_factory() -> async_sessionmaker[AsyncSession]:
|
|
if _session_factory is None:
|
|
_build_engine()
|
|
assert _session_factory is not None
|
|
return _session_factory
|
|
|
|
|
|
async def get_session() -> AsyncIterator[AsyncSession]:
|
|
factory = get_session_factory()
|
|
async with factory() as session:
|
|
yield session
|
|
|
|
|
|
def override_session_factory(factory: async_sessionmaker[AsyncSession]) -> None:
|
|
"""Testing hook: swap the session factory for an in-memory engine."""
|
|
global _session_factory
|
|
_session_factory = factory
|