# S01 Research: [B] LightRAG Deployment + Docker Integration

## Summary

LightRAG is a graph-enhanced RAG framework that combines knowledge graph entity/relationship extraction with vector search. It ships an official Docker image (`ghcr.io/hkuds/lightrag:latest`) with a built-in REST API server. The framework natively supports Qdrant as a vector backend and NetworkX for graph storage (the D032-specified MVP approach). The main integration work is: (1) adding a LightRAG container to the existing Chrysopedia Docker Compose stack, (2) configuring it to use the existing Qdrant and Ollama services, (3) configuring DGX Sparks as the LLM backend via OpenAI-compatible binding, (4) customizing entity types for the music production domain, and (5) verifying entity extraction on test input.

## Recommendation

Use the official `ghcr.io/hkuds/lightrag:latest` Docker image with environment-variable-only configuration (no custom Dockerfile needed). Configure:
- **Vector storage:** `QdrantVectorDBStorage` pointing to `chrysopedia-qdrant:6333`
- **Graph storage:** `NetworkXStorage` (default, persisted to working dir)
- **LLM binding:** `openai` pointing to DGX Sparks at `https://chat.forgetyour.name/api`
- **Embedding binding:** `ollama` pointing to `chrysopedia-ollama:11434`
- **Entity types:** Custom music production types via `ENTITY_TYPES` env var
- **Workspace:** `chrysopedia` (isolates data in shared Qdrant via payload partitioning)

## Implementation Landscape

### Existing Infrastructure (on ub01)

| Service | Container | Internal Address | Notes |
|---------|-----------|-----------------|-------|
| PostgreSQL 16 | chrysopedia-db | chrysopedia-db:5432 | Not used by LightRAG (uses file-based KV) |
| Redis 7 | chrysopedia-redis | chrysopedia-redis:6379 | Could be used for LightRAG KV cache, but JSON default is simpler for MVP |
| Qdrant v1.13.2 | chrysopedia-qdrant | chrysopedia-qdrant:6333 | Shared — LightRAG creates its own collections with workspace prefix |
| Ollama | chrysopedia-ollama | chrysopedia-ollama:11434 | Runs nomic-embed-text (768 dim) |
| API | chrysopedia-api | chrysopedia-api:8000 | FastAPI — LLM at DGX Sparks (`https://chat.forgetyour.name/api`) |
| Web UI | chrysopedia-web-8096 | port 8096 | React frontend |
| Worker | chrysopedia-worker | — | Celery worker |
| Watcher | chrysopedia-watcher | — | Transcript folder watcher |
| MCP | chrysopedia-mcp | port 8101 | AI agent tools |

**Network:** `chrysopedia` bridge (172.32.0.0/24)
**Compose project:** `xpltd_chrysopedia`
**Repo:** `/vmPool/r/repos/xpltdco/chrysopedia`

### LightRAG Architecture (Key Findings)

**Four storage types:**
1. **KV_STORAGE** — LLM response cache, text chunks, document info. Default: `JsonKVStorage` (file-based). Persists to `WORKING_DIR`.
2. **VECTOR_STORAGE** — Entity, relation, and chunk vectors. Using: `QdrantVectorDBStorage`.
3. **GRAPH_STORAGE** — Entity relation graph. Using: `NetworkXStorage` (default, file-based GraphML). Per D032, NetworkX for MVP, Neo4j migration path at >100K entities.
4. **DOC_STATUS_STORAGE** — Document indexing status. Default: `JsonDocStatusStorage` (file-based).

**Qdrant workspace isolation:** QdrantVectorDBStorage uses payload-based partitioning (Qdrant's recommended multitenancy approach) — shared collections with payload filtering. Set `WORKSPACE=chrysopedia` to avoid any collision with the existing `chrysopedia` collection used by the current search system. LightRAG creates its own collections automatically.

**Entity types customization:** Set via `ENTITY_TYPES` env var (JSON array string). Default types are generic ("Person", "Creature", "Organization", etc.). For music production domain, define custom types like:
```
ENTITY_TYPES='["Creator", "Technique", "Plugin", "Synthesizer", "Effect", "Genre", "DAW", "SamplePack", "SignalChain", "Concept", "Frequency", "SoundDesignElement"]'
```

**REST API endpoints (key ones for this slice):**
- `POST /documents/text` — Insert text for indexing (triggers entity extraction)
- `POST /documents/texts` — Insert multiple texts
- `POST /query` — Query with modes: naive, local, global, hybrid, mix
- `GET /health` — Health check
- `POST /graph/entity/create` — Manual entity creation
- Built-in WebUI at the server root (port 9621)

**LLM configuration for DGX Sparks:**
```env
LLM_BINDING=openai
LLM_MODEL=fyn-llm-agent-chat
LLM_BINDING_HOST=https://chat.forgetyour.name/api
LLM_BINDING_API_KEY=<same key as chrysopedia-api>
```

**Embedding configuration (reusing existing Ollama):**
```env
EMBEDDING_BINDING=ollama
EMBEDDING_BINDING_HOST=http://chrysopedia-ollama:11434
EMBEDDING_MODEL=nomic-embed-text
EMBEDDING_DIM=768
```

### Docker Integration Plan

**New service in docker-compose.yml:**
```yaml
chrysopedia-lightrag:
  image: ghcr.io/hkuds/lightrag:latest
  container_name: chrysopedia-lightrag
  restart: unless-stopped
  ports:
    - "127.0.0.1:9621:9621"
  volumes:
    - /vmPool/r/services/chrysopedia_lightrag:/app/data
  env_file:
    - .env.lightrag
  environment:
    - TIKTOKEN_CACHE_DIR=/app/data/tiktoken
  depends_on:
    chrysopedia-qdrant:
      condition: service_healthy
    chrysopedia-ollama:
      condition: service_healthy
  networks:
    - chrysopedia
  healthcheck:
    test: ["CMD-SHELL", "curl -sf http://localhost:9621/health || exit 1"]
    interval: 15s
    timeout: 5s
    retries: 5
    start_period: 30s
```

**Separate .env.lightrag file** (cleaner than mixing vars with main .env):
```env
HOST=0.0.0.0
PORT=9621
WORKSPACE=chrysopedia

# LLM
LLM_BINDING=openai
LLM_MODEL=fyn-llm-agent-chat
LLM_BINDING_HOST=https://chat.forgetyour.name/api
LLM_BINDING_API_KEY=${LLM_API_KEY}

# Embedding
EMBEDDING_BINDING=ollama
EMBEDDING_BINDING_HOST=http://chrysopedia-ollama:11434
EMBEDDING_MODEL=nomic-embed-text
EMBEDDING_DIM=768

# Storage
LIGHTRAG_VECTOR_STORAGE=QdrantVectorDBStorage
QDRANT_URL=http://chrysopedia-qdrant:6333
LIGHTRAG_GRAPH_STORAGE=NetworkXStorage

# Entity extraction
ENTITY_TYPES='["Creator", "Technique", "Plugin", "Synthesizer", "Effect", "Genre", "DAW", "SamplePack", "SignalChain", "Concept", "Frequency", "SoundDesignElement"]'
ENABLE_LLM_CACHE_FOR_EXTRACT=true
```

### Bind mount path
Create `/vmPool/r/services/chrysopedia_lightrag` on ub01 for LightRAG data persistence (rag_storage, inputs, tiktoken cache).

## Risks & Mitigations

| Risk | Impact | Mitigation |
|------|--------|------------|
| DGX Sparks API compatibility with LightRAG's OpenAI binding | high — entity extraction fails silently or produces garbage | Test with a simple `/documents/text` insert before full integration. The API uses standard `/v1/chat/completions`. If it fails, fall back to Ollama with a local model. |
| Qdrant collection namespace collision | medium — LightRAG overwrites existing chrysopedia vectors | LightRAG uses workspace-prefixed collection names + payload filtering. The existing `chrysopedia` collection is managed by the current pipeline's QdrantManager. No collision as long as WORKSPACE is set. Verify by listing collections after first insert. |
| LightRAG image size / resource usage on ub01 | low — but ub01 is the home server | The image includes a WebUI frontend (bun build). Memory footprint with NetworkX graph is modest at MVP scale. Monitor with `docker stats`. |
| nomic-embed-text dimension mismatch | low — but would cause silent errors | Existing system uses 768 dims. LightRAG's EMBEDDING_DIM=768 must match. If LightRAG defaults to a different dim, inserts will fail with a Qdrant dimension mismatch error (loud failure). |
| Entity extraction quality with music production content | medium — generic prompts may miss domain concepts | Custom ENTITY_TYPES helps. First test should use real technique page content. If extraction quality is poor, the fix is ENTITY_TYPES tuning (no code change). |
| LLM_BINDING_API_KEY in .env.lightrag needs the same key as main stack | low — operational | Reference the same key. Could use env var interpolation in docker-compose or a shared .env. |

## Natural Task Seams

1. **Docker Compose + .env.lightrag** — Add the service definition, create the env file, create the bind mount directory. Pure infrastructure. Verify: `docker compose config` validates, `docker compose up chrysopedia-lightrag` starts, health check passes.

2. **LLM + Embedding connectivity verification** — With the container running, test that LightRAG can reach both the DGX Sparks LLM endpoint and the Ollama embedding endpoint. Verify: insert a trivial text via the REST API, check logs for successful LLM calls and embedding generation.

3. **Music production entity extraction test** — Insert real technique page content (from existing DB). Check that entities and relationships are extracted with correct music production types. Verify: query the `/graph` endpoints or check the working directory for the GraphML file, confirm entities like "Serum", "sidechain compression", "COPYCATT" appear with appropriate types.

## Don't Hand-Roll

- **Docker image:** Use `ghcr.io/hkuds/lightrag:latest`. Do NOT build from source — the official image includes the WebUI, all storage backends, and tiktoken cache setup.
- **Entity extraction prompts:** LightRAG has built-in extraction prompts that use the ENTITY_TYPES list. Do NOT write custom extraction prompts — configure via the env var.
- **Qdrant collection management:** LightRAG creates and manages its own Qdrant collections. Do NOT manually create collections for it.

## Key Files to Modify

| File | Change |
|------|--------|
| `docker-compose.yml` | Add `chrysopedia-lightrag` service |
| `.env.lightrag` (new) | LightRAG-specific environment configuration |
| (ub01 filesystem) | Create `/vmPool/r/services/chrysopedia_lightrag` directory |

## Verification Strategy

1. `docker compose config` — validates compose syntax
2. `docker compose up -d chrysopedia-lightrag` — container starts
3. `curl http://localhost:9621/health` — API responds
4. `curl -X POST http://localhost:9621/documents/text -H 'Content-Type: application/json' -d '{"text": "COPYCATT uses Serum for bass sound design with sidechain compression from the kick drum...", "file_source": "test_input.txt"}'` — insert succeeds, entities extracted
5. `curl http://localhost:9621/query -X POST -H 'Content-Type: application/json' -d '{"query": "What plugins does COPYCATT use?", "mode": "mix"}'` — returns meaningful response mentioning Serum

## Sources

- LightRAG GitHub: https://github.com/HKUDS/LightRAG
- LightRAG API docs: https://github.com/HKUDS/LightRAG/blob/main/lightrag/api/README.md
- LightRAG env.example: https://github.com/HKUDS/LightRAG/blob/main/env.example
- Docker image: `ghcr.io/hkuds/lightrag:latest`
- PyPI: `lightrag-hku` v1.4.12 (latest as of research date)
