1 Agent-Context
xpltd_admin edited this page 2026-04-03 22:55:03 -06:00

Agent Context

Meta Value
Repo xpltdco/fractafrag
Language Python 3.12 (API), TypeScript (Frontend), Node.js (Renderer/MCP)
Framework FastAPI + SQLAlchemy 2 (async) + React 18 + Vite
Entry Point services/api/app/main.py
Test Command make test or docker compose exec api pytest tests/ -v
Build Command docker compose build
Compose Services 8 (nginx, frontend, api, mcp, renderer, worker, postgres, redis)
Database PostgreSQL 16 + pgvector
Cache/Queue Redis 7 (Celery broker, token blocklist)
Last Updated 2026-04-04

File Index (Read These First)

Priority File Purpose
1 services/api/app/main.py FastAPI app setup, lifespan, CORS, router registration
2 services/api/app/config.py Pydantic Settings — all env vars
3 services/api/app/models/models.py All SQLAlchemy ORM models (14 tables)
4 services/api/app/schemas/schemas.py Pydantic request/response schemas
5 services/api/app/middleware/auth.py JWT auth, password hashing, dependencies
6 services/api/app/routers/shaders.py Shader CRUD, versioning, fork, search
7 services/api/app/routers/feed.py Personalized feed, trending, tag affinity
8 services/api/app/routers/votes.py Voting + Wilson hot score calculation
9 services/api/app/routers/desires.py Bounty board (create, list, fulfill)
10 services/api/app/routers/auth.py Register, login, refresh, logout
11 services/api/app/routers/users.py Profile, BYOK keys
12 services/api/app/worker/__init__.py Celery tasks (render, embed, cluster, generate)
13 services/api/app/services/embedding.py TF-IDF + SVD vectorizer (512-dim)
14 services/api/app/services/clustering.py pgvector cosine nearest-neighbor
15 services/api/app/services/glsl_validator.py Static GLSL syntax analysis
16 services/api/app/database.py Async SQLAlchemy engine + sessions
17 services/api/app/redis.py Async Redis client singleton
18 services/mcp/server.py MCP tools for AI agents
19 services/renderer/server.js Express + Puppeteer rendering pipeline
20 docker-compose.yml Service orchestration
21 db/init.sql Full PostgreSQL schema + indexes

Dependency Map

docker-compose.yml
├── nginx (routes to frontend, api, mcp, renders)
├── frontend (React SPA, Vite)
├── api (FastAPI)
│   ├── app/main.py
│   │   ├── app/routers/*.py (10 router modules)
│   │   ├── app/middleware/auth.py (JWT)
│   │   └── app/middleware/rate_limit.py
│   ├── app/database.py → PostgreSQL (asyncpg)
│   ├── app/redis.py → Redis
│   ├── app/models/models.py (ORM)
│   ├── app/schemas/schemas.py (Pydantic)
│   └── app/services/
│       ├── embedding.py (TF-IDF + SVD)
│       ├── clustering.py (pgvector cosine)
│       ├── glsl_validator.py
│       ├── renderer_client.py → renderer:3100
│       └── byok.py (encryption)
├── worker (Celery, reuses api image)
│   └── app/worker/__init__.py
│       ├── render_shader → renderer:3100
│       ├── process_desire → embedding + clustering
│       └── ai_generate → external LLM APIs
├── mcp (FastMCP)
│   └── server.py → api:8000 (internal)
├── renderer (Express + Puppeteer)
│   └── server.js → /renders volume
├── postgres (pgvector/pgvector:pg16)
│   └── db/init.sql (bootstrap schema)
└── redis (redis:7-alpine)

Common Modification Patterns

To add a new API endpoint:

  1. Create/edit router in services/api/app/routers/
  2. Add Pydantic schemas in services/api/app/schemas/schemas.py
  3. Register router in services/api/app/main.py if new file
  4. Add Depends(get_current_user) for auth, Depends(get_db) for database

To modify the database schema:

  1. Edit db/init.sql (for new installs)
  2. Edit ORM model in services/api/app/models/models.py
  3. Create Alembic migration: docker compose exec api alembic revision --autogenerate -m "change"
  4. Apply: docker compose exec api alembic upgrade head

To add a Celery task:

  1. Define in services/api/app/worker/__init__.py
  2. Use sync DB (psycopg2, not asyncpg) — Celery is sync
  3. Enqueue from router: my_task.delay(args)

To add an MCP tool:

  1. Add function in services/mcp/server.py with @mcp.tool()
  2. Call API internally via httpx to http://api:8000/api/v1/...

Gotchas

  1. Async vs Sync DB — The API uses asyncpg (async SQLAlchemy). The Celery worker uses psycopg2 (sync). Don't mix them — worker tasks must create their own sync engine/session.

  2. Celery task imports — The worker image is built from the same Dockerfile as the API. Tasks are defined in app/worker/__init__.py. The Celery app must be importable as app.worker.

  3. System user — UUID 00000000-0000-0000-0000-000000000001 is the platform system account. It's created by db/init.sql. System-generated shaders use this author. Don't delete it.

  4. GLSL validation — The validator in glsl_validator.py does static analysis only (no GPU compilation). It checks for mainImage entry point, balanced braces, banned extensions, and infinite loop patterns. A shader can pass validation but still fail to render.

  5. Render status — Shaders go through: pendingrenderingready or failed. The feed only shows shaders with render_status='ready'. A newly published shader won't appear in the feed until the worker renders it.

  6. JWT refresh rotation — On every /refresh call, the old refresh token is blocklisted in Redis. If a client uses the same refresh token twice, the second attempt fails (preventing replay attacks). The blocklist entries have TTL matching the refresh token lifetime (30 days).

  7. pgvector cosine — The <=> operator returns cosine distance (0 = identical, 2 = opposite). Similarity = 1 - distance. The clustering threshold (0.82 similarity) means distance < 0.18.

  8. Embedding dimensions — All vectors are 512-dimensional. This is hardcoded in the TF-IDF + SVD pipeline and in the PostgreSQL vector(512) column types. Changing it requires rebuilding all embeddings and altering the column type.

  9. CORS — Currently set to allow_origins=["*"]. This must be restricted before production deployment.

  10. Init SQL vs Alembicdb/init.sql bootstraps the schema on first PostgreSQL startup. For schema changes after initial deployment, use Alembic migrations. The init.sql and ORM models must stay in sync manually.

MCP Tools (for AI Agents)

The MCP server at /mcp/* exposes these tools:

Tool Purpose
browse_shaders(query, tags, shader_type, sort, limit) Search/browse shaders
get_shader(shader_id) Get full shader with GLSL source
get_shader_versions(shader_id) Version history
get_shader_version_code(shader_id, version_number) Specific version code
submit_shader(title, glsl_code, ...) Create new shader
update_shader(shader_id, glsl_code, ...) Update shader (new version)
get_trending(limit) Trending shaders
get_similar_shaders(shader_id, limit) Similar by tag overlap
get_desire_queue(min_heat, limit) Browse bounties
fulfill_desire(desire_id, shader_id) Mark bounty fulfilled

GLSL Shader Format (Shadertoy-compatible WebGL2)

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec2 uv = fragCoord / iResolution.xy;
    fragColor = vec4(uv, 0.5 + 0.5 * sin(iTime), 1.0);
}

Available uniforms: iTime (float), iResolution (vec3), iMouse (vec4)

Verification Commands

# Start all services
make up

# Check service health
docker compose ps

# Run API tests
make test

# Lint Python code
docker compose exec api ruff check app/ tests/

# Database shell
make db-shell

# View logs
make logs

# Rebuild after changes
make build && make up

Architecture Summary

8-service Docker Compose stack:

  nginx:80 (reverse proxy)
    ├── / → frontend:5173 (React SPA)
    ├── /api/* → api:8000 (FastAPI)
    ├── /mcp/* → mcp:3200 (FastMCP for AI agents)
    └── /renders/* → static volume (shader thumbnails)

  api:8000 (FastAPI, async)
    ├── JWT auth + bcrypt passwords
    ├── SQLAlchemy 2 async → PostgreSQL
    ├── Redis (token blocklist, rate limits)
    └── Enqueues Celery tasks

  worker (Celery, sync)
    ├── render_shader → renderer:3100
    ├── process_desire → embed + cluster
    └── Periodic: feed cache, bounty expiry

  renderer:3100 (Puppeteer + Chromium)
    └── GLSL → WebGL → screenshot → /renders

  postgres:5432 (pgvector)
    └── 14 tables, HNSW indexes, trigram search

  redis:6379
    └── Celery broker/backend, JWT blocklist