rt-vlm

Par nvidia · skills

Utilisez cette skill lorsque vous travaillez avec l'API du microservice RTVI VLM ou RT-VLM sur VSS 3.1. Générez des légendes denses et des alertes pour des fichiers vidéo stockés et des flux RTSP en direct via `/v1/generate_captions_alerts` ; importez des médias via `/v1/files` ; ajoutez et supprimez des flux en direct avec `/v1/streams/add` et `/v1/streams/delete/{stream_id}` ; appelez `/v1/chat/completions` compatible OpenAI ; consommez les topics Kafka de légendes, d'incidents et d'erreurs ; ou déboguez les réponses rtvi-vlm. Pour le déploiement, consultez d'abord `references/deploy-rt-vlm-service.md`.

npx skills add https://github.com/nvidia/skills --skill rt-vlm

API d'utilisation RTVI VLM (VSS 3.1)

RTVI VLM est le microservice vision-langage temps réel de NVIDIA : décode la vidéo (fichier ou RTSP) → segmente en chunks → exécute un VLM (cosmos-reason1, cosmos-reason2, ou tout modèle compatible OpenAI) → diffuse des captions denses via SSE/HTTP et publie les captions + alertes d'incidents + erreurs sur Kafka. Utilisez cette skill quand vous devez accéder à n'importe quel endpoint /v1/... du microservice rtvi-vlm VSS 3.1 : génération de captions, upload de fichiers, gestion de flux en direct, vérifications de santé, chat completions compatible NIM, métriques Prometheus. Référence API : https://docs.nvidia.com/vss/latest/real-time-vlm-api.html.

Configuration

export BASE_URL="http://localhost:8000"     # RTVI VLM host:port — correspond à $RTVI_VLM_PORT dans compose
export API_KEY="$NGC_API_KEY"               # Token Bearer (la clé NGC fonctionne si le service a été déployé avec l'authentification NGC)

Chaque requête ci-dessous utilise Authorization: Bearer $API_KEY. Les endpoints de santé (/v1/health/*, /v1/ready, /v1/live, /v1/startup) fonctionnent généralement sans authentification.

Test de fumée avant utilisation :

curl -fsS "$BASE_URL/v1/health/ready" && curl -fsS "$BASE_URL/v1/models" | jq

Démarrage rapide — captions denses à partir d'une vidéo locale

# 1. Uploadez la vidéo, capturez son file id
FILE_ID=$(curl -fsS -X POST "$BASE_URL/v1/files" \
  -H "Authorization: Bearer $API_KEY" \
  -F "file=@/path/to/warehouse.mp4" \
  -F "purpose=vision" \
  -F "media_type=video" | jq -r '.id')

# 2. Générez les captions + alertes (flux SSE de réponses segmentées)
curl -N -X POST "$BASE_URL/v1/generate_captions_alerts" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"id\": \"$FILE_ID\",
    \"prompt\": \"Write a concise dense caption for each 10-second segment of this warehouse video.\",
    \"model\": \"cosmos-reason1\",
    \"chunk_duration\": 10,
    \"stream\": true
  }"

Endpoints

Captions

Générez des captions VLM et des alertes pour les vidéos et flux en direct.

POST /v1/generate_captions_alerts — Générez les captions VLM (et alertes) pour vidéo/flux

Requis : | Champ | Type | Description | |-------|------|-------------| | id | string | array | UUID d'un fichier précédemment uploadé, ou id d'un flux en direct actif. Accepte une liste d'ids pour un batch | | prompt | string | Prompt utilisateur au VLM (ex. instruction de dense-caption) | | model | string | Nom du modèle — voir GET /v1/models |

Champs optionnels clés : | Champ | Type | Défaut | Description | |-------|------|--------|-------------| | system_prompt | string | — | System prompt ; utilisez les balises <think></think><answer></answer> pour activer le reasoning sur Cosmos Reason | | enable_reasoning | boolean | false | Activez le reasoning pour les modèles Cosmos Reason | | enable_audio | boolean | false | Transcrivez l'audio (via Riva) et intégrez-le aux captions | | chunk_duration | integer | — | Segmentez la vidéo en chunks de N secondes (0 = pas de segmentation) | | chunk_overlap_duration | integer | 0 | Chevauchement entre chunks consécutifs | | num_frames_per_second_or_fixed_frames_chunk | number | — | FPS (si use_fps_for_chunking=true) ou frames fixes par chunk | | use_fps_for_chunking | boolean | false | Interprétez le champ ci-dessus comme FPS vs. nombre de frames fixe | | vlm_input_width / vlm_input_height | int | — | Redimensionnez les frames avant inférence (0 = natif) | | media_info | object | — | {"start_offset_ms": ..., "end_offset_ms": ...} pour traiter une tranche de fichier (pas les flux en direct) | | stream | boolean | false | SSE : émettez les deltas de caption par chunk sous forme d'événements data: (recommandé pour les longues vidéos) | | max_tokens / temperature / top_p / top_k / seed / ignore_eos | | | Contrôles d'échantillonnage standard | | response_format | object | — | Objet format de réponse de query | | mm_processor_kwargs | object | — | Kwargs supplémentaires pour le processeur multimodal (ex. size, shortest/longest edge) |

curl -N -X POST "$BASE_URL/v1/generate_captions_alerts" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "prompt": "Dense-caption this warehouse video, one sentence per 10s chunk.",
    "model": "cosmos-reason1",
    "chunk_duration": 10,
    "stream": true
  }'

Réponse (200, SSE quand stream=true) : chaque payload d'événement contient start_ts, end_ts, content, et un événement terminal {"status": "completed"}. Réponse (200, non-stream) : { "id", "object": "caption", "choices": [{...}], "usage": {...} }.

DELETE /v1/generate_captions_alerts/{stream_id} — Arrêtez la génération de captions pour un flux en direct

Arrête l'inférence tout en laissant le flux enregistré. À associer avec DELETE /v1/streams/delete/{stream_id} pour aussi dé-enregistrer la source RTSP.

curl -X DELETE "$BASE_URL/v1/generate_captions_alerts/$STREAM_ID" -H "Authorization: Bearer $API_KEY"

Fichiers

Uploadez et gérez les fichiers média consommés par /v1/generate_captions_alerts.

POST /v1/files — Uploadez un fichier média (multipart)

curl -X POST "$BASE_URL/v1/files" -H "Authorization: Bearer $API_KEY" \
  -F "file=@./video.mp4" -F "purpose=vision" -F "media_type=video"

Réponse : { "id", "object": "file", "bytes", "created_at", "filename", "purpose" }.

GET /v1/files?purpose=vision — Listez les fichiers uploadés

GET /v1/files/{file_id} — Métadonnées de fichier

GET /v1/files/{file_id}/content — Téléchargez le contenu du fichier original

DELETE /v1/files/{file_id} — Supprimez le fichier (libère le stockage d'assets)

Flux en direct

Cycle de vie du flux RTSP.

POST /v1/streams/add — Enregistrez un ou plusieurs flux RTSP

Requis par flux : liveStreamUrl (doit commencer par rtsp://), description. Optionnel : username, password, sensor_name, et métadonnées de placement (place_name, place_type, place_lat, place_lon, place_alt, place_coordinate_x, place_coordinate_y).

STREAM_ID=$(curl -fsS -X POST "$BASE_URL/v1/streams/add" \
  -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
  -d '{"streams":[{"liveStreamUrl":"rtsp://cam:8554/live","description":"warehouse cam 1"}]}' \
  | jq -r '.results[0].id')

GET /v1/streams/get-stream-info — Listez les flux actifs

DELETE /v1/streams/delete/{stream_id} — Supprimez un flux unique

DELETE /v1/streams/delete-batch — Supprimez plusieurs flux ({"stream_ids":[...]})

Compatible NIM

Endpoints compatibles OpenAI pour l'interopérabilité avec les clients OpenAI/NVIDIA-API.

POST /v1/chat/completions — Chat compatible OpenAI (texte + multimodal)

Requis : messages, model. Les requêtes texte uniquement omettent id / video_url / image_url.

curl -X POST "$BASE_URL/v1/chat/completions" -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"cosmos-reason1","messages":[{"role":"user","content":"Summarize this scene."}]}'

POST /v1/completions — Completions héritées compatibles OpenAI

GET /v1/version{ "version": "3.1.0-..." }

GET /v1/license — texte de licence

GET /v1/manifest — manifeste NIM

GET /v1/health/live · GET /v1/health/ready — sondes de style NIM

Modèles · Métadonnées · Métriques · Vérification de santé

GET /v1/models — Listez les VLMs chargés : { "data": [{ "id", "object": "model", "owned_by" }] }

GET /v1/metadata — Métadonnées du service (build, release, image tag)

GET /v1/metrics — Métriques Prometheus (texte brut)

GET /v1/ready · GET /v1/live · GET /v1/startup — Sondes de style Kubernetes


Workflows courants

Les quatre scénarios des exigences de skill RT-VLM Usage VSS 3.1.

1. Captions denses à partir d'un fichier vidéo stocké

# Upload → capturez file id → générez les captions (flux SSE)
FILE_ID=$(curl -fsS -X POST "$BASE_URL/v1/files" \
  -H "Authorization: Bearer $API_KEY" \
  -F "file=@warehouse.mp4" -F "purpose=vision" -F "media_type=video" | jq -r '.id')

curl -N -X POST "$BASE_URL/v1/generate_captions_alerts" \
  -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
  -d "{
    \"id\": \"$FILE_ID\",
    \"prompt\": \"Describe warehouse events in 1 sentence per 10s chunk.\",
    \"model\": \"cosmos-reason1\",
    \"chunk_duration\": 10,
    \"stream\": true
  }"

# Quand terminé, libérez le stockage :
curl -X DELETE "$BASE_URL/v1/files/$FILE_ID" -H "Authorization: Bearer $API_KEY"

2. Captions denses à partir d'un flux en direct RTSP

# Enregistrez le flux
STREAM_ID=$(curl -fsS -X POST "$BASE_URL/v1/streams/add" \
  -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
  -d '{"streams":[{"liveStreamUrl":"rtsp://10.0.0.5:8554/warehouse","description":"warehouse cam"}]}' \
  | jq -r '.results[0].id')

# Commencez la génération de captions continue (s'exécute jusqu'à l'arrêt du flux ou DELETE)
curl -N -X POST "$BASE_URL/v1/generate_captions_alerts" \
  -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
  -d "{
    \"id\": \"$STREAM_ID\",
    \"prompt\": \"Describe each event; start each sentence with a timestamp.\",
    \"model\": \"cosmos-reason1\",
    \"chunk_duration\": 10,
    \"num_frames_per_second_or_fixed_frames_chunk\": 2,
    \"use_fps_for_chunking\": true,
    \"stream\": true
  }" &

# Démontez quand terminé :
curl -X DELETE "$BASE_URL/v1/generate_captions_alerts/$STREAM_ID" -H "Authorization: Bearer $API_KEY"
curl -X DELETE "$BASE_URL/v1/streams/delete/$STREAM_ID"  -H "Authorization: Bearer $API_KEY"

3. Workflows Kafka (alertes + message bus)

Pré-req : le container a été lancé avec :

RTVI_VLM_KAFKA_ENABLED=true

RTVI_VLM_KAFKA_TOPIC=vision-llm-messages

RTVI_VLM_KAFKA_INCIDENT_TOPIC=vision-llm-events-incidents

RTVI_VLM_ERROR_MESSAGE_TOPIC=vision-llm-errors

HOST_IP=<kafka-host>

STREAM_ID=$(curl -fsS -X POST "$BASE_URL/v1/streams/add" \
  -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
  -d '{"streams":[{"liveStreamUrl":"rtsp://10.0.0.5:8554/warehouse","description":"warehouse cam"}]}' \
  | jq -r '.results[0].id')

curl -N -X POST "$BASE_URL/v1/generate_captions_alerts" \
  -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
  -d "{
    \"id\": \"$STREAM_ID\",
    \"prompt\": \"You are a warehouse monitoring system. Describe the scene in one sentence, then on a new line output exactly:\\nAnomaly Detected: Yes/No\\nReason: <one sentence>\\nFlag an anomaly if any worker is missing a hard hat or high-vis vest.\",
    \"system_prompt\": \"Answer the user's question correctly in yes or no.\",
    \"model\": \"cosmos-reason2\",
    \"chunk_duration\": 60,
    \"chunk_overlap_duration\": 10,
    \"stream\": true
  }"

Consommez les alertes depuis Kafka (quand vous utilisez le container Kafka fondational VSS). Les valeurs Kafka sont des payloads protobuf NvSchema, donc utilisez print.value=false pour une passe de validation propre qui affiche le timestamp, la clé et les headers sans dumper les bytes de payload binaires :

docker exec mdx-kafka kafka-console-consumer \
  --bootstrap-server 127.0.0.1:9092 \
  --topic vision-llm-events-incidents \
  --from-beginning \
  --timeout-ms 5000 \
  --max-messages 10 \
  --property print.timestamp=true \
  --property print.key=true \
  --property print.headers=true \
  --property print.value=false

Si Kafka n'est pas exécuté dans le container VSS mdx-kafka, utilisez la CLI Kafka depuis l'hôte exécutant le broker :

kafka-console-consumer \
  --bootstrap-server "$HOST_IP:9092" \
  --topic vision-llm-events-incidents \
  --from-beginning \
  --timeout-ms 5000 \
  --max-messages 10 \
  --property print.timestamp=true \
  --property print.key=true \
  --property print.headers=true \
  --property print.value=false

Protobuf d'incident (ext.proto :: Incident) champs clés : sensorId, timestamp, end, objectIds, frameIds, place, analyticsModule, category, isAnomaly (true pour les alertes), llm (VisionLLM imbriquée), map info incluant triggerPhrase, verdict, requestId, chunkIdx, streamId, alertCategory (si le déploiement supporte le champ de query alert_category — post-3.1).

La dense captioning avec alertes sur un flux RTSP et le modèle de réponse HTTP-vs-Kafka sont documentés dans references/kafka-workflows.md.

Référence d'erreurs

Code Sens Cause courante
400 Bad Request Champ requis manquant (id, prompt, model) ; media_type non supporté ; nom de model inconnu
401 Unauthorized Authorization: Bearer $API_KEY manquant/invalide — ou format de clé incorrect (attendu nvapi-...)
404 Not Found file_id supprimé / stream_id non enregistré / chemin d'endpoint incorrect (note : {stream_id} est requis sur DELETE /v1/streams/delete/{stream_id})
413 Payload Too Large Fichier uploadé dépasse MAX_FILE_SIZE du serveur ; augmentez ou pré-segmentez la vidéo
422 Unprocessable Entity Violation de schéma Pydantic — ex. use_fps_for_chunking=true sans num_frames_per_second_or_fixed_frames_chunk ; stream ids fournis à un champ réservé aux fichiers comme media_info
429 Rate Limited Trop de flux concurrents — relevez VLM_BATCH_SIZE ou distribuez sur plusieurs instances
500 Server Error Exception d'inférence VLM (OOM, modèle indisponible) — vérifiez docker logs rtvi-vlm-*
503 Service Busy Démarrage incomplet (modèle toujours en téléchargement) ou dépendance NIM upstream unhealthy

Pièges

  • L'endpoint GA 3.1 est /v1/generate_captions_alerts, pas /v1/generate_captions. Le renommage arrive dans une build post-3.1. Pour les releases VSS 3.1 (rtvi_vlm/26.01.x26.02.3), utilisez toujours le suffixe _alerts. https://docs.nvidia.com/vss/latest/real-time-vlm-api.html est la référence canonique.
  • Pas d'entrée basée sur URL en 3.1 GA — les champs url/media_type/creation_time ont été ajoutés post-3.1. Vous devez d'abord uploader via POST /v1/files puis passer l'id retourné.
  • Déclencheur d'alerte = les tokens "yes" ou "true" dans la réponse VLM (insensible à la casse). Il n'y a pas de flag d'alerte par requête. Concevez les prompts avec une ligne Anomaly Detected: Yes/No explicite et définissez system_prompt pour contraindre le modèle à répondre Yes/No (selon la doc VSS). Chaque chunk est publié sur KAFKA_TOPIC ; les chunks matchés vont aussi sur KAFKA_INCIDENT_TOPIC avec isAnomaly=true, info["triggerPhrase"] défini aux tokens matchés, et info["verdict"]="confirmed".
  • Pas de champ de query alert_category dans la spec OpenAPI 3.1. Le topic d'incidents Kafka défaut incident.category = "vlm-alert" sur 3.1. Les builds post-3.1 exposent un champ de requête optionnel alert_category pour surcharger incident.category.
  • Les topics Kafka sont une config côté serveur, pas par requête. Les variables KAFKA_* (via rewrites RTVI_VLM_KAFKA_* en compose) sont fixes au démarrage du container — les clients ne peuvent pas surcharger les topics sur une base par requête. La publication Kafka est additive à la réponse HTTP, jamais un remplacement.
  • stream=true retourne Server-Sent Events, pas du JSON segmenté. Utilisez curl -N (pas de buffering). Chaque événement est data: {"content": "...", "start_ts": ..., "end_ts": ...}\n\n, terminé par data: {"status":"completed"}\n\n. Sans stream=true le serveur buffère jusqu'à ce que la vidéo complète soit traitée — acceptable pour les courts clips (<1 min), évitez pour les flux en direct.
  • chunk_duration=0 désactive la segmentation — la vidéo entière est envoyée au VLM d'un seul coup. Utile seulement pour les courts clips ; les longues vidéos vont OOM ou dépasseront max_model_len.
  • Budget de frames par défaut capped à VLLM_MM_PROCESSOR_VIDEO_NUM_FRAMES (256). Demander un FPS qui implique >256 frames par chunk est silencieusement cappé ; baissez le FPS ou raccourcissez chunk_duration pour rester dans le budget.
  • enable_reasoning requiert un modèle Cosmos Reason. Le passer avec Qwen3-VL ou d'autres modèles non-reasoning est un no-op.
  • /v1/metrics requiert l'auth, contrairement à /v1/health/*. Les scrapers Prometheus ont besoin du token Bearer.
  • L'upload de fichier est multipart, pas JSON. Utilisez -F file=@path -F purpose=vision -F media_type=video ; un corps -d retourne 422.
  • Le cycle de vie du flux en direct requiert deux deletes pour démolir complètement : DELETE /v1/generate_captions_alerts/{stream_id} arrête l'inférence ; DELETE /v1/streams/delete/{stream_id} dé-enregistre le flux. Sauter le second leake les ressources de connexion RTSP.

Skills similaires