Build Neat
This guide covers source builds of Neat. For prebuilt package installation, see Neat
Framework.
[Link] is the supported build entry point. It handles dependency checks, optional deps
sync, CMake configure/build, optional docs generation, install sanity checks, and
packaging.
Build Environments
[Link] automatically detects the active environment:
· Modalix DevKit native environment
· Neat SDK environment (cross-compilation)
You can run the same [Link] commands in either environment.
Cross Compilation Prerequisites
Cross-compilation is typically faster than building directly on the DevKit, but you must
transfer build artifacts to the DevKit afterward. You will need the Neat SDK for cross
compilation.
Install sima-cli first on the host machine, then install the SDK.
curl [Link] | bash
sima-cli install sdk
When prompted by sima-cli, select the SDK option.
Then start the SDK:
sima-cli sdk elxr
Then install sima-cli inside the SDK, then install the SDK patch.
curl [Link] | bash
source ~/.bash_profile
sima-cli install tools/sdk-patch
· SDK installation is supported on Windows and Ubuntu.
· If you are building natively on a Modalix DevKit, the SDK install/patch steps are
not required.
Build Options
Supported [Link] options:
· --dev-only: Build only the core library and headers (default).
· --all: Build library + tests + tutorials + Python wheel; enables docs and deps.
· --python: Build Python bindings (pyneat) in addition to selected targets.
· --install-neat-internals, --install-deps: Download and install deps artifacts before
build.
· --doc: Build docs only.
· --install: After build/package, install generated artifacts into the current
environment. In paired Neat SDK mode, this also deploys and installs matching
artifacts on the paired DevKit.
· --no-dist: Skip distribution packaging.
· --clean: Remove build/ before configuring.
· --no-doc: Skip docs build (even with --all).
· --no-node: Skip [Link] install (docs build may fail if Node is missing).
· --install-deps-only: Install system dependencies only, then exit.
Typical Builds
Core library only (default):
./[Link]
Full build (library, tests, tutorials, docs, wheel, packaging):
./[Link] --all
Core library + Python bindings:
./[Link] --dev-only --python
Docs only:
This command also works on macOS.
./[Link] --doc
Clean full build:
./[Link] --all --clean
Install dependencies only:
./[Link] --install-deps-only
Outputs
· Build tree: build/
· Docusaurus site output (when docs build runs): website/build/
· Install sanity-check prefix: /tmp/sima-neat-install-test
· Core package (*.deb) is generated on Linux full builds unless --no-dist is used.
· Extras package (*[Link]) is generated on Linux full builds unless --no-dist is
used.
· Python wheel (dist/*.whl) is generated when Python build is enabled.
Hello Neat!
Minimal Example
This guide uses a minimal example to verify that Neat is installed and runnable, while
introducing the core application development and validation workflow.
· a Modalix DevKit
· the Neat SDK
Neat SDK Prerequisite
To run commands on the DevKit directly from inside the SDK (for example, dk
build/sima_neat_hello or dk hello_neat.py), set up DevKit pairing first:
sima-cli sdk setup --devkit <devkit-ip>
If SDK/DevKit pairing is not configured, you can still build inside the Neat SDK, but you
must manually transfer the built binary or script to the DevKit and run it there.
tip
About dk / devkit-run
dk (alias for devkit-run) is a shell function in the SDK container, defined in ~/devkit-
[Link] and loaded by ~/.bashrc.
Because it is a shell function, commands such as which devkit-run may return nothing in
the SDK shell. Use dk <file> to execute a built binary or Python entry-point file on the
paired DevKit.
Create a working directory with the following files:
C++Python
hello_neat.py:
from pyneat import DeviceType
def main():
print("Hello from sima-neat")
print("[Link] =", [Link])
if __name__ == "__main__":
main()
Run:
DevKit
source ~/pyneat/bin/activate
python3 hello_neat.py
Neat SDK
dk hello_neat.py
Python Runtime Location
pyneat is installed on the DevKit runtime side, even if you run the Neat installer from
inside the Neat SDK container.
When you run dk hello_neat.py, dk executes the script on the paired DevKit using the
DevKit pyneat environment.
Next Steps
Once this minimal example works, continue with broader SiMa Neat learning resources:
· Learn the core programming model, which explains the main Neat concepts such
as sessions, models, pipeline stages, and graph execution.
· Follow the tutorials, which walk through specific concepts and workflows step by
step.
· Explore curated applications on the apps portal, with source code in the apps
repository on GitHub.
Inference Workflow
If you are new to SiMa Neat, keep this sequence in mind:
1. Load a compiled model package (.[Link]) with Model.
2. Compose a Session with input nodes, model stages, and output nodes.
3. Build a Run in sync or async mode.
4. Push inputs and pull outputs as Tensor or Sample.
Synchronous inference snippet
Sync: use run(...) or push_and_pull(...) for request/response style execution.
C++Python
# Pseudo Python sync example.
model = [Link]("resnet_50_model.[Link]")
session = [Link]()
[Link]([Link]())
img = ... # your frame tensor
run = [Link](img, [Link])
out = [Link](img, timeout_ms=1000)
Asynchronous inference snippet
Use async mode when you want to decouple producers and consumers, control
queueing, or overlap IO and compute. Async: use push(...) / pull(...) with RunOptions to
tune queueing and drop behavior.
C++Python
# Pseudo Python async example.
model = [Link]("resnet_50_model.[Link]")
session = [Link]()
[Link]([Link]())
img = ... # your frame
opt = [Link]()
opt.queue_depth = 8
opt.overflow_policy = [Link]
run = [Link](img, [Link], opt)
[Link](img)
out = [Link](timeout_ms=1000)
Learn the concepts
· Model: model pack loading and model-driven pipeline fragments.
· Session: assembly, validation, and run/build entry point.
· Node: atomic pipeline building block and composition unit.
· Pipeline: deterministic node composition and execution handles.
· Graph: hybrid DAG runtime for pipeline + stage composition.
· Tensor and Sample: payload vs metadata envelope.
· Input and Output: sources, sinks, groups, and I/O contracts.
Tutorials
· Run Your First Model
· Run Inference Asynchronously
· Build an Inference Pipeline
· Embed a Model Inside a Graph
Model
Model is the top-level API for loading a compiled model (.[Link]) and exposing reusable
stage fragments.
Use Model when you want model-aware pipeline assembly without manually wiring
every model plugin.
What Model gives you
· session(): full model path as a node group.
· preprocess(), inference(), postprocess(): stage-level composition.
· input_spec() and output_spec(): tensor contract introspection.
· build(...) / run(...): direct convenience execution via Model::Runner.
Reference:
· Model API
· Model::Options
· Model::SessionOptions
Typical usage inside a session
C++Python
# Pseudo Python model example.
model = NeatModel("yolov8s_model.[Link]")
session = Session()
[Link]([Link]())
Failure handling
Model-driven sessions use the same diagnostics contract as raw Session:
· [Link]().error_code for terminal failures
· [Link]().repro_note for actionable context + hints
· [Link]().bus for plugin/runtime detail
Start triage from error_code (misconfig.*, build.*, runtime.*, io.*) before inspecting detailed
bus logs.
See also
· Inference Workflow
· Session
· Pipeline
Tutorials
· Run Your First Model
· Configure Model Options
· Read Detection Boxes from Model Output
· Plug a Model Into Your Pipeline
Session
Session is the runtime entry point. It owns pipeline assembly, validation, and build/run
orchestration.
Think of Session as the place where you define pipeline structure once, then execute it
many times.
Core responsibilities
· Add nodes and groups with add(...).
· Inspect pipeline text with describe() and to_gst().
· Validate contracts before runtime with validate(...).
· Build a Run object via build(...).
· Run inference with run(...).
Reference:
· Session API
· SessionOptions
· ValidateOptions
Session + Run pattern
C++Python
# Pseudo Python session + Run example.
session = Session()
[Link]([Link]())
[Link]([Link]())
run = [Link](input_tensor, [Link])
out = run.push_and_pull(input_tensor, 1000)
See also
· Model
· Node
· Pipeline
· Graph
· Tensor and Sample
· Input and Output
Tutorials
· Build an Inference Pipeline
· Plug a Model Into Your Pipeline
· Diagnose and Profile a Pipeline
Node
A Node is the smallest composable unit in a SiMa Neat pipeline. Each node contributes
deterministic pipeline fragments and metadata used by the builder and runtime.
Reference:
· nodes namespace
· nodes::groups namespace
What a Node represents
· One logical stage in the pipeline (decode, convert, preprocess, sink, etc.).
· A deterministic gst fragment with stable element naming.
· Caps behavior and wiring hints used by Session build/validation.
Node vs NodeGroup
· Node: single stage.
· NodeGroup: ordered reusable list of nodes (for example input groups or model
stages).
You add both through Session with add(...).
Why it matters
· Predictable pipeline structure and naming.
· Easier debugging with describe() and describe_backend().
· Reusable building blocks for custom and model-driven flows.
See also
· Session
· Pipeline
· Graph
· Input and Output
Tutorials
· Build an Inference Pipeline
· Plug a Model Into Your Pipeline
Pipeline
In SiMa Neat, a pipeline is an ordered composition of nodes and node groups
assembled through Session.
The result is deterministic graph wiring and a reproducible gst-launch representation.
Building blocks
· Node: atomic stage unit (decode, convert, preprocess, sink, etc.).
· NodeGroup: reusable ordered set of nodes (for example model stages or input
groups).
· Session: composition boundary and runtime build/validate point.
Reference:
· Run
· RunOptions
· Node API
· nodes namespace
Deterministic naming
Element names are generated deterministically from node order. This gives:
· Stable describe() output.
· Stable describe_backend() strings for reproduction/debug.
· Consistent diagnostics and probe attachment points.
Execution options
· Sync execution: push_and_pull(...) or run(...) for simple request-response
behavior.
· Async execution: push(...) / pull(...) with queue tuning and drop policy.
Caps and negotiation (simple mental
model)
SiMa Neat relies on native GStreamer negotiation, but controls key boundaries:
· Push pipelines (Input): caps are derived from actual input at build/start time,
then enforced by runtime policy.
· Source pipelines (file/RTSP/image groups): caps are negotiated by
source/decode elements, optionally constrained with explicit caps nodes.
· Output normalization: add_output_tensor(...) inserts convert/scale/caps + sink to
keep output predictable.
For push pipelines, format/shape changes are handled automatically. Runtime presets
and queue policies control latency/safety tradeoffs, and advanced memory limits can be
set with RunAdvancedOptions::max_input_bytes.
Reference:
· Input
· InputOptions
· RunOptions
· OutputTensorOptions
Why this matters
· Stable element naming for debugging and diagnostics.
· Reproducible describe_backend() for troubleshooting with native GStreamer tools.
· Composable model + media pipelines in one API surface.
· Preset-driven runtime behavior for caps transitions and buffer safety.
See also
· Inference Workflow
· Node
· Graph
· Session
· Input and Output
Tutorials
· Run Inference Asynchronously
· Build an Inference Pipeline
· Plug a Model Into Your Pipeline
· Feed Models That Take Multiple Inputs
· Build a Custom Data Graph
Graph
Use Graph when a single linear pipeline is no longer enough. In real deployments, you
often need:
· One input, multiple consumers: run inference, recording, and telemetry from
the same stream without duplicating ingest.
· Multi-stream coordination: synchronize or merge outputs from multiple
cameras/sources before downstream decisions.
· Mixed workloads: combine media-heavy pipeline stages with lightweight in-
process logic (routing, filtering, policy checks).
· Selective branching: route only specific frames/events to expensive stages (for
example secondary models).
· Operational isolation: keep custom control logic in stage nodes while leaving
media/runtime-heavy parts in pipeline nodes.
graph::Graph provides this as a DAG (Directed Acyclic Graph): deterministic forward
flow, explicit fan-out/join, and hybrid execution.
How it works
1. Define nodes and edges in Graph.
2. Build with GraphSession.
3. Run with GraphRun.
You can mix:
· PipelineNode for regular Neat Node/NodeGroup pipeline fragments.
· StageNode for in-process custom logic.
Core objects
· Graph: DAG container with typed ports and edges.
· GraphSession: compiles the DAG into runnable segments.
· GraphRun: runtime handle for push/pull and stats.
· Compiler: partitions and wires pipeline/stage backends.
Key node families
· PipelineNode
· StageNode
· StreamScheduler
· JoinBundle
See also
· Node
· Pipeline
· Session
Tutorials
· Build a Custom Data Graph
· Embed a Model Inside a Graph
· Run Multiple Streams in One Graph
Tensor and Sample
The runtime data model has two primary types:
· Tensor: typed numeric payload (shape, dtype, layout, storage, device).
· Sample: envelope around tensor payload with media/runtime metadata.
Tensor
Use Tensor when you only need numeric data and shape/type semantics.
Typical metadata in tensor:
· shape / dtype / layout
· storage and mapping behavior
· optional image/audio/encoded semantic tags
Related references:
· Tensor
· TensorConstraint
· TensorTypes.h
NumPy and PyTorch interop
For Python developers familiar with NumPy and PyTorch, Tensor supports DLPack-
based interop:
· Tensor.from_numpy(...)
· Tensor.to_numpy(...)
· Tensor.from_torch(...)
· Tensor.to_torch(...)
· Tensor.from_dlpack(...)
· Tensor.__dlpack__()
This keeps interop paths explicit and enables zero-copy where backend data layout
permits it.
NumPyPyTorch
import numpy as np
import pyneat as neat
# HWC uint8 image-like tensor
arr = [Link](0, 255, (224, 224, 3), dtype=np.uint8)
t = [Link].from_numpy(arr, copy=False, image_format=[Link])
arr_back = t.to_numpy(copy=False)
# If you already have a model, NumPy can be passed directly.
# out = [Link](arr, timeout_ms=2000)
Sample
Use Sample when you need pipeline metadata in addition to tensor bytes.
Typical sample fields:
· caps_string, media_type, payload_tag
· pts_ns, dts_ns, duration_ns
· stream_id, frame_id, port_name
· fields for bundle outputs
Related references:
· Sample
· SessionOptions.h
· PullError
C++Python
import pyneat as neat
session = [Link]()
[Link]([Link]([Link]()))
[Link]([Link]([Link]()))
run = [Link]([Link](), [Link])
sample = [Link]()
[Link] = [Link]
sample.stream_id = "cam-0"
sample.frame_id = 42
# [Link] = ...
[Link](sample)
out = [Link](1000)
if out is not None:
print(f"stream={out.stream_id} frame={out.frame_id} media_type={out.media_type}")
Runtime handle
Both types flow through Run (push, pull, push_and_pull, run).
See also
· Session
· Pipeline
· Input and Output
Tutorials
· Pass NumPy Arrays to the Model
· Feed Models That Take Multiple Inputs
· Read and Interpret Model Output
Input and Output
I/O in SiMa Neat is explicit and contract-driven. You select how data enters and exits
the pipeline, then tune runtime behavior around those contracts.
Use this page as a decision guide:
· Choose how input enters: source-managed vs app-pushed.
· Choose output style: rich samples vs normalized tensors.
· Match that choice to your runtime pattern (service, stream, or batch/offline).
Input patterns
File/stream input groups
Use group helpers from the node-group APIs when the source is image/video/RTSP.
They package common GStreamer source recipes and reduce boilerplate.
Language mapping:
· C++: simaai::neat::nodes::groups::VideoInputGroup(...), RtspDecodedInput(...)
· Python: [Link].video_input(...), [Link].rtsp_decoded_input(...)
C++Python
import pyneat as neat
session = [Link]()
vopt = [Link]()
[Link] = "/data/sample.mp4"
[Link]([Link].video_input(vopt))
Use this pattern when:
· Input comes from files, camera, or RTSP stream URLs.
· You want decode/source behavior handled inside the pipeline.
· You are building media-first flows (decode -> convert -> infer -> render/stream).
Push input (nodes::Input)
Use the input node pattern for application-driven frame/tensor push. This is the most
common pattern for inference services.
Language mapping:
· C++: [Link](simaai::neat::nodes::Input(...))
· Python: [Link]([Link](...))
C++Python
import pyneat as neat
session = [Link]()
iopt = [Link]()
[Link] = "RGB"
[Link] = 224
[Link] = 224
[Link]([Link](iopt))
Use this pattern when:
· Your app already produces frames/tensors.
· You need request/response or queue-controlled async inference.
· You want explicit push/backpressure behavior with RunOptions.
Key options and contracts:
· InputOptions
· RunOptions
Output patterns
Rich output (nodes::Output)
Use the output node pattern when you need sample output with pull-side buffering
policy.
Language mapping:
· C++: [Link](simaai::neat::nodes::Output(...))
· Python: [Link]([Link](...))
C++Python
import pyneat as neat
session = [Link]()
[Link]([Link]([Link]()))
[Link]([Link]([Link]()))
run = [Link]([Link](), [Link])
[Link]([Link]())
sample = [Link](1000) # returns Sample (metadata + payload)
Use this pattern when:
· You need full Sample metadata (stream/frame identity, payload details).
· You apply custom post-processing/business logic after pull.
· You want output buffering behavior controlled via OutputOptions.
Tensor-first output (add_output_tensor)
Use Session::add_output_tensor(...) for a simpler tensor-oriented output path with
format/shape normalization.
Use this pattern when:
· Your consumer expects predictable tensor format/shape.
· You do not need richer media/sample envelope data.
· You want a lower-boilerplate model-serving output path.
C++Python
import pyneat as neat
session = [Link]()
[Link]([Link]([Link]()))
session.add_output_tensor([Link]())
run = [Link]([Link](), [Link])
[Link]([Link]())
tensor = run.pull_tensor(1000) # tensor-first consumption
RTSP mode
For server-style output, use Session::run_rtsp(...) and configure RtspServerOptions.
Use this when:
· The pipeline should publish an endpoint for viewers/downstream systems.
· You need long-running streaming service behavior.
Quick decision guide
· Source is file/camera/RTSP: use input groups from nodes::groups.
· Source is app-produced tensor/frame: use nodes::Input.
· Need rich metadata-aware outputs: use nodes::Output.
· Need normalized tensor outputs: use add_output_tensor(...).
· Need network-served stream output: use run_rtsp(...).
See also
· Inference Workflow
· Session
· Tensor and Sample
Tutorials
· Pass NumPy Arrays to the Model
· Feed Models That Take Multiple Inputs
· Read and Interpret Model Output