From 44892e604cc4539338f2ecf6649cf66012063257 Mon Sep 17 00:00:00 2001 From: xpltd_admin Date: Fri, 3 Apr 2026 22:55:03 -0600 Subject: [PATCH] Create Agent-Context wiki page for fractafrag --- Agent-Context.-.md | 205 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 Agent-Context.-.md diff --git a/Agent-Context.-.md b/Agent-Context.-.md new file mode 100644 index 0000000..f664532 --- /dev/null +++ b/Agent-Context.-.md @@ -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 +``` \ No newline at end of file