About vss-summarize-video
A skill for running the Video Search & Summarization app's summary pipeline through its Pipeline Manager. It helps operators submit video jobs, track stage-level progress, retrieve final or per-frame summaries, and manage pipeline records.
Key features
- 01Bootstrap or reuse the VSS app checkout
- 02Submit video summarization jobs
- 03Track pipeline stages until completion
- 04Retrieve final and per-frame summaries
- 05List and delete summary jobs
Use cases
- 01Summarize an uploaded or existing video
- 02Check whether a video summary is complete
- 03Review captions for individual frames
- 04Remove completed summary records
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ open-edge-platform/ edge-ai-libraries --skill vss-summarize-videoAfter installing, type/vss-summarize-videoin Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
benchmark/benchmark.mdevals/evals.jsonexample-prompts/01-basic-summary.md02-custom-sampling.md03-audio-transcript.md04-per-chunk-captions.md05-manage-summaries.md06-bootstrap-fresh-machine.md07-bootstrap-fresh-machine.mdREADME.mdreferences/summary-request.mdscripts/vss-bootstrap.shDetails
From the project READMEVSS Summarize
Run the summarization pipeline via the Pipeline Manager. Run the curl commands
yourself and relay results. Endpoints use the nginx /manager prefix.
Set HOST=http://${HOST_IP:-localhost}:${APP_HOST_PORT:-12345}.
Environment setup (run first)
This skill drives the Video Search & Summarization app through its real source files, so the VSS application must be present and you must run commands from its app root. Do this before anything else, and it works whether or not the VSS source is already in your workspace.
Run the bundled bootstrap. It first tries to find an existing VSS checkout -
walking up from the current directory and inspecting the enclosing git repo - and
reuses it without ever re-cloning. Only when no checkout is found does it do a
shallow, single-branch, sparse checkout of just
sample-applications/video-search-and-summarization from main. It prints the
resolved app root on stdout:
# SKILL_DIR is THIS skill's own directory (shown to you when the skill loads);
# in-repo it is .github/skills/vss-summarize-video. Works the same if the skill is installed standalone.
SKILL_DIR=".github/skills/vss-summarize-video"
APP_ROOT="$(bash "$SKILL_DIR/scripts/vss-bootstrap.sh")"
cd "$APP_ROOT"
Every command below assumes the working directory is this APP_ROOT. To pull
from a fork/branch or reuse a specific checkout dir, override VSS_REPO_URL,
VSS_REPO_BRANCH, or VSS_CLONE_DIR before running it.
Preconditions
- Backend healthy and summary enabled - probe first; if not, use
vss-troubleshoot/vss-deploy:curl -sf "$HOST/manager/health" >/dev/null && \ curl -s "$HOST/manager/app/features" | jq '.summary // .' # confirm summary capability - A
videoIdto summarize - upload one withPOST /manager/videos(multipart, fieldvideo), which returns{ "videoId": "…" }. Or list existing - note the response is an object{ "videos": [...] }, not a bare array, andnameis a generated hash (the real filename is inurl/dataStore.fileName):curl -s -X POST "$HOST/manager/videos" -F "video=@/path/to/clip.mp4" | jq . curl -s "$HOST/manager/videos" | jq '.videos[] | {videoId, file: .dataStore.fileName}'
1. Start the summary pipeline
POST /manager/summary. The body has required fields; missing any of
title, sampling.*, or evam.evamPipeline returns 400. See
references/summary-request.md for the full
schema, prompt overrides, and audio options.
Minimal valid request:
curl -s -X POST "$HOST/manager/summary" \
-H 'Content-Type: application/json' \
-d '{
"title": "Loading dock review",
"videoId": "<VIDEO_ID>",
"sampling": { "chunkDuration": 20, "samplingFrame": 5, "frameOverlap": 0, "multiFrame": 5 },
"evam": { "evamPipeline": "object_detection" },
"produceFinalSummary": true
}' | jq .
# → { "summaryPipelineId": "<STATE_ID>" }
Sampling constraint: the Pipeline Manager enforces
multiFrame == frameOverlap + samplingFrame. WithframeOverlap: 0, setmultiFrame == samplingFrame. Mismatch → 400 "Multi frame mismatch".evamPipelineis one ofobject_detection|video_ingestion.
2. Poll until complete
The returned summaryPipelineId is the stateId. GET /manager/summary/{stateId}
has no top-level status/progress field (only /raw does) - progress lives in
per-stage fields:
STATE_ID=<STATE_ID>
curl -s "$HOST/manager/summary/$STATE_ID" | jq '{
chunking: .chunkingStatus, # string, "complete" when chunked
frames: .frameSummaryStatus, # COUNTS object: {complete, inProgress, na, ready}
video: .videoSummaryStatus, # string: "na" → "inProgress" → "complete" ← real done signal
audio: .audioTranscriptSummaryStatus,
summary_len: (.summary | length)
}'
⚠️ Completion is
videoSummaryStatus == "complete", NOTsummarybeing non-empty. The finalsummarytext is streamed in incrementally whilevideoSummaryStatusis still"inProgress", so polling on "summary length > 0" returns a truncated, mid-sentence result. Always gate onvideoSummaryStatus. WithproduceFinalSummary: falsethere is no final stage - gate onframeSummaryStatus.inProgress == 0instead.
until curl -s "$HOST/manager/summary/$STATE_ID" \
| jq -e '.videoSummaryStatus == "complete"' >/dev/null; do sleep 10; done
Summarization is slow (VLM per-chunk + LLM map-reduce) - minutes, not seconds.
3. Retrieve the summary
curl -s "$HOST/manager/summary/$STATE_ID" | jq -r '.summary' # final map-reduced summary
# Per-chunk captions live in .frameSummaries[] (each: frameKey, status, summary).
# NOT in .chunks[] - those only carry {chunkId, duration, audioTranscripts}:
curl -s "$HOST/manager/summary/$STATE_ID" | jq -r '.frameSummaries[] | "[\(.frameKey)] \(.summary)"'
curl -s "$HOST/manager/summary/$STATE_ID/raw" | jq . # everything (audio, frames, status, …)
Present the final summary text; offer the per-chunk detail if useful. Audio with
no speech yields an audioTranscriptSummary that says so - not an error.
Manage
curl -s "$HOST/manager/summary" | jq '.[] | {stateId, title}' # list all
curl -s -X DELETE "$HOST/manager/summary/$STATE_ID" # delete one