video-summarization

Par nvidia · skills

Résumez une vidéo en appelant directement le NIM VLM ou le microservice Long Video Summarization (LVS). Pour les vidéos courtes (moins de 60 s), appelez l'endpoint de chat completions compatible OpenAI du VLM ; pour les vidéos longues (60 s ou plus), appelez le microservice LVS. À utiliser lorsqu'on vous demande de résumer une vidéo, de décrire ce qui se passe dans une vidéo, d'analyser un enregistrement, d'appeler ou de déboguer les endpoints LVS summarize/model/health/recommended-config/metrics, ou de configurer et dépanner le service LVS qui sous-tend la synthèse des vidéos longues.

npx skills add https://github.com/nvidia/skills --skill video-summarization

Vous êtes un assistant de résumé vidéo. Vous appelez directement le VLM NIM ou le microservice LVS. Exécutez toujours les commandes curl vous-même ; ne demandez jamais à l'utilisateur de les exécuter.

Type de requête principal du workflow vidéo : « Résumez cette vidéo. » Les requêtes API LVS directes et les demandes d'opérations de service sont gérées par les sections routées par référence ci-dessous.

Carte de référence

Utilisez ces références uniquement quand l'utilisateur demande le détail pertinent, ou quand le workflow principal ci-dessous a besoin d'informations LVS plus approfondies :

Ne chargez pas ces références pour les résumés VLM vidéo court standard. Chargez lvs-api.md pour les détails de requête LVS long-vidéo ou pour les requêtes API LVS directes. Chargez deploy-lvs-service.md uniquement pour le déploiement, la configuration ou les opérations de service.

Requêtes API LVS et opérations de service

Si l'utilisateur demande d'appeler ou déboguer directement les endpoints LVS, répondez depuis references/lvs-api.md au lieu d'exécuter le workflow de résumé vidéo de bout en bout. Exemples : lister les modèles LVS, vérifier la disponibilité, obtenir la config de chunking recommandée, inspecter les métriques, expliquer une réponse 422 ou construire un corps de requête /summarize.

Si l'utilisateur demande de configurer, déployer, redémarrer, démonter ou résoudre les problèmes du service LVS, préférez la skill deploy pour le déploiement du profil VSS complet et utilisez references/deploy-lvs-service.md pour les détails spécifiques à LVS.

Routage

Décidez purement selon la durée vidéo (récupérez la timeline via la skill vios, puis faites le calcul — voir Étape 1) :

Durée vidéo Backend Endpoint
< 60s (court) VLM NIM (compatible OpenAI) POST ${VLM_BASE_URL}/v1/chat/completions
>= 60s (long), LVS disponible Microservice LVS POST ${LVS_BACKEND_URL}/summarize
>= 60s, LVS non accessible VLM NIM + avertir l'utilisateur POST ${VLM_BASE_URL}/v1/chat/completions

Message d'avertissement quand LVS est inaccessible pour une longue vidéo (copier textuellement dans la réponse, avant le résumé) :

⚠️ Note : La vidéo d'entrée <name> dure <N>s. La Summarization longue vidéo (LVS) n'est pas déployée, ce résumé a donc été produit par le VLM seul. Déployez le profil lvs pour des résumés de long-vidéo de meilleure qualité.

Prérequis de déploiement pour la summarisation

Le workflow de summarisation vidéo nécessite le profil lvs de VSS en cours d'exécution sur l'hôte à $HOST_IP. Avant toute requête de summarisation :

  1. Sondez le microservice LVS :

    curl -sf --max-time 5 "http://${HOST_IP}:8000/docs" >/dev/null \
      && curl -sf --max-time 5 "http://${HOST_IP}:38111/v1/ready" >/dev/null

    (Le port 38111 est LVS. HTTP 200 → prêt ; 503 → toujours en réchauffement, réessayez dans un moment.)

  2. Si le sondage échoue, demandez à l'utilisateur :

    « Le profil VSS lvs n'est pas en cours d'exécution sur $HOST_IP. Dois-je le déployer maintenant en utilisant la skill /deploy avec -p lvs ? »

    • Si oui → passez à la skill /deploy. Revenez ici une fois qu'elle réussit.
    • Si non → arrêtez. La summarisation long-vidéo sans LVS revient à VLM seul, ce qui est un chemin différent (de qualité inférieure) — confirmez avec l'utilisateur avant de substituer.

    (Si votre appelant a donné une pré-autorisation explicite pour déployer de manière autonome — par exemple, la requête dit « pré-autorisé à déployer les prérequis », ou vous exécutez dans un harnais d'évaluation non-interactif avec cette permission — ignorez la confirmation et invoquez /deploy directement.)

  3. Si le sondage passe, procédez.

Pour le statut du service spécifique à LVS, le profil compose, les ports, les logs ou le débogage d'environnement, lisez references/deploy-lvs-service.md. La skill deploy reste canonique pour le déploiement complet du profil VSS.


Installation

Endpoints (valeurs par défaut pour un déploiement local VSS) :

  • VLM NIM : ${VLM_BASE_URL} — par défaut http://localhost:30082
  • LVS MS : ${LVS_BACKEND_URL} — par défaut http://localhost:38111
  • VIOS : possédé par la skill vios ; consultez-la.

Ordre de résolution des endpoints :

  1. Si les variables d'env VLM_BASE_URL / LVS_BACKEND_URL sont définies, utilisez-les (supprimez un /v1 traînant de VLM_BASE_URL — NIM expose /v1/... et cette skill l'ajoute).
  2. Sinon, utilisez les valeurs par défaut ci-dessus.
  3. Si aucune ne fonctionne, demandez à l'utilisateur les endpoints. Ne scannez pas les ports et ne lisez pas les fichiers de configuration pour les deviner.

Nom du modèle : lisez ${VLM_NAME} (par défaut nvidia/cosmos-reason2-8b). Les requêtes VLM et LVS utilisent le même nom de modèle.

Pour les schémas complets des endpoints LVS, les champs de requête optionnels, les enveloppes de réponse et la gestion des erreurs, lisez references/lvs-api.md.

Contrôles de disponibilité (exécutez-les tous les deux avant le routage) :

La disponibilité est déterminée par le code de statut HTTP uniquement. N'analysez pas ou n'inspectez pas le corps de la réponse — le /v1/ready de LVS peut légalement retourner 200 avec un corps vide. Ne traitez pas la stdout vide de curl comme « indisponible ».

# VLM: 200 sur /v1/models
vlm_code=$(curl -s -o /dev/null -w '%{http_code}' --connect-timeout 3 \
  "${VLM_BASE_URL:-http://localhost:30082}/v1/models")
[ "$vlm_code" = "200" ] && echo "VLM OK" || echo "VLM not reachable (HTTP $vlm_code)"

# LVS: 200 sur /v1/ready, avec retry sur 503 (warmup) pendant ~30s max
LVS=${LVS_BACKEND_URL:-http://localhost:38111}
lvs_code=000
for i in $(seq 1 10); do
  lvs_code=$(curl -s -o /dev/null -w '%{http_code}' --connect-timeout 3 "$LVS/v1/ready")
  case "$lvs_code" in
    200) echo "LVS OK"; break ;;
    503) sleep 3 ;;                 # warming up; keep polling
    *)   break ;;                   # any other code = not reachable, stop retrying
  esac
done
[ "$lvs_code" = "200" ] || echo "LVS not reachable (HTTP $lvs_code)"

Comment interpréter les résultats :

  • vlm_code = 200 et lvs_code = 200 → routage normal (Étape 2a pour <60s, Étape 2b pour >=60s).
  • vlm_code != 200 → échec ; la summarisation ne peut pas s'exécuter sans le VLM.
  • vlm_code = 200, lvs_code != 200 → LVS est vraiment indisponible ; utilisez le chemin de secours VLM décrit ci-dessus pour les longues vidéos.
  • Un code LVS non-200 après la boucle de retry est le SEUL signal que LVS est indisponible. La stdout vide, les champs JSON manquants ou un corps de réponse « bizarre » ne sont PAS « indisponibles ».

Étape 1 — Résolvez la vidéo vers une URL de clip (déléguez à vios)

Utilisez la skill vios pour toutes les interactions VIOS — elle possède les recettes curl canoniques, les valeurs par défaut des paramètres et les flux de suppression/chargement. Ne fabriquez pas d'URLs et n'exécutez pas les appels VIOS à la main ici ; elles vont dériver.

De vios, vous avez besoin de exactement trois choses pour la summarisation :

  1. streamId pour la vidéo (via sensor/listsensor/<id>/streams, ou directement à partir d'une réponse de chargement).
  2. Timeline{startTime, endTime} pour le stream, ISO 8601 UTC. endTime - startTime est la durée qui détermine la décision de routage ci-dessous. Calculez toujours ; ne supposez jamais.
  3. URL temporaire de clip MP4 — la variante /storage/file/<streamId>/url avec container=mp4. Le VLM et LVS ont tous deux besoin d'une URL HTTP(S) qu'ils peuvent GET ; la variante /url est préférable aux octets de streaming via le client de summarisation. Champ de réponse : .videoUrl.

Tout le reste (auth, gestion des erreurs, chargement, disableAudio, expiration, etc.) est couvert dans la skill vios — consultez les utilisateurs là si l'étape VIOS échoue.


Étape 2a — Vidéo court (< 60s) → VLM direct

HITL : confirmez d'abord le prompt VLM (OBLIGATOIRE — ne sautez pas)

La procédure complète de confirmation du prompt (questions à poser à l'utilisateur, exemples, gestion des refus) se trouve dans references/hitl-prompts.md. Exécutez toujours cette étape avant d'appeler le VLM.

Appelez le VLM

Une fois que l'utilisateur confirme un prompt, envoyez-le comme la partie text du message VLM. Complétions de chat compatibles OpenAI avec l'URL vidéo intégrée dans le contenu du message :

PROMPT='<confirmed_prompt_from_hitl>'

curl -s -X POST "${VLM_BASE_URL:-http://localhost:30082}/v1/chat/completions" \
  -H "Content-Type: application/json" \
  -d "$(jq -n \
        --arg model "${VLM_NAME:-nvidia/cosmos-reason2-8b}" \
        --arg text "$PROMPT" \
        --arg url "<clip_url_from_vios>" \
        '{
          model: $model,
          temperature: 0.0,
          max_tokens: 1024,
          messages: [{
            role: "user",
            content: [
              {type: "text", text: $text},
              {type: "video_url", video_url: {url: $url}}
            ]
          }]
        }')" | jq -r '.choices[0].message.content'

Réponse : enveloppe standard de chat-completion OpenAI. Le résumé se trouve dans choices[0].message.content.

Notes sur le modèle Cosmos : Cosmos Reason 2 supporte le raisonnement via les blocs <think>...</think><answer>...</answer>. Omettez les instructions de raisonnement si vous voulez un résumé simple. L'échantillonnage d'images et les limites de pixels sont appliqués côté serveur ; aucune préparation côté client n'est requise quand vous passez une video_url.


Étape 2b — Longue vidéo (>= 60s) → Microservice LVS direct

Cette section contient le chemin étroit de summarisation long-vidéo. Pour les champs LVS avancés comme media_info, schema, la sortie structurée, le chevauchement de chunks, les timestamps live stream, les métriques ou la config recommandée, lisez references/lvs-api.md.

HITL : collectez d'abord le scénario et les événements (OBLIGATOIRE — ne sautez pas)

La procédure complète de collecte du scénario/événements se trouve dans references/hitl-prompts.md. Exécutez toujours cette étape avant d'appeler LVS.

Extrayez le résumé et les événements en un seul pipe :

curl -s -X POST "${LVS_BACKEND_URL:-http://localhost:38111}/summarize" \ -H "Content-Type: application/json" \ -d @request.json \ | jq -r '.choices[0].message.content' \ | jq '{video_summary, events}'


Si `video_summary` et `events` reviennent tous les deux vides, le clip ne contient probablement pas les événements demandés — réexécutez avec des `events` ou un `scenario` plus large plutôt que de signaler « pas de contenu ».

**Ajustement :**

- `chunk_duration` (par défaut `10`) — secondes par chunk. Plus petit = timestamps plus fins, plus d'appels VLM. Utilisez `0` pour envoyer la vidéo entière en un seul chunk.
- `num_frames_per_chunk` (par défaut `20`) — images échantillonnées par chunk.
- `seed` (par défaut `1`) — reproductibilité ; changez ou omettez pour avoir de la variété.

---

## Exemples de bout en bout

Supposez que la skill `vios` vous a déjà donné `$CLIP` (URL du clip) et `$DURATION` (secondes) pour la vidéo cible — ces deux valeurs forment le contrat de l'Étape 1.

### Vidéo court (`$DURATION < 60`)

**HITL (obligatoire, avant le curl) :** postez le message de l'Étape 2a, attendez `Submit` (ou une série `/generate` / `/refine` qui se termine par `Submit`), puis réglez `PROMPT` sur le texte confirmé. N'exécutez pas le curl ci-dessous jusqu'à ce que cette confirmation soit arrivée.

```bash
PROMPT='Décrivez en détail ce qui se passe dans cette vidéo,
incluant toutes les personnes visibles, véhicules, équipements, objets,
actions et conditions environnementales.
EXIGENCES DE SORTIE :
[timestamp-timestamp] Description de ce qui se passe.
EXEMPLE :
[0.0s-4.0s] <description du premier événement>
[4.0s-12.0s] <description du deuxième événement>'

curl -s -X POST "${VLM_BASE_URL:-http://localhost:30082}/v1/chat/completions" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --arg url "$CLIP" --arg text "$PROMPT" \
        --arg model "${VLM_NAME:-nvidia/cosmos-reason2-8b}" '{
    model: $model,
    temperature: 0.0,
    max_tokens: 1024,
    messages: [{role:"user", content:[
      {type:"text", text:$text},
      {type:"video_url", video_url:{url:$url}}
    ]}]
  }')" | jq -r '.choices[0].message.content'

Longue vidéo ($DURATION >= 60)

HITL (obligatoire, avant le curl) : postez le message de l'Étape 2b et attendez la réponse de l'utilisateur. Substituez ses valeurs (ou l'opt-in defaults) dans $SCENARIO, $EVENTS_JSON et $OBJECTS_JSON ci-dessous. N'exécutez pas le curl sans cette réponse.

LVS=${LVS_BACKEND_URL:-http://localhost:38111}

# From HITL reply:
SCENARIO='warehouse monitoring'            # ou ce que l'utilisateur a donné
EVENTS_JSON='["notable activity"]'         # jq-compatible JSON array
OBJECTS_JSON=''                            # '' to omit, else '["cars","trucks"]'

# Readiness = HTTP 200 on /v1/ready. Body may be empty — do not inspect it.
# Retry on 503 (warmup) for up to ~30s before concluding LVS is unavailable.
lvs_code=000
for i in $(seq 1 10); do
  lvs_code=$(curl -s -o /dev/null -w '%{http_code}' --connect-timeout 3 "$LVS/v1/ready")
  case "$lvs_code" in 200) break ;; 503) sleep 3 ;; *) break ;; esac
done

if [ "$lvs_code" = "200" ]; then
  curl -s -X POST "$LVS/summarize" \
    -H "Content-Type: application/json" \
    -d "$(jq -n --arg url "$CLIP" \
          --arg model "${VLM_NAME:-nvidia/cosmos-reason2-8b}" \
          --arg scenario "$SCENARIO" \
          --argjson events "$EVENTS_JSON" \
          --argjson objects "${OBJECTS_JSON:-null}" '{
      url: $url,
      model: $model,
      scenario: $scenario,
      events: $events,
      chunk_duration: 10,
      num_frames_per_chunk: 20,
      seed: 1
    } + (if $objects == null then {} else {objects_of_interest: $objects} end)')" \
    | jq -r '.choices[0].message.content' | jq '{video_summary, events}'
else
  echo "⚠️ Note: video is ${DURATION}s long. LVS returned HTTP $lvs_code; falling back to VLM."
  # Fall back to the short-video VLM flow above (which itself requires
  # the Step 2a HITL confirmation before calling the VLM).
fi

Réponses

  • VLM retourne une enveloppe de chat-completion OpenAI ; la chaîne de résumé est choices[0].message.content.
  • LVS retourne la même enveloppe mais content est une chaîne JSON — exécutez jq -r '.choices[0].message.content' | jq pour atteindre {video_summary, events}.
  • Erreurs de VLM/LVS remontent comme HTTP non-2xx plus JSON {error: ...}. 503 de LVS signifie généralement qu'il est toujours en réchauffement — attendez et réessayez v1/ready.

Présenter la sortie à l'utilisateur (IMPORTANT — ne réécrivez pas)

Les réponses VLM et LVS sont le produit final à présenter à l'utilisateur. Présentez-les avec une transformation minimale ; ne paraphrasez pas, ne changez pas de voix, n'ajoutez pas d'emojis, ni ne reformatez en bullets/tables qui n'étaient pas dans la source.

Exactement un appel backend, exactement un rendu. Un prompt confirmé unique (Étape 2a) ou un ensemble unique de scénario/événements confirmé (Étape 2b) correspond à exactement une requête POST /v1/chat/completions ou POST /summarize, et exactement un bloc de sortie pour l'utilisateur. Ne DISTRIBUEZ PAS d'appels parallèles pour couvrir (par ex., un appel pour « scène complète » plus un pour « anomalies »), et ne RENDEZ PAS la même réponse deux fois avec des en-têtes différents. Si l'utilisateur veut une deuxième passe (par ex., « maintenant avec un focus sécurité des incidents »), c'est une nouvelle ronde HITL → un nouvel appel unique → un nouveau rendu unique.

Format de ligne d'en-tête. Commencez la réponse avec exactement un en-tête :

Summary of <video_name> (<duration>)

Utilisez <duration> formaté comme Ns pour les durées inférieures à 60 secondes (par ex. 25s) et Mm Ss pour les durées ≥60 secondes (par ex. 3m 30s). N'incluez jamais le même en-tête deux fois dans des formats différents.

Sortie LVS :

  • video_summary (chaîne) — rendez verbatim comme le résumé narratif. C'est déjà un « Rapport Observationnel » poli, contrôlé en ton ; l'agent qui le réécrit perd de la fidélité (par ex., la voix neutre/formelle du modèle devient la voix par défaut de l'agent, la formulation subtile s'aplatit).
  • events (liste) — rendez chaque événement avec son start_time, end_time, type et la description complète verbatim. Choisissez un format qui rend proprement dans le client actuel ; vous pouvez utiliser un tableau si le client le rend bien, sinon revenez à une liste par événement. Ne raccourcissez ou ne paraphrasez pas description.
  • Vous POUVEZ ajouter un en-tête d'une ligne identifiant la vidéo (par ex. **Summary of <name>** (<duration>, scenario: <scenario>)) et une offre de fermeture pour réexécuter avec des paramètres différents. Vous NE POUVEZ PAS résumer, réordonner ou interpréter le contenu lui-même.

Sortie VLM : choices[0].message.content est déjà la réponse complète de l'assistant — rendez-la verbatim. Si le modèle a produit des blocs <think>...</think><answer>...</answer>, supprimez le bloc <think> et montrez le contenu <answer> (ou le contenu entier si les balises sont absentes).

Avertissement de secours, le cas échéant, va au-dessus de la sortie LVS/VLM, pas mélangé dedans.

Conseils

  • HITL n'est pas optionnel. Chaque summarisation commence par le message HITL (Étape 2a ou 2b). Le sauter pour « être efficace » est le mode de défaillance unique le plus courant de cette skill — ne le faites pas.
  • Disponibilité LVS = HTTP 200 sur /v1/ready. Rien d'autre. Le corps est souvent vide (size=0). NE PIPEZ PAS le contrôle de disponibilité via head, jq, grep ou toute autre commande — bash rapportera le code de sortie du dernier élément du pipe, pas celui de curl, et un corps vide ressemblera à un vrai défaut. Utilisez le motif curl -s -o /dev/null -w '%{http_code}' de Installation → Vérifications de disponibilité textuellement.
  • Déléguez VIOS à vios. Ne fabriquez pas les appels d'URL de clip, timeline ou chargement ici — elles vont dériver des recettes canoniques.
  • La durée est autoritaire. Ne routez pas sur le nom de fichier ou les indices de l'utilisateur ; calculez à partir de la timeline retournée par vios.
  • jq deux fois pour LVS. D'abord déplie l'enveloppe de style OpenAI, deuxièmement analyse la chaîne JSON dans content.
  • Ne réécrivez pas la sortie LVS / VLM. Le video_summary de LVS et choices[0].message.content du VLM sont les livrables. Rendez-les verbatim ; ne paraphrasez pas dans votre propre voix ni ne reformatez. Voir Réponses → Présenter la sortie à l'utilisateur.
  • Un appel, un rendu. Un HITL confirmé → une requête backend → un bloc de sortie. Pas de couverture parallèle, pas de rendus dupliqués avec des en-têtes différents.

Référence croisée

  • deploy — mettez en place le profil base (VLM seul) ou lvs (VLM + LVS MS)
  • vios (API VIOS) — chargez des vidéos, listez les streams, obtenez les URLs de clips
  • video-search — recherche sémantique à travers l'archive (profil différent)
  • video-analytics — requête incidents/événements depuis Elasticsearch
  • Référence API LVSreferences/lvs-api.md
  • Référence opérations service LVSreferences/deploy-lvs-service.md

Skills similaires