SuperAPI AI Directory

metro-ai-app-recipe

Deployment & operations

View repository

Updated 10-28 00:23

About this skill

Metro AI App Recipe — DLSPS + WebRTC + Mosquitto + Node-RED + Grafana + Nginx

Build an end-to-end {{OBJECT}}-analytics stack on Intel hardware in ./{{STACK_DIR}}/ with Docker Compose. Vertical-agnostic: the same seven-container topology serves any DL Streamer / OpenVINO CV pipeline — only the model, class filter, alert rule, dashboard, and topics differ. Follows the open-edge-platform Metro Vision AI App Recipe MediaMTX + Coturn + WebRTC path, streamlined (no Prometheus/OTel). Scenescape is off by default (opt-in multi-camera analysis). Data-flow and routing are detailed in Reference architecture below.

When to use this skill

Use when building an object-detection, classification, object-counting, or zone/line-crossing alerting pipeline for any vertical (see table below). Optionally adds a Scenescape multi-camera path, or a lightweight demo/PoC single app when no full stack is needed.

Not for: non-Intel or cloud-only deployments, Prometheus/OpenTelemetry metrics stacks, or training/exporting models.

Supported verticals & use-cases

Vertical Example use-cases (each = one invoking prompt)
Smart city / ITS person/vehicle detection, ANPR, smart-parking, wrong-way
Retail customer counting, queue-length, shelf out-of-stock, dwell-time
Industrial / logistics surface-defect, PPE compliance, zone intrusion, forklift tracking
Healthcare / facilities fall detection, hand-hygiene, occupancy, perimeter intrusion
Custom any OpenVINO IR / ONNX detector + optional classifier

The invoking prompt maps its vertical to concrete {{OBJECT}}, {{PIPELINE_NAME}}, {{DEFAULT_MODEL}}, {{DEFAULT_RULE}}, {{DASHBOARD_SLUG}} — nothing else changes.

How to use this skill

  1. Read this file end-to-end.
  2. Ask Question 0 (packaging) first. If demo/function/port (single-app path), branch to Demo/PoC mode + load references/DEMO_POC.md, skip questions 1–7; else (microservice, the default full stack) continue.
  3. Ask the 7 questions in ONE batched message (defaults in brackets); accept go/defaults/empty. Question 7 selects the Scenescape path.
  4. Run parameter validation (below); refuse to proceed on any failure.
  5. Load reference file(s) on demand — not all up front (per the Reference files table): Scenescape when {{SCENESCAPE}}=yes; PIPELINE for GPU/NPU, RTSP//dev/video, or classifier; NODE_RED for </<=/>= rules or a non-empty {{CLASS_FILTER_IDS}}.
  6. Verify against completion criteria before declaring success (record throughput/latency vs benchmark.md); validate_env.sh is step 0 of install.sh.

Reference files (load on demand)

File Load when authoring
references/PIPELINE.md DLSPS config.json, GPU/NPU variants (_gpu/_npu + group_add), input sources (file/RTSP/device), gvaclassify wiring, REST launcher, watchdog
references/PROXY_UI.md nginx.conf proxy (WHEP/WHIP + WebRTC-TCP), Grafana iframe panels, dashboard provisioning, Mosquitto
references/NODE_RED.md flows.json, MQTT wildcard, gva_meta probe, alert flow
references/INSTALL.md file layout, .env, validate_env.sh + rules, install.sh, docker-compose.yml volumes
references/TESTS.md conftest.py, test_webrtc_stream.py, assertion contracts
references/SCENESCAPE.md {{SCENESCAPE}}=yes only — multi-camera scene-fusion via scenescape-setup skill
references/DEMO_POC.md single-app packaging only ({{PACKAGING}}∈demo/function/port) — lightweight single-app path (DL Streamer or OpenVINO); no full stack

Parameters (from invoking prompt)

Param Purpose
{{PACKAGING}} demo | function | microservice | port (default microservice). demo/function/port = single-app path (DEMO_POC); microservice = full stack (rows below are microservice-only)
{{MODE}} Back-compat alias derived from {{PACKAGING}}: demo (single-app) | production (= microservice)
{{HW_TARGET}} Intel inference device: CPU|GPU|NPU|AUTO (default CPU; AUTO = pick). Drives sample_start.sh <cpu|gpu|npu> + _gpu/_npu variants; no platform/generation naming
{{OBJECT}} class label in dashboard/alerts (e.g. person, vehicle, defect, fall); any MQTT/Grafana-safe string
{{STACK_DIR}} e.g. person-detect-stack, ppe-compliance-stack, anpr-stack
{{DEFAULT_MODEL}}, {{OTHER_MODELS}} allowed model options; when auto, suggest from OpenVINO / Intel / Metro Analytics Catalog HF collections per a performance goal
{{PIPELINE_NAME}} canonical DLSPS pipeline name (e.g. yolov11s); variants <name>/_gpu/_npu; topic {{DETECTIONS_TOPIC_PREFIX}}_X/<name>
{{CLASSIFIER}} secondary model or none; if set, also {{CLASSIFIER_URL}} + {{CLASSIFIER_XML}}
{{CLASS_FILTER_IDS}} JSON array of class IDs to keep ([]=all). Filtered in Node-RED
{{DEFAULT_RULE}} e.g. count>2 in 10s; parses to {{RULE_OP}}∈>,>=,<,<=, {{RULE_N}}, {{RULE_WINDOW_S}} (see NODE_RED)
{{RULE_SCOPE}} per-source | aggregate (default per-source)
{{ALERT_TOPIC}} e.g. alerts/{{OBJECT}}
{{DETECTIONS_TOPIC_PREFIX}} e.g. object_detection (per-source _1, _2, …)
{{COUNT_TOPIC}} e.g. stats/{{OBJECT}}_count
{{LABEL_RULE_NOTE}} model-specific classification note for Node-RED
{{DASHBOARD_SLUG}} e.g. smart-parking
{{NUM_SOURCES}} default 4
{{SCENESCAPE}} yes | no (default no). yes = multi-camera path (SCENESCAPE)
{{SCENE_NAME}} (Scenescape only) scene name, e.g. intersection-1
{{CAMERA_IDS}} (Scenescape only) unique IDs (no /), one per input stream, in input order
{{TURN_USER}}, {{TURN_PASS}} Coturn / MediaMTX ICE credentials (default turnuser / a generated secret)

Questions (single batched prompt)

Question 0 — Packaging [microservice]: demo/function/port (single-app: PoC, one-shot job, or migrate a pipeline) or microservice (full stack, default). For demo/function/port, STOP → follow Demo/PoC mode; skip questions 1–7 (microservice only).

  1. Model [{{DEFAULT_MODEL}}] (also: {{OTHER_MODELS}}; or auto — give a performance goal, I'll suggest one from the OpenVINO / Intel / Metro Analytics Catalog HF collections via model-download)
  2. Classifier [{{CLASSIFIER}}] (or none)
  3. Target hardware [CPU] — Intel CPU/GPU/NPU/AUTO (I pick). Multi-vendor noted as suggestions only; no platform/generation naming.
  4. Inputs [{{NUM_SOURCES}}× sample-video] (or RTSP URLs / /dev/videoN / local paths); sets INPUT_TYPE. RTSP/device are continuous → no sample-video download, no file:// watchdog (see PIPELINE).
  5. Node-RED rule [{{DEFAULT_RULE}}, {{RULE_SCOPE}}]
  6. Alert channel [MQTT {{ALERT_TOPIC}}]
  7. Scenescape multi-camera spatial analysis? [{{SCENESCAPE}}, default no] (if yes, also collect {{SCENE_NAME}} + one unique {{CAMERA_IDS}} per input stream → references/SCENESCAPE.md)

Parameter validation (enforce BEFORE install.sh runs)

Ship validate_env.sh and call it as step 0 of install.sh; reject on any failure. The script body and full validation rules table (PACKAGING/MODE, HOST_IP, NUM_SOURCES, HW_TARGET/DEVICE, PIPELINE_NAME, topics, TURN creds, inputs, Scenescape params, …) are in references/INSTALL.md; validate {{PACKAGING}} ∈ demo/function/microservice/port and {{HW_TARGET}} ∈ CPU/GPU/NPU/AUTO.

Reference architecture

Single Compose network app_network. Nginx publishes 80/443; Coturn also publishes 3478/udp (WebRTC TURN). Nginx reverse-proxies DLSPS REST, Grafana, Node-RED, and MediaMTX WHEP/WHIP signalling (ICE 8189) — full route map in references/PROXY_UI.md. Data: DLSPS→MQTT→Mosquitto→Node-RED→Grafana; DLSPS→WHIP→MediaMTX (peer-id {{DETECTIONS_TOPIC_PREFIX}}_N, ICE/TURN via Coturn); Grafana embeds <iframe src="/mediamtx/{{DETECTIONS_TOPIC_PREFIX}}_N/">.

Demo/PoC mode

When Question 0 selects demo/function/port (single-app packaging), do not build the full stack — produce one lightweight app proving a model runs on Intel HW (DL Streamer app or minimal OpenVINO script). Follow references/DEMO_POC.md (load only on this branch); production criteria (1–11) do not apply.

Scenescape spatial-analysis path (optional, {{SCENESCAPE}}=yes)

When Question 7 selects Scenescape, branch: keep the DLSPS detection pipeline but replace the MediaMTX/WebRTC + Node-RED + Grafana-MQTT tail with an Intel® Scenescape multi-camera scene-fusion stack. Do not re-implement by hand — delegate to scenescape-setup, passing {{SCENE_NAME}} + {{CAMERA_IDS}}. Architecture, images, validation, criteria in references/SCENESCAPE.md; load only on this branch. Default ({{SCENESCAPE}}=no) path is unchanged.

Images — pin to the latest available tag (never :latest)

Resolve each image to the newest published stable tag on Docker Hub (query the repo's tags API with ordering=last_updated), pin it, and ignore *-weekly pre-releases. Grafana is the one exception: keep it pinned to 11.5.4 (the MQTT datasource plugin only works on that tag).

  • intel/dlstreamer-pipeline-server:2026.2.0-ubuntu24
  • eclipse-mosquitto:2.1.2-alpine
  • nodered/node-red:5.0.4
  • nginx:1.31.3-alpine
  • bluenviron/mediamtx:1.20.0 (WebRTC: WHIP in, WHEP out)
  • coturn/coturn:4.17.0 (ICE/TURN)
  • grafana/grafana:11.5.4 (pinned — do not upgrade) with GF_INSTALL_PLUGINS="grafana-mqtt-datasource 1.3.3,yesoreyeram-infinity-datasource 3.11.1" — a bad plugin version kills the container → Nginx 502
  • intel/dlstreamer:2026.2.0-ubuntu24 (one-shot in install.sh: model dl + INT8 quantize + TLS cert)

Layout (flat)

Generate a flat {{STACK_DIR}}/: README.md, docker-compose.yml, .env, validate_env.sh, install.sh, sample_*.sh/update_dashboard.sh, a src/ tree (per-service dirs), and tests/. Full annotated tree in references/INSTALL.md.

README.md (required content)

The generated README.md MUST document, at minimum:

  • Architecture — the data + video flow from Reference architecture above; include the ASCII diagram + seven containers.
  • Quick start — ./install.sh → docker compose up -d → ./sample_start.sh <cpu|gpu|npu>, plus stop/status scripts.
  • Access URLs + credentials — dashboard https://<HOST_IP>/grafana/ (Grafana login admin/admin, change on first login), DLSPS REST https://<HOST_IP>/api/pipelines, WHEP https://<HOST_IP>/mediamtx/{{DETECTIONS_TOPIC_PREFIX}}_N/.
  • Configuration — key .env values (HOST_IP, GIDs, TURN creds) + how to swap the video source for RTSP.

Template variable substitution

Substitute every {{VAR}} with its concrete value BEFORE writing any file — a literal {{...}} left in nginx.conf, config.json, flows.json, the dashboard JSON, or a test is a syntax error.

Execution guardrails

  • Hard timeouts: model dl+INT8 300 s; video dl 120 s/file; compose pull 300 s; compose up -d 120 s + 180 s healthy; each pytest 60 s.
  • Max 2 retries per step, then STOP and print last 30 log lines from the failing container. Never loop.
  • Before compose up: ss -ltn shows :80/:443 free, ss -lun shows :3478 free (Coturn TURN).
  • Bypass host proxy for all localhost/LAN curl — corporate proxies route https://localhost/... through an unreachable proxy (→ Could not resolve host / 502). Every curl in sample_*.sh MUST use --noproxy '*' + --cacert src/nginx/ssl/server.crt (generated by install.sh); tests set NO_PROXY=* in conftest.py.
  • Test WebRTC signalling with the criterion-7 curl (expect 200; stream exists only after sample_start.sh). Test MQTT via docker run --rm --network <project>_app_network eclipse-mosquitto:2.1.2-alpine mosquitto_sub -h broker -t '#' -v (tag 2.1.2-alpine is an intentional pin — never :latest).
  • pytest venv at ./.venv inside stack dir (python -m venv .venv) — system pip is PEP-668 blocked; /tmp may be noexec.

Optional external skills

If available, invoke; otherwise write files from the reference templates.

  • dlstreamer-coding-agent — pipeline JSON (+ single-app DL Streamer app when {{PACKAGING}}∈demo/function/port)
  • dlsps-user — DLSPS deploy/config/REST; default production path (PIPELINE)
  • model-download — OMZ model IR
  • scenescape-setup — only when {{SCENESCAPE}}=yes (SCENESCAPE)

Reference implementation

The upstream smart-parking/src/ recipe uses the same path — consult it for config.json, mosquitto.conf, nginx.conf, datasources.yml, dashboards.yml, flows.json shapes; drop prometheus/otel-collector/metrics-manager.

Completion criteria (all must pass)

When {{SCENESCAPE}}=yes, criteria 3–11 are superseded by the Scenescape criteria in references/SCENESCAPE.md; 1–2 still apply.

  1. ./install.sh succeeds: .env populated; INT8 model + optional classifier IR under src/dlstreamer-pipeline-server/models/…; videos downloaded; TLS cert with SAN.
  2. ./validate_env.sh cpu exits 0 with a valid .env; HOST_IP=127.0.0.1 ./validate_env.sh cpu exits non-zero.
  3. docker compose up -d → all containers running/healthy (incl. mediamtx-server, coturn).
  4. curl --cacert src/nginx/ssl/server.crt https://localhost/api/pipelines/status returns 3 variants.
  5. ./sample_start.sh <cpu|gpu|npu> launches {{NUM_SOURCES}} pipelines; none QUEUED, all RUNNING.
  6. Detections arrive on {{DETECTIONS_TOPIC_PREFIX}}_1..{{NUM_SOURCES}}/{{PIPELINE_NAME}} (or _gpu/_npu) within 30 s.
  7. curl --cacert src/nginx/ssl/server.crt https://localhost/mediamtx/{{DETECTIONS_TOPIC_PREFIX}}_1/ returns 200 (WHEP HTML) once pipelines run; MediaMTX logs show the WHIP publisher connected per peer-id.
  8. Node-RED publishes JSON {{ALERT_TOPIC}} and scalar {{COUNT_TOPIC}} / stats/alert_active / stats/alert_total per {{RULE_SCOPE}}; mosquitto_sub -t '{{COUNT_TOPIC}}/#' -C 1 MUST parse as int() (JSON breaks Grafana plotting).
  9. Grafana at https://localhost/grafana (admin/admin) shows live {{OBJECT}} counts + alert data and {{NUM_SOURCES}} <iframe> WebRTC panels (GF_SECURITY_ALLOW_EMBEDDING=true). MQTT datasource health (/grafana/api/datasources/uid/mqtt_ds/health) MUST return "MQTT Connected" — broker in jsonData.uri (tcp://broker:1883), not url:. Blank-panel / redirect-loop fixes: references/PROXY_UI.md.
  10. pytest -q tests/ passes; pytest --collect-only -q tests/ | tail -1 reports ≥ 9 tests collected (no empty stubs).
  11. Watchdog continuity (file:// sources): after video-length + 30 s, /api/pipelines/status shows {{NUM_SOURCES}} RUNNING (COMPLETED history is fine), WebRTC re-establishes, MQTT still flowing (>{{NUM_SOURCES}} RUNNING = missing watchdog dedup guard).

Final summary — surface the proof (don't just name files)

Graders see only your final message + tool names, not file contents. An expectation counts as met only if you state it and quote the one decisive line in your closing summary — walk every completion criterion plus the proof-point checklist (topology, WebRTC, pinned tags, MQTT/class filter, no literal {{...}}, certs, curl guards, watchdog, rule, classifier, Scenescape) detailed in references/INSTALL.md → Final-summary proof points. A claim with no quoted evidence is treated as unmet.