---
estimated_steps: 56
estimated_files: 1
skills_used: []
---

# T02: Wire debug-mode-aware LLM I/O capture into pipeline callback and verify end-to-end

Modify the pipeline's `_make_llm_callback` to conditionally capture full prompt and response text when debug mode is enabled. Thread system_prompt and user_prompt through to the callback at all 4 stage call sites. Rebuild worker container and verify end-to-end on ub01.

All work is on ub01 via SSH at /vmPool/r/repos/xpltdco/chrysopedia.

## Steps

1. Add `system_prompt_text`, `user_prompt_text`, `response_text` kwargs to `_emit_event()` in `backend/pipeline/stages.py`. Pass them through to the `PipelineEvent` constructor.

2. Add a sync Redis helper at the top of `backend/pipeline/stages.py` to read debug mode:
   ```python
   def _is_debug_mode() -> bool:
       """Check if debug mode is enabled via Redis. Falls back to config."""
       try:
           import redis
           settings = get_settings()
           r = redis.from_url(settings.redis_url)
           val = r.get("chrysopedia:debug_mode")
           r.close()
           if val is not None:
               return val.decode().lower() == "true"
       except Exception:
           pass
       return getattr(get_settings(), 'debug_mode', False)
   ```
   Note: uses sync `redis.from_url()` because Celery worker is sync (D004). The `redis` package is already installed (Celery broker dependency).

3. Modify `_make_llm_callback(video_id, stage)` to accept `system_prompt: str | None = None` and `user_prompt: str | None = None` parameters. Inside the callback closure:
   - Check `_is_debug_mode()`
   - If debug on: pass `system_prompt_text=system_prompt`, `user_prompt_text=user_prompt`, `response_text=content` to `_emit_event()`
   - If debug off: pass None for all three (existing behavior preserved)

4. Update all 4 `llm.complete()` call sites to pass prompt text to the callback factory:
   - Stage 2 (~line 278): `_make_llm_callback(video_id, "stage2_segmentation", system_prompt=system_prompt, user_prompt=user_prompt)`
   - Stage 3 (~line 370): same pattern
   - Stage 4 (~line 485): same pattern  
   - Stage 5 (~line 724): same pattern

5. Rebuild and restart worker + API containers:
   ```bash
   cd /vmPool/r/repos/xpltdco/chrysopedia
   docker compose build chrysopedia-api chrysopedia-worker
   docker compose up -d
   docker compose restart chrysopedia-web-8096  # nginx DNS cache
   ```

6. End-to-end verification:
   a. Enable debug mode: `curl -X PUT http://ub01:8096/admin/pipeline/debug-mode -H 'Content-Type: application/json' -d '{"debug_mode":true}'`
   b. Trigger pipeline for a video: `curl -X POST http://ub01:8096/admin/pipeline/trigger/{video_id}`
   c. Wait for pipeline completion (check logs or events)
   d. Query events: `curl http://ub01:8096/admin/pipeline/events/{video_id}?event_type=llm_call` — verify `system_prompt_text`, `user_prompt_text`, `response_text` are populated
   e. Query token summary: `curl http://ub01:8096/admin/pipeline/token-summary/{video_id}` — verify per-stage breakdown
   f. Disable debug mode: `curl -X PUT http://ub01:8096/admin/pipeline/debug-mode -H 'Content-Type: application/json' -d '{"debug_mode":false}'`
   g. (Optional) Trigger another run — verify new events have null prompt/response columns

## Must-Haves

- [ ] `_emit_event` accepts and stores prompt/response text columns
- [ ] `_is_debug_mode()` reads from sync Redis, falls back to config
- [ ] `_make_llm_callback` conditionally captures full I/O when debug is on
- [ ] All 4 stage call sites pass system_prompt and user_prompt to callback
- [ ] Pipeline still works when debug is off (existing behavior preserved)
- [ ] End-to-end: debug on → pipeline → events have full prompt/response text

## Observability Impact

- Signals added: Full LLM system prompt, user prompt, and response text stored per llm_call event when debug mode is on
- How a future agent inspects this: `curl http://ub01:8096/admin/pipeline/events/{video_id}?event_type=llm_call` — check system_prompt_text/user_prompt_text/response_text fields
- Failure state exposed: `_is_debug_mode()` failure is silent (falls back to off) — matches existing best-effort pattern in `_emit_event`

## Inputs

- ``backend/pipeline/stages.py` — existing _emit_event, _make_llm_callback, and 4 stage call sites`
- ``backend/models.py` — PipelineEvent model with new columns (from T01)`
- ``backend/routers/pipeline.py` — debug-mode and token-summary endpoints (from T01)`
- ``backend/config.py` — Settings.debug_mode field (from T01)`

## Expected Output

- ``backend/pipeline/stages.py` — _emit_event with prompt/response params, _is_debug_mode() helper, _make_llm_callback with conditional capture, all 4 call sites updated`

## Verification

ssh ub01 'curl -sf http://ub01:8096/admin/pipeline/debug-mode | python3 -c "import sys,json; assert json.load(sys.stdin)[\"debug_mode\"] in [True,False]"' && ssh ub01 'docker logs chrysopedia-worker --tail 5 2>&1 | head -5'
