Create Agent-Context wiki page for fractafrag

xpltd_admin 2026-04-03 22:55:03 -06:00
parent 845143a489
commit 44892e604c

205
Agent-Context.-.md Normal file

@ -0,0 +1,205 @@
# 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: `pending``rendering``ready` 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 Alembic**`db/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)
```glsl
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
```bash
# 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
```