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