# S01: [B] LightRAG Deployment + Docker Integration

**Goal:** LightRAG service running in Docker Compose stack on ub01, connected to existing Qdrant and Ollama services, with DGX Sparks as LLM backend. Entity extraction produces music production entities from test input. Query returns meaningful results.
**Demo:** After this: LightRAG service running in Docker, connected to Qdrant, entity extraction prompts producing music production entities from test input

## Tasks
- [x] **T01: Added chrysopedia-lightrag service to docker-compose.yml with Qdrant vector storage, Ollama embeddings, and DGX Sparks LLM config in .env.lightrag** — Add the chrysopedia-lightrag service definition to docker-compose.yml and create the .env.lightrag configuration file. This is the infrastructure config task — all file changes are local, verified with `docker compose config`.

## Steps

1. Read the existing `docker-compose.yml` to understand the current service layout, network, and patterns.
2. Add the `chrysopedia-lightrag` service to `docker-compose.yml` after the `chrysopedia-ollama` service (before the application services). Use:
   - Image: `ghcr.io/hkuds/lightrag:latest`
   - Container name: `chrysopedia-lightrag`
   - Port: `127.0.0.1:9621:9621`
   - Volume: `/vmPool/r/services/chrysopedia_lightrag:/app/data`
   - env_file: `.env.lightrag`
   - Environment: `TIKTOKEN_CACHE_DIR=/app/data/tiktoken`
   - depends_on: `chrysopedia-qdrant` (service_healthy), `chrysopedia-ollama` (service_healthy)
   - Network: `chrysopedia`
   - Health check: `curl -sf http://localhost:9621/health || exit 1` with interval 15s, timeout 5s, retries 5, start_period 30s
   - restart: unless-stopped
3. Create `.env.lightrag` with these settings:
   - `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=` (placeholder — to be set on ub01)
   - 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 types: `ENTITY_TYPES=["Creator", "Technique", "Plugin", "Synthesizer", "Effect", "Genre", "DAW", "SamplePack", "SignalChain", "Concept", "Frequency", "SoundDesignElement"]`
   - Cache: `ENABLE_LLM_CACHE_FOR_EXTRACT=true`
4. Add `.env.lightrag` to `.gitignore` if not already covered by a wildcard pattern. Check if `.env*` or `.env.lightrag` is already ignored.
5. Run `docker compose config` to validate — must exit 0 with no errors.

## Must-Haves

- [ ] chrysopedia-lightrag service in docker-compose.yml with correct image, ports, volumes, depends_on, healthcheck
- [ ] .env.lightrag with all required LightRAG configuration
- [ ] .env.lightrag excluded from git tracking
- [ ] `docker compose config` exits 0

## Verification

- `docker compose config 2>&1 | grep -q chrysopedia-lightrag && echo PASS || echo FAIL`
- `docker compose config` exits 0 (validates all service definitions, env var references, network config)
- `grep -q 'env.lightrag' .gitignore && echo PASS || echo FAIL`

## Failure Modes

| Dependency | On error | On timeout | On malformed response |
|------------|----------|-----------|----------------------|
| docker compose config | Fix syntax error in compose file | N/A | N/A |
| .env.lightrag file | Verify all env var names match LightRAG's expected names | N/A | N/A |
  - Estimate: 30m
  - Files: docker-compose.yml, .env.lightrag, .gitignore
  - Verify: docker compose config 2>&1 | grep -q chrysopedia-lightrag && echo PASS
- [x] **T02: Deployed LightRAG on ub01, fixed healthcheck (curl missing from image, switched to Python urllib), verified entity extraction (10 entities, 9 relations) and query returning accurate music production results** — Deploy the LightRAG container on ub01, set the real API key, verify health, and test entity extraction with music production content. This is the operational proof task.

## Steps

1. SSH to ub01 and create the bind mount directory:
   ```
   ssh ub01 'mkdir -p /vmPool/r/services/chrysopedia_lightrag'
   ```
2. Push local changes (docker-compose.yml, .env.lightrag) to the repo so ub01 can pull them:
   - Stage and commit docker-compose.yml and .env.lightrag changes
   - Push to the remote
   - On ub01: `cd /vmPool/r/repos/xpltdco/chrysopedia && git pull`
3. On ub01, copy .env.lightrag and set the real LLM_BINDING_API_KEY:
   - The key is the same as LLM_API_KEY in the main .env file on ub01
   - `ssh ub01` then edit `.env.lightrag` in the repo directory to set the real key
4. Pull the LightRAG Docker image and bring up the service:
   ```
   ssh ub01 'cd /vmPool/r/repos/xpltdco/chrysopedia && docker compose pull chrysopedia-lightrag && docker compose up -d chrysopedia-lightrag'
   ```
5. Wait for health check to pass (up to 60s):
   ```
   ssh ub01 'for i in $(seq 1 12); do docker inspect --format="{{.State.Health.Status}}" chrysopedia-lightrag 2>/dev/null | grep -q healthy && echo HEALTHY && break; sleep 5; done'
   ```
6. Verify the /health endpoint:
   ```
   ssh ub01 'curl -sf http://localhost:9621/health'
   ```
7. Verify existing services still healthy (R010 re-verification):
   ```
   ssh ub01 'docker ps --filter name=chrysopedia --format "{{.Names}} {{.Status}}"'
   ```
   All existing services should still show as healthy.
8. Test entity extraction with music production content:
   ```
   ssh ub01 'curl -sf -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. The signal chain runs through OTT for multiband compression, then into a Valhalla Room reverb send. The technique involves layering a sub bass with a mid-range reese bass, using LFO modulation on the wavetable position in Serum to create movement.", "file_source": "test_music_production.txt" }\''
   ```
   Should return success. Check container logs for entity extraction output.
9. Test query to verify entities were extracted and are searchable:
   ```
   ssh ub01 'curl -sf -X POST http://localhost:9621/query -H "Content-Type: application/json" -d \'{ "query": "What plugins does COPYCATT use for bass sound design?", "mode": "mix" }\''
   ```
   Should return a response mentioning Serum, OTT, Valhalla Room.
10. If entity extraction or query fails, check logs:
    ```
    ssh ub01 'docker logs chrysopedia-lightrag --tail 50'
    ```
    Common failures: LLM API key invalid (401), embedding model not loaded (connection refused), Qdrant unreachable (DNS). Fix and retry.

## Must-Haves

- [ ] Bind mount directory exists on ub01
- [ ] LightRAG container running and healthy on ub01
- [ ] /health endpoint returns 200
- [ ] All existing Chrysopedia services still healthy
- [ ] Entity extraction succeeds with music production text
- [ ] Query returns meaningful response with extracted entities

## Verification

- `ssh ub01 'curl -sf http://localhost:9621/health'` returns 200
- `ssh ub01 'docker ps --filter name=chrysopedia-lightrag --format "{{.Status}}"'` contains "healthy"
- Entity extraction POST returns success (HTTP 200)
- Query POST returns response containing music production entities
- `ssh ub01 'docker ps --filter name=chrysopedia --format "{{.Names}} {{.Status}}"'` shows all services healthy

## Failure Modes

| Dependency | On error | On timeout | On malformed response |
|------------|----------|-----------|----------------------|
| DGX Sparks LLM API | Check API key, verify endpoint URL. If 401/403: key is wrong. If 500: try with Ollama fallback. | Increase LightRAG timeout config or check DGX Sparks availability | Log response body, may need model name adjustment |
| Ollama embedding | Verify chrysopedia-ollama is healthy, check `ollama list` for nomic-embed-text model | Ollama may be loading model — wait and retry | Check embedding dimension config matches model output |
| Qdrant vector store | Verify chrysopedia-qdrant is healthy, check port 6333 reachable from LightRAG container | Qdrant may be recovering — wait for health check | Collection creation may fail — check Qdrant logs |

## Negative Tests

- **Malformed inputs**: If entity extraction returns empty/garbage, check ENTITY_TYPES env var is valid JSON array
- **Error paths**: If LLM returns 401, the API key is wrong — compare with main .env LLM_API_KEY
- **Boundary conditions**: First-time startup may be slow (downloading tiktoken data, creating Qdrant collections) — allow 60s start_period
  - Estimate: 45m
  - Files: docker-compose.yml, .env.lightrag
  - Verify: ssh ub01 'curl -sf http://localhost:9621/health && echo HEALTH_OK'; ssh ub01 'docker ps --filter name=chrysopedia-lightrag --format "{{.Status}}" | grep -q healthy && echo CONTAINER_OK'
