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.x–26.02.3), utilisez toujours le suffixe_alerts.https://docs.nvidia.com/vss/latest/real-time-vlm-api.htmlest la référence canonique. - Pas d'entrée basée sur URL en 3.1 GA — les champs
url/media_type/creation_timeont été ajoutés post-3.1. Vous devez d'abord uploader viaPOST /v1/filespuis passer l'idretourné. - 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 ligneAnomaly Detected: Yes/Noexplicite et définissezsystem_promptpour contraindre le modèle à répondre Yes/No (selon la doc VSS). Chaque chunk est publié surKAFKA_TOPIC; les chunks matchés vont aussi surKAFKA_INCIDENT_TOPICavecisAnomaly=true,info["triggerPhrase"]défini aux tokens matchés, etinfo["verdict"]="confirmed". - Pas de champ de query
alert_categorydans la spec OpenAPI 3.1. Le topic d'incidents Kafka défautincident.category = "vlm-alert"sur 3.1. Les builds post-3.1 exposent un champ de requête optionnelalert_categorypour surchargerincident.category. - Les topics Kafka sont une config côté serveur, pas par requête. Les variables
KAFKA_*(via rewritesRTVI_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=trueretourne Server-Sent Events, pas du JSON segmenté. Utilisezcurl -N(pas de buffering). Chaque événement estdata: {"content": "...", "start_ts": ..., "end_ts": ...}\n\n, terminé pardata: {"status":"completed"}\n\n. Sansstream=truele 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=0dé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épasserontmax_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 raccourcissezchunk_durationpour rester dans le budget. enable_reasoningrequiert un modèle Cosmos Reason. Le passer avec Qwen3-VL ou d'autres modèles non-reasoning est un no-op./v1/metricsrequiert 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-dretourne 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.