Documentation Index
Fetch the complete documentation index at: https://docs.cyberwave.com/llms.txt
Use this file to discover all available pages before exploring further.
What are Workflows?
Workflows in Cyberwave let you create automated sequences of robot operations. Connect nodes visually to build complex behaviors without writing procedural code. Workflows can run on the cloud (Celery tasks) or on the edge device depending on the trigger type. Cloud triggers handle schedule, webhook, event, and manual execution. Thecamera_frame trigger runs ML inference directly on the edge — no video leaves the device.
Workflow Components
Nodes
Nodes are the building blocks of workflows. Each node performs a specific action.stub: Nodes are organised into a hybrid robotics + automation taxonomy. The same categories show up in the editor palette and onGET /api/v1/workflows/config(node_categories), so the docs, the API, and the UI all read from the sameWorkflowNodeCategoryenum (seecyberwave-backend/src/lib/node_categories.py). Categories with no nodes today (Perception) are reserved — they appear in the API response but are hidden in the palette until they ship.
Triggers
Start the workflow on an event, schedule, or incoming message: manual, schedule, webhook, event, MQTT, email, camera_frame, alert
Data Sources
Pull snapshots and samples from sensors, cameras, and external systems on demand:
data_sourcePerception
Convert raw signals into reliable observations — reserved for object trackers, sensor fusion, IMU filtering, ASR (no nodes ship today)
Transform & Routing
Reshape, convert, route, multiplex, or fan out data:
json_parser, annotate, anonymizeState & Memory
Store and retrieve memory over time:
create_asset, edit_asset, create_attachment, update_attachment, video_taggerIntelligence
Use models for interpretation, reasoning, or prediction:
call_model (cloud VLM/LLM and edge ML)Decision & Control Flow
Choose what happens next:
conditional, loop; FSM / Behavior Tree / Rule Engine on the roadmapActuation
Execute physical or twin-side actions:
twin_control (Move Twin); future joint / gripper / navigation primitivesIntegration
Talk to external systems and run user-supplied logic:
http_request, send_email, code (edge-only)Observability & Safety
Guard, validate, observe, and alert:
send_alert; validators, watchdogs, e-stop guards, and anomaly detectors are on the roadmapConnections
Connections define the execution flow between nodes:- Sequential: Execute nodes one after another
- Parallel: Execute multiple nodes simultaneously
- Conditional: Branch based on conditions
Connection validation prevents invalid graphs: trigger nodes cannot accept incoming connections, cycles are blocked, and
camera_frame triggers can only connect to call_model nodes.Trigger Types
| Trigger | Where it runs | How it fires |
|---|---|---|
| Manual | Cloud (Celery) | User clicks “Run” in the UI or triggers via SDK/API |
| Schedule | Cloud (Celery) | Cron or interval timer |
| Webhook | Cloud (Celery) | HTTP POST to a webhook URL |
| Event | Cloud (Celery) | Business event matching conditions |
| MQTT | Cloud (Celery) | MQTT message on a topic |
| Zenoh | Edge/local data plane | Zenoh message on a key expression |
| Cloud (Celery) | Incoming email | |
| Camera Frame | Edge device | Every camera frame, locally — never sends video to the cloud |
Creating a Workflow
- Dashboard
- CLI
- Python SDK
Create
Click Create Workflow. Give it a name, optional slug (unique within the workspace), and visibility.
Build
Drag nodes from the palette to the canvas. Connect nodes by dragging from output to input ports.
Configure
Configure each node’s parameters (twin UUID, model, confidence threshold, send-alert metadata, etc.).
Edge Workflows (Camera Frame)
Thecamera_frame trigger is designed for on-device ML inference. The backend generates a Python worker file that runs directly on the edge — raw video frames never leave the device.
How it works
Migrating from emit_event
Older workflows configured implicit alerts via an emit_event block on the call_model node (event_type, severity, emit_mode, cooldown_seconds). That path was removed: emit_event is no longer a schema field, the next save of any call_model node strips leftover emit_event values from parameters and metadata.input_mappings so the saved graph converges to the post-migration shape, and call_model no longer publishes alerts on its own. Pre-migration rows that haven’t been re-saved yet still load — codegen also drops emit_event during compilation, so they ship a worker that runs inference but publishes nothing.
To raise alerts from detections, wire an explicit send_alert node downstream of call_model. The legacy fields map onto existing nodes:
Legacy emit_event field | Replacement |
|---|---|
enabled: false | Don’t add a send_alert node |
event_type / severity | send_alert.parameters.alert_type / severity |
emit_mode: on_enter / on_change | Insert a detection_event_gate between call_model and send_alert |
cooldown_seconds | Insert a timed_condition (mode: debounce, cooldown_s: <seconds>) before send_alert |
Implicit force=True (no UI knob — the legacy path always passed it) | The emitter defaults force=True on a send_alert whose direct upstream is call_model, so the per-frame loop bypasses the backend’s content-hash dedupe and identical consecutive detections still raise distinct alerts. Set send_alert.parameters.force to false to opt back into the dedupe. When a gate or timed_condition sits between call_model and send_alert the heuristic backs off and the long-standing force=False default applies, since the gate already debounces. |
timed_condition wired downstream of call_model without a trailing send_alert is now inert — the legacy implicit-alert path it used to gate is gone. The compiler surfaces a warning on the resulting compilation (the /compile API and cyberwave workflow sync preflight already render it) so the silent-skip case becomes visible instead of looking like a quietly broken alert chain.
Existing workflows continue to load and run inference, but stop emitting alerts until you add the explicit chain — the migration is intentionally a hard break so silent regressions are impossible.
Syncing to the edge
After activating a workflow in the UI, push it to edge devices:sync_workflows command via MQTT. Edge core receives it and immediately pulls the latest worker files from the backend — no need to wait for the periodic cycle.
stub: You can also trigger the samesync_workflowsMQTT command from the twin editor in the dashboard via the Sync workflow action (next to Restart edge core). The dashboard callsPOST /api/v1/twins/{uuid}/sync-workflows, which publishes the same MQTT command as the CLI oncyberwave/twin/{twin_uuid}/command.
stub: The CLI derives the MQTT topic prefix (The edge device also syncs automatically on boot and periodically (default ~5 min, configurable viadev,staging, empty for production) fromCYBERWAVE_ENVIRONMENT/credentials and cross-checks it against the broker host before publishing. A mismatch (e.g. dev broker with production prefix) aborts with a clear error instead of silently shipping the command into a topic edge-core never subscribed to. Override withCYBERWAVE_MQTT_TOPIC_PREFIXif you really need to.
CYBERWAVE_WORKER_SYNC_INTERVAL_LOOPS).
Execution Modes
Workflows can be triggered by:| Trigger | Description |
|---|---|
| Manual | Run on demand from the dashboard or SDK |
| Schedule | Run at specific times (cron) |
| Events | Run when sensor data matches conditions |
| API | Trigger from external systems via REST or MCP |
| Camera Frame | Run on every camera frame at the edge device |
Monitoring Executions
Track workflow execution status and results:started_at, finished_at, and error_message fields.
Check if a Workflow is Running
Useis_running() to quickly check if a workflow has any active execution without manually querying runs:
True when any run has status running, waiting, or requested.
In the dashboard, a Running indicator appears next to the Active badge in the workflow editor header whenever the workflow has an active execution. It refreshes automatically every 2 seconds.
Example: Edge Detection Workflow
A camera_frame workflow that runs YOLO on the edge and emits alerts:Best Practices
- Keep workflows focused — create separate workflows for distinct operations rather than one large workflow. This makes debugging and maintenance easier.
- Add error handling — include condition nodes to handle failure cases gracefully. Consider what should happen if a joint can’t reach its target.
- Use meaningful names — name nodes and workflows descriptively. “Alert on person in zone A” is better than “Node 1”.
- Use
on_enteremit mode for alerts — avoids flooding with repeated events while the same object stays in frame. - Set appropriate cooldowns — balance between responsiveness and event volume. 5s is a safe default; lower for time-critical use cases.
- Eject before customising — never edit
wf_*.pyfiles directly. Copy them to a custom name and deactivate the originating workflow.
Environment-bound workflows
A workflow can optionally be bound to an environment (setenvironment_uuid when creating it, or open the Create Environment Workflow dialog from an environment panel). Bound workflows are scoped to that environment in the editor, surface in the environment’s workflow list, and ship to the environment’s edges through edge_sync_workflows when combined with run_on_edge: true.
Compiler dispatch keys off the graph itself: a twin_control node renders through the navigation path and a camera_frame trigger renders through the perception path, regardless of how the workflow is bound. Run cyberwave workflow compile <uuid> to see exactly which path the unified compiler took, or the diagnostic explaining why it skipped the workflow.
Code node (stub)
The Code action node runs a user-supplied Python function on the edge. It is only valid on workflows withrun_on_edge: true — Cyberwave never executes user-authored code on its own infrastructure.
- Runtime: Python, on the edge worker container that already runs the compiled
wf_*.pymodule. - I/O contract: Every Code node must define a top-level
def run(input: dict) -> dict. The return value is fed into the next Code node’sinputvia an internal_node_outputvariable. - Validation: The API parses the body with
ast.parseon create/update and rejects syntax errors or missingrunfunctions inline, before activation. - Activation guards: Activating a workflow is blocked if it contains a Code node and either (a)
run_on_edgeis false, or (b) the workflow uses acamera_frametrigger (not yet supported by the perception path of the edge compiler). - Availability: The node is always visible in the palette; it is disabled with an “Available only on run_on_edge workflows” tooltip when the workflow is not edge-bound.