CLAUDE.md@genai-engine · git:20260731.d2c2e65 · 2026-07-31 · sha256 39d00aa1c81e03a3

CLAUDE.md@genai-engine git:20260731.d2c2e65A

Immutable. This exact content is served forever at /api/v1/blob/39d00aa1c81e03a3.

# GenAI Engine

FastAPI service for LLM evaluation, guardrails, and monitoring. Python 3.12, SQLAlchemy, PostgreSQL + pgvector, Alembic.

## Commands

```bash
uv sync --group dev --group linters
uv run serve                                 # dev server → http://localhost:3030/docs
uv run pytest -m "unit_tests"                # coverage must stay ≥ 79% (--cov=src --cov-fail-under=79)
./tests/test_remote.sh                       # integration tests against a deployed env
uv run alembic revision --autogenerate -m "description"
uv run alembic upgrade head
uv run black src && uv run isort src --profile black
uv run generate_changelog                    # required after any API change
uv run routes_security_check
```

Requires PostgreSQL (`docker compose up`) plus `POSTGRES_*` and `GENAI_ENGINE_SECRET_STORE_KEY` env vars — the `setup-genai-dev` skill walks through this. pytest markers: `unit_tests`, `integration_tests`, `aws_live`, `azure_live`.

## Gotchas

- The API has two versions (v1 legacy, v2 current) with different authentication and feature sets.
- Unit tests must be deterministic: never rely on database result ordering, and clean up any state they create.
- Review autogenerated Alembic migrations for idempotency before applying.
- Put all imports at the top of Python files.
- DB sessions from `Depends(get_db_session)` are auto-closed by FastAPI's dependency lifecycle — do not add `try/finally: db_session.close()` in route handlers.
- ML model files (prompt injection, toxicity, PII models) download and cache on first use; GPU is optional but speeds up model-based checks.

## Strong typing

Represent any data with known structure — API objects, LLM response schemas, request/response bodies — as Pydantic `BaseModel` classes, never raw `dict`/`str`/`Any` parsed with `.get()`. For LLM calls, pass a Pydantic class as `response_format` to `client.completion()` instead of `{"type": "json_object"}`; the parsed result is on `response.structured_output_response`.

## Multi-tenancy

The engine supports multi-tenant organizations. Tasks belong to exactly one org; tenant API keys are scoped to a single org via `api_keys.org_id` and reach all tasks within it. Admin keys (`org_id = NULL`) keep cross-org access. Existing tasks migrate into the `default` org; system tasks (`is_system_task=True`) migrate into the `system` org.

Tenant provisioning is gated by `GENAI_ENGINE_DEMO_MODE` (default off). When enabled, `POST /api/v2/tenant/signup` creates `(org, task, api_key)` in one transaction and returns the raw key once. When disabled, the endpoint returns 404. Keep this flag **off** for customer production deployments.

When adding endpoints or repository methods that touch task-scoped data, see [docs/MULTI_TENANCY_DESIGN.md](docs/MULTI_TENANCY_DESIGN.md) for the four enforcement patterns (path / resource-id / query-param / admin-only), the `TENANT-USER` role rules, and the 404-vs-403 convention. A fuzz test enforces that decorators are applied to new routes.