--- name: metro-ai-app-recipe description: >- Stand up a complete, ready-to-run computer-vision analytics stack on Intel hardware with one Docker Compose command — point it at your video sources and an OpenVINO/ONNX model to get live annotated WebRTC video plus real-time detection dashboards and alerts (object detection, classification, counting, or zone/line-crossing) for any vertical, with no glue code. See "When to use this skill" for the full component list, trigger conditions, and boundaries. license: Apache-2.0 compatibility: >- Requires Docker + Docker Compose v2, host with Intel CPU (and optionally Intel GPU/NPU with `video`/`render` groups), outbound network access to Docker Hub, ghcr.io, and github.com (for model + sample video downloads). Ports 80 and 443 (Nginx) plus 3478/udp (Coturn TURN) must be free on the host; WebRTC also publishes MediaMTX port 8189 (tcp+udp) for ICE, with signalling proxied via Nginx. Tested with the open-edge-platform Metro Vision AI App Recipe reference (v2026.2.0 image tags). --- # 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](https://github.com/open-edge-platform/edge-ai-suites/tree/main/metro-ai-suite/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](#demopoc-mode) + load [`references/DEMO_POC.md`](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`](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`](references/PROXY_UI.md) | `nginx.conf` proxy (WHEP/WHIP + WebRTC-TCP), Grafana iframe panels, dashboard provisioning, Mosquitto | | [`references/NODE_RED.md`](references/NODE_RED.md) | `flows.json`, MQTT wildcard, `gva_meta` probe, alert flow | | [`references/INSTALL.md`](references/INSTALL.md) | file layout, `.env`, `validate_env.sh` + rules, `install.sh`, `docker-compose.yml` volumes | | [`references/TESTS.md`](references/TESTS.md) | `conftest.py`, `test_webrtc_stream.py`, assertion contracts | | [`references/SCENESCAPE.md`](references/SCENESCAPE.md) | **`{{SCENESCAPE}}=yes` only** — multi-camera scene-fusion via `scenescape-setup` skill | | [`references/DEMO_POC.md`](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](references/DEMO_POC.md)); `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 ` + `_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 ``/`_gpu`/`_npu`; topic `{{DETECTIONS_TOPIC_PREFIX}}_X/` | | `{{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](references/NODE_RED.md)) | | `{{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](references/SCENESCAPE.md)) | | `{{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](#demopoc-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](references/PIPELINE.md)). 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`](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`](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`](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 `