Video Ingestion Agent — Environment Doctor

SkillDocs & knowledge

Diagnostic checklist for the Video Ingestion Agent environment — run when something is broken, before a first ingest, or as a sanity check. Use this skill whenever the user reports the pipeline isn't working, the vLLM server won't start or crashes during model loading, gets `CUDA out of memory` during ingestion, sees `401 Unauthorized` from HuggingFace, the webapp fails to launch, retrieval returns no results despite a populated database, or asks generally to "diagnose", "debug", "verify", or "check" the video ingestion setup. Also trigger on phrases like "ingestion environment broken", "is my install correct", "vllm server health", or descriptions of half-failing runs (`runs/<ts>/` directory exists but `report.html` is missing).

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Video Ingestion Agent — Environment Doctor skill

What this skill tells your AI

The instructions your AI receives, as published by nvidia-isaac/video_to_data in .claude/skills/ingestion_agent_doctor/SKILL.md and read by ahel’s review.

Run through these checks in order when something's wrong, or before a first run as a sanity sweep. Each step has a command, what a pass looks like, and what to do on a fail. Stop at the first failing check and apply its fix before continuing — most failures cascade.

All commands assume the user is in /home/liuw/Projects/video_to_data/video_ingestion_agent/ (substitute your clone path) and has activated the project's .venv.

Before You Start

Ask the user briefly:

  • What's the symptom? (server won't start / OOM / no retrieval results / webapp errors / pipeline hangs / unsure)
  • When did it last work? (never / yesterday / right after I ran X)

Skip ahead to the relevant section if the symptom is specific. Otherwise run sections 1 -> 6 in order. Section 7 covers escalation when nothing here resolves it.

For the full first-time setup, use the ingestion_agent_onboard skill instead — this skill assumes the user has at least attempted setup and is troubleshooting.

1. vLLM Server Health

The pipeline needs a running vLLM server at vllm_url (default http://localhost:8000/v1) for the default vllm backend. If the user is on the local or api backend, skip to Section 2.

python scripts/serve.py --status
OutputMeaningAction
Server is running on port 8000HealthyContinue to Section 2
Server is not runningDaemon never started or has stoppedpython scripts/serve.py -c configs/ingestion.yaml
Reports running but curl http://localhost:8000/v1/models hangs/failsDaemon process alive but model load failedpython scripts/serve.py --logs and look for the crash

To inspect the log directly:

python scripts/serve.py --logs
# or
tail -200 ~/.video_ingestion_agent/vllm.log

What to look for:

  • Application startup complete — fully loaded, ready to serve
  • OutOfMemoryError — see Section 2
  • 401 Unauthorized from huggingface — see Section 3
  • ValueError: Requested more deepstack tokens than available — vLLM 0.20+ regression specific to Qwen3-VL. The pyproject.toml pin vllm>=0.15.1,<0.20 avoids it; if hit on a manually upgraded vllm, downgrade: uv pip install 'vllm==0.15.1'
  • flashinfer-cubin version (X) does not match flashinfer version (Y) — bypass with FLASHINFER_DISABLE_VERSION_CHECK=1 python scripts/serve.py -c configs/ingestion.yaml. Long-term, align with uv pip install --reinstall for flashinfer.

Port collision check:

lsof -i :8000

If something other than the vLLM server holds the port (look at the COMMAND column), either stop it or set vllm_url to a different port in your config and restart the server.

2. GPU + Driver

The default 8B VLM (Qwen/Qwen3-VL-8B-Instruct) needs ~16 GB VRAM at bf16; SigLIP-2 needs another ~2 GB. If both share one GPU, reserve headroom with vllm_gpu_memory_utilization: 0.7 (or --gpu-mem 0.7 on the CLI).

nvidia-smi

Check:

  • All expected GPUs visible (matches vllm_tp_size in the config)
  • Free memory ≥ ~18 GB summed across the TP shard for the default model
  • Driver version supports the installed CUDA build

Then verify torch can see the GPUs:

python -c "import torch; print(torch.cuda.is_available(), torch.cuda.device_count(), torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'no GPU')"

Pass: True N <model_name>. If False, the torch install doesn't see the driver — reinstall with uv sync --extra local --reinstall, or check the nvidia-smi output above first to make sure the driver is actually loaded.

When OOM is the symptom

CauseFix
Default 8B model too large for a single GPUvllm_tp_size: N in config (or --tp N on CLI) to shard across GPUs
SigLIP competing for VRAM with the VLMvllm_gpu_memory_utilization: 0.7 (or --gpu-mem 0.7)
Embedding batch too largeReduce embedding_batch_size to 4 or 8 in config
Just need a smaller modelChange models.vlm_model to a smaller HF VLM and restart the server
Local backend, not vLLMSwitch models.vlm_backend: vllm (vLLM is more memory-efficient via PagedAttention)

3. Auth + Weights

Both the default VLM (Qwen/Qwen3-VL-8B-Instruct) and the default embedder (google/siglip2-base-patch16-256) are gated on HuggingFace and require the user to have accepted the license on their account.

echo $HF_TOKEN

Pass: hf_xxxx.... Fail (empty): get a token from https://huggingface.co/settings/tokens with read permission and export HF_TOKEN=hf_xxx. Then visit each gated model's HF page in a browser and click "agree" on the license — having a token isn't enough.

Cache check:

ls ~/.cache/huggingface/hub/ 2>/dev/null | head -20

Pass: subdirectories like models--Qwen--Qwen3-VL-8B-Instruct/, models--google--siglip2-base-patch16-256/. Fail (empty or missing target model): not yet downloaded — start the server or run an ingest, the download happens lazily on first use.

If you see 401 Unauthorized in the vLLM log specifically: HF_TOKEN is missing, expired, or the account hasn't accepted that specific model's license. Visit the HF model page in a browser to confirm.

4. Database Integrity

After at least one successful ingest, two SQLite files live in the database directory (default outputs/).

ls -la outputs/

Pass: graph.db and vector.db exist. During or shortly after an active session, *.db-wal and *.db-shm may also exist — that's WAL mode and is normal.

Counts in graph.db:

sqlite3 outputs/graph.db "SELECT COUNT(*) FROM video_metadata;"
sqlite3 outputs/graph.db "SELECT COUNT(*) FROM action_segments;"
sqlite3 outputs/graph.db "SELECT COUNT(*) FROM entities;"
sqlite3 outputs/graph.db "SELECT COUNT(*) FROM relationships;"

Pass: each non-zero. Fails:

What's zeroDiagnosisAction
video_metadataIngestion never wrote anythingRe-run scripts/run_ingestion.py; if it errors, check runs/<ts>/pipeline.log
action_segments (with video_metadata non-zero)Segmentation produced no clipsCheck VLM responses in pipeline.log; the VLM may be returning empty arrays — try a different chunk size or fps
entities (with action_segments non-zero)Entity-graph stage skipped or failedCheck ingest was run with enable_entity_graph: true (default); look in pipeline.log for entity-extractor errors

Visual embeddings:

sqlite3 outputs/vector.db "SELECT COUNT(*) FROM frame_embeddings;"

Non-zero -> visual search is wired up. Zero -> SigLIP didn't run; visual queries will return nothing but graph queries still work.

For retrieval to resolve visual hits to clip boundaries, frame embeddings must be tagged with segment_id:

sqlite3 outputs/vector.db "SELECT segment_id FROM frame_embeddings WHERE segment_id IS NOT NULL LIMIT 5;"

Should return non-empty IDs that match action_segments.segment_id in graph.db. If empty, either the DB is from an older version that didn't tag embeddings, or the ingest skipped the entity graph (which is what generates the segments). Re-run ingest with enable_entity_graph: true.

5. Python Environment

pip list | grep -E "vllm|torch|transformers|gradio|sentence-transformers|pre-commit"

Expected (versions vary; the question is "is it installed at all"):

PackageRequired forWhen
vllm[server] extra; vLLM backendServer-side machine only
torch[local] extra; SigLIP embedder is always loaded locallyAlways
transformerstokenizers / SigLIPAlways
gradio[webapp] extraRequired even for non-UI imports — see below
sentence-transformers[benchmark] extraBenchmark only
pre-commit[dev] extraDev only

[webapp] is required even if the user never runs the webapp, because webapp/__init__.py eagerly imports app. If a non-webapp script fails with ModuleNotFoundError: No module named 'gradio':

uv sync --extra webapp

CI installs [local,dev,benchmark,webapp] for the test suite for exactly this reason.

Lockfile drift check:

uv sync --frozen

Pass: completes without changes. Fail (pyproject.toml differs from lock): either uv lock (regenerate the lock) or git checkout pyproject.toml uv.lock (revert local edits). CI runs with --frozen, so lock drift is a likely cause of "works on my machine but not in CI" failures.

6. Run-Dir Sanity

For any specific failed run:

ls -la runs/<timestamp>/

A successful run has:

FileWhat's in it
clips_stage1.jsonlInitial VLM-segmented clips (1 line per clip)
clips_verified.jsonlAfter critic pass (with iteration_status field per clip)
clips_final.jsonlFinal clips, post-refinement
critic_responses/One JSON per clip with detailed critic feedback
report.htmlSummary HTML
pipeline.logStage-by-stage execution log

A half-completed run is missing some of the above:

  • clips_stage1.jsonl empty -> segmentation produced nothing. Check VLM responses in pipeline.log for empty / malformed JSON
  • clips_verified.jsonl missing -> verify stage failed or was skipped (--no-verify). If the stage was meant to run, look for critic errors in pipeline.log
  • clips_final.jsonl missing but clips_verified.jsonl present -> refinement was skipped or crashed
  • A temp_clips/ directory still present -> an interrupted verify/refine cycle. The cleanup is normally idempotent on re-run; if you want to manually clean up, rm -rf runs/<ts>/temp_clips/

If pipeline.log ends mid-stage without a clean shutdown message, the process was killed (OOM, Ctrl+C, kernel OOM-killer). Cross-check with dmesg | tail -50 for OOM-killer entries.

7. When to Escalate

If after the above the issue persists, file a GitHub issue at https://github.com/nvidia-isaac/video_to_data/issues. Attach:

File / outputWhere it lives
Pipeline logruns/<ts>/pipeline.log
vLLM server log~/.video_ingestion_agent/vllm.log
Config usedconfigs/ingestion.yaml (or whichever was passed to -c)
Python env snapshotpip freeze (or uv pip freeze) output
GPU infonvidia-smi output
OS / driveruname -a, nvidia-smi --query-gpu=driver_version --format=csv
Repro commandThe full python scripts/... command that failed

Redact any HuggingFace tokens or API keys before attaching.

For depth on each subsystem the docs are the canonical reference:

  • docs/pages/troubleshooting.rst — symptom-keyed full troubleshooting guide
  • docs/pages/model_backends.rst — backend-specific troubleshooting table
  • docs/pages/configuration.rst — every config key explained
  • docs/pages/architecture.rst — system design (LangGraph nodes, data flow)

Signals

GitHub stars
618
Forks
60
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
ingestion-agent-doctor
Source
github.com/nvidia-isaac/video_to_data