Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
eeda7e9
Add inference runtime API design proposal
jarcherNV Jul 30, 2026
b6b7d55
Add experimental inference runtime API envelope (#403)
jarcherNV Aug 4, 2026
d107043
WIP Implement T2, T3, and part of T4 from API refactor plan (#413)
aidanfnv Aug 5, 2026
2d2e5b3
Clarify global conditioning input semantics
aidanfnv Aug 6, 2026
c0ec431
Rename misleading `semantic_type` to `input_modality`
aidanfnv Aug 6, 2026
33a84fe
Correct disagreements in docs from PR #413
aidanfnv Aug 6, 2026
1b7bac6
Fix input canonicalization in tests
aidanfnv Aug 6, 2026
c78920d
Add experimental inference runtime and shared demo API (#422)
jarcherNV Aug 6, 2026
8068c8e
Port Lingbot to new API (#425)
aidanfnv Aug 7, 2026
c9705f4
Unify video output stream handling
gtong-nv Aug 7, 2026
31fc0c9
Unify WebRTC demo viewer
gtong-nv Aug 7, 2026
20f9210
Unify WebRTC UI
gtong-nv Aug 7, 2026
6d14cf9
Continue Lingbot porting after webRTC refactor (#428)
aidanfnv Aug 8, 2026
209f936
Unify serving paths (#429)
gtong-nv Aug 8, 2026
a7ea381
Restore shared WebRTC manager input helpers (#432)
jarcherNV Aug 8, 2026
6239fdc
unify demo runtime through OmniDreams migration (#433)
jarcherNV Aug 10, 2026
23f182c
Migrate LingBot demos onto unified runtime (#435)
jarcherNV Aug 11, 2026
b982790
Migrate LingBot demo onto unified runtime (#437)
jarcherNV Aug 11, 2026
c2adab2
Preserve setup-time driver invariants (#439)
jarcherNV Aug 11, 2026
895922d
Unify launch cmd (#438)
gtong-nv Aug 11, 2026
a9853ea
Migrate text2video models to use unified runtime API (#444)
gtong-nv Aug 11, 2026
1d39eb6
Simplify T2V demo defaults
gtong-nv Aug 12, 2026
aaf6a92
Simplify T2V launch delegation
gtong-nv Aug 12, 2026
acfc933
Share demo launch runner defaults
gtong-nv Aug 12, 2026
2c1c100
Share prompt generation WebRTC routes
gtong-nv Aug 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
334 changes: 334 additions & 0 deletions .github/workflows/lingbot-demo-runtime.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,334 @@
name: LingBot Demo Runtime

on:
push:
branches:
- main
- "pull-request/[0-9]+"
paths:
- ".github/workflows/lingbot-demo-runtime.yml"
- "pyproject.toml"
- "uv.lock"
- "flashdreams/pyproject.toml"
- "flashdreams/flashdreams/core/**"
- "flashdreams/flashdreams/infra/**"
- "flashdreams/flashdreams/runtime/**"
- "flashdreams/flashdreams/serving/**"
- "flashdreams/flashdreams/recipes/taehv/**"
- "flashdreams/flashdreams/recipes/wan/**"
- "flashdreams/tests/test_webrtc_*.py"
- "integrations/lingbot/**"
workflow_dispatch:

permissions:
contents: read

jobs:
demo-runtime:
name: null and MP4
runs-on: linux-amd64-gpu-rtxpro6000-latest-2
timeout-minutes: 180
defaults:
run:
shell: bash
container:
image: nvidia/cuda:13.2.1-cudnn-devel-ubuntu24.04
options: --gpus all
env:
UV_PROJECT_ENVIRONMENT: /tmp/flashdreams-venv
UV_LINK_MODE: copy
UV_PYTHON: "3.12"
MAX_JOBS: 8
HF_HOME: /tmp/huggingface
FLASHDREAMS_CACHE_DIR: /tmp/flashdreams-cache
# Streaming avoids the old duplicate merged safetensors cache. CI uses
# the generic reserve so the model-specific 200 GiB first-run budget does
# not reject runners that have enough room for the streamed shards.
FLASHDREAMS_MIN_CACHE_FREE_GB: "20"
ARTIFACT_DIR: artifacts/lingbot_demo_runtime
PRESET_ID: lingbot-world-v2-14b-causal-fast-taehv-window15-sink3
BLOCKS: "5"
FPS: "16"
WIDTH: "640"
HEIGHT: "352"
EXPECTED_WIDTH: "640"
EXPECTED_HEIGHT: "352"
MIN_DURATION_SECONDS: "3"
MAX_DURATION_SECONDS: "5"
steps:
- name: Detect GPU architecture
id: gpu-arch
run: |
nvidia-smi
compute_cap=$(nvidia-smi --query-gpu=compute_cap --format=csv,noheader 2>/dev/null | head -1 | tr -d '[:space:]')
arch=$(echo "${compute_cap}" | tr -d '.')
echo "arch=${arch}" >> "$GITHUB_OUTPUT"
echo "Detected GPU compute capability: ${compute_cap} -> sm_${arch}"

- name: Checkout
uses: actions/checkout@v4

- name: Install system dependencies
run: |
apt-get update -qq
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq --no-install-recommends \
python3 python3-dev python3-venv \
ffmpeg \
gcc g++ ninja-build \
libnccl-dev \
curl git ca-certificates jq unzip
rm -rf /var/lib/apt/lists/*

- name: Setup proxy cache
uses: nv-gha-runners/setup-proxy-cache@main

- name: Setup uv
uses: astral-sh/setup-uv@v6
with:
enable-cache: true
cache-suffix: "lingbot-demo-runtime-sm${{ steps.gpu-arch.outputs.arch }}"
prune-cache: false

- name: Install dependencies
env:
NVTE_CUDA_ARCHS: ${{ steps.gpu-arch.outputs.arch }}
run: |
uv venv --clear
uv sync --locked --package flashdreams-lingbot --no-dev

- name: Verify GPU availability
run: nvidia-smi

- name: Run LingBot demo modes
env:
HF_TOKEN: ${{ secrets.HF_TOKEN }}
run: |
set -uo pipefail

log_dir="${ARTIFACT_DIR}/logs"
output_dir="${ARTIFACT_DIR}/outputs"
summary="${ARTIFACT_DIR}/summary.md"
status_file="${ARTIFACT_DIR}/command-status.env"
mkdir -p "${log_dir}" "${output_dir}"
: > "${status_file}"

ldemo() {
uv run --no-sync --package flashdreams-lingbot lingbot-demo "$@"
}

run_demo() {
local name="$1"
shift
local log="${log_dir}/${name}.log"

{
printf '$'
printf ' %q' "$@"
printf '\n\n'
"$@"
} 2>&1 | tee "${log}"

local rc="${PIPESTATUS[0]}"
echo "${name}=${rc}" >> "${status_file}"
echo "${name} exit code: ${rc}" | tee -a "${summary}"
return 0
}

{
echo "# LingBot Demo Runtime CI"
echo
echo "| Mode | Expected blocks | Output |"
echo "| --- | ---: | --- |"
echo "| null | ${BLOCKS} | none |"
echo "| MP4 | ${BLOCKS} | lingbot-demo-replay.mp4 |"
echo
echo "## Command Status"
} > "${summary}"

run_demo null \
ldemo replay \
--device cuda:0 \
--preset-id "${PRESET_ID}" \
--example-idx 0 \
--total-blocks "${BLOCKS}" \
--fps "${FPS}" \
--pixel-height "${HEIGHT}" \
--pixel-width "${WIDTH}" \
--output-mode null

run_demo mp4 \
ldemo replay \
--device cuda:0 \
--preset-id "${PRESET_ID}" \
--example-idx 0 \
--total-blocks "${BLOCKS}" \
--fps "${FPS}" \
--pixel-height "${HEIGHT}" \
--pixel-width "${WIDTH}" \
--output "${output_dir}/lingbot-demo-replay.mp4"

- name: Validate LingBot demo artifacts
run: |
set -euo pipefail

log_dir="${ARTIFACT_DIR}/logs"
output_dir="${ARTIFACT_DIR}/outputs"
probe_dir="${ARTIFACT_DIR}/ffprobe"
summary="${ARTIFACT_DIR}/summary.md"
status_file="${ARTIFACT_DIR}/command-status.env"
mkdir -p "${probe_dir}"

status_of() {
awk -F= -v name="$1" '$1 == name { print $2 }' "${status_file}"
}

assert_exit_zero() {
local name="$1"
local rc
rc="$(status_of "${name}")"
if [ "${rc}" != "0" ]; then
echo "${name} command failed with exit code ${rc}" >&2
exit 1
fi
}

assert_clean_log() {
local log="$1"
if grep -En "ERROR|Traceback|Exception|status=failed|Run failed|failed run" "${log}"; then
echo "failure marker found in ${log}" >&2
exit 1
fi
}

assert_log_contains() {
local log="$1"
local pattern="$2"
local label="$3"
if ! grep -Eq "${pattern}" "${log}"; then
echo "expected ${label} in ${log}" >&2
exit 1
fi
}

assert_log_not_contains() {
local log="$1"
local pattern="$2"
local label="$3"
if grep -Eq "${pattern}" "${log}"; then
echo "unexpected ${label} in ${log}" >&2
exit 1
fi
}

validate_mp4() {
local mode="$1"
local mp4="$2"
local metadata="${probe_dir}/${mode}.json"

if [ ! -s "${mp4}" ]; then
echo "expected non-empty MP4 at ${mp4}" >&2
exit 1
fi

ffprobe \
-v error \
-select_streams v:0 \
-show_entries stream=width,height,r_frame_rate,avg_frame_rate,nb_frames,duration:format=duration \
-of json \
"${mp4}" > "${metadata}"

local stream_count width height duration
stream_count="$(jq '.streams | length' "${metadata}")"
width="$(jq -r '.streams[0].width // ""' "${metadata}")"
height="$(jq -r '.streams[0].height // ""' "${metadata}")"
duration="$(jq -r '.streams[0].duration // .format.duration // "0"' "${metadata}")"

if [ "${stream_count}" -lt 1 ]; then
echo "ffprobe found no video stream in ${mp4}" >&2
exit 1
fi

if [ "${width}" != "${EXPECTED_WIDTH}" ] || [ "${height}" != "${EXPECTED_HEIGHT}" ]; then
echo "unexpected ${mode} resolution ${width}x${height}; expected ${EXPECTED_WIDTH}x${EXPECTED_HEIGHT}" >&2
exit 1
fi

awk \
-v duration="${duration}" \
-v min_duration="${MIN_DURATION_SECONDS}" \
-v max_duration="${MAX_DURATION_SECONDS}" \
'BEGIN {
if ((duration + 0) < min_duration || (duration + 0) > max_duration) {
exit 1
}
}' || {
echo "unexpected ${mode} duration ${duration}s; expected ${MIN_DURATION_SECONDS}-${MAX_DURATION_SECONDS}s" >&2
exit 1
}
}

null_log="${log_dir}/null.log"
mp4_log="${log_dir}/mp4.log"

assert_exit_zero null
assert_exit_zero mp4

assert_clean_log "${null_log}"
assert_clean_log "${mp4_log}"

assert_log_contains "${null_log}" "Streaming sharded safetensors checkpoint" "null streaming checkpoint load"
assert_log_contains "${null_log}" "Finished streaming .* safetensors shard" "null streamed checkpoint completion"
assert_log_contains "${null_log}" "AR 4 encode" "null final AR block"
assert_log_contains "${null_log}" "Lingbot runtime step 4 frames=" "null final replay step"
assert_log_not_contains "${null_log}" "Loading merged sharded checkpoint from cache|Saved merged sharded checkpoint" "merged safetensors cache usage"

assert_log_contains "${mp4_log}" "Streaming sharded safetensors checkpoint" "MP4 streaming checkpoint load"
assert_log_contains "${mp4_log}" "Finished streaming .* safetensors shard" "MP4 streamed checkpoint completion"
assert_log_contains "${mp4_log}" "AR 4 encode" "MP4 final AR block"
assert_log_contains "${mp4_log}" "Lingbot runtime step 4 frames=" "MP4 final replay step"
assert_log_not_contains "${mp4_log}" "Loading merged sharded checkpoint from cache|Saved merged sharded checkpoint" "merged safetensors cache usage"

validate_mp4 mp4 "${output_dir}/lingbot-demo-replay.mp4"

{
echo
echo "## Validation"
echo
echo "- Null and MP4 commands exited zero."
echo "- Logs contained expected streaming-checkpoint and final AR-step markers."
echo "- Logs did not contain merged-safetensors cache markers."
echo "- MP4 output was non-empty and passed ffprobe stream checks."
} >> "${summary}"

- name: Trim uv cache for upload
if: always()
run: |
cache_dir="${UV_CACHE_DIR:-/github/home/.cache/uv}"
echo "=== Cache size before trim ==="
du -sh "${cache_dir}" || true
du -sh "${cache_dir}"/*/ 2>/dev/null || true

rm -rf "${cache_dir}/wheels-v6"
rm -rf "${cache_dir}/archive-v0"

find "${cache_dir}/git-v0/checkouts" \
\( -name "build" -o -name "*.egg-info" -o -name "__pycache__" \) \
-type d -exec rm -rf {} + 2>/dev/null || true

rm -rf "${cache_dir}/sdists-v9/editable"

echo ""
echo "=== Cache size after trim ==="
du -sh "${cache_dir}" || true
du -sh "${cache_dir}"/*/ 2>/dev/null || true
echo ""
echo "=== Cached built wheels (sdists-v9) ==="
find "${cache_dir}/sdists-v9" -name "*.whl" -exec ls -lh {} \; 2>/dev/null || true

- name: Upload demo runtime artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: lingbot-demo-runtime
path: ${{ env.ARTIFACT_DIR }}
if-no-files-found: ignore
Loading