Skip to content

Standalone nodes

Constantly adding and running nodes through the node stack is highly inefficient during development. In that scenario, you want to be able to run your node as a regular Rust/Python program and use your favorite IDE to debug it.

While debugging the nodes based on the logs can be quite helpful, nothing beats the ability to fire up the debugger to inspect the code that is supposed to run inside the peppy node stack. To support this, peppy can run a node in “standalone mode”—it communicates with other nodes in the stack but runs as a regular program outside of it, allowing you to use standard debugging tools. Let’s create a new node:

  1. Initialize the node:

    Terminal window
    peppy node init --toolchain uv standalone
  2. Navigate into the directory:

    Terminal window
    cd standalone

with the following configuration:

peppy.json5
{
schema_version: 1,
manifest: {
name: "standalone",
tag: "0.1.0",
language: "python",
},
process: {
add_cmd: [
"uv",
"sync"
],
start_cmd: [
"uv",
"run",
"standalone"
]
},
// A bunch of fake parameters required to start our node
parameters: {
device: {
physical: "string",
sim: "string",
priority: "string"
},
video: {
frame_rate: "u16",
resolution: {
width: "u16",
height: "u16",
},
encoding: "string",
},
},
interfaces: {}
}

Now sync the node:

Terminal window
peppy node sync

Now if you try to run the node:

Terminal window
uv run standalone

You’ll run into the following error:

Terminal window
RuntimeError: missing required parameter(s) for standalone mode: device, video. Provide them via StandaloneConfig().with_parameters()

These parameters are usually provided during peppy node start, but since we want this node to run as a standalone program, we need to pass them outside of the peppy daemon environment. We can define our parameters in a params.json file at the root of the project:

params.json
{
"device": {
"physical": "/dev/video0",
"sim": "virtual_camera",
"priority": "high"
},
"video": {
"frame_rate": 30,
"resolution": {
"width": 1920,
"height": 1080
},
"encoding": "h264"
}
}

Then modify the source file to read from this file:

src/standalone/__main__.py
import json
from peppygen import NodeBuilder, NodeRunner, StandaloneConfig
from peppygen.parameters import Parameters
async def setup(params: Parameters, node_runner: NodeRunner):
print("Inside the setup callback!")
def main():
# Parameters can also be defined directly in code:
#
# from peppygen.parameters import Device, Video, VideoResolution
#
# params = Parameters(
# device=Device(
# physical="/dev/video0",
# sim="virtual_camera",
# priority="high",
# ),
# video=Video(
# frame_rate=30,
# resolution=VideoResolution(
# width=1920,
# height=1080,
# ),
# encoding="h264",
# ),
# )
with open("params.json") as f:
params = json.load(f)
standalone_config = StandaloneConfig().with_parameters(params)
NodeBuilder().standalone(standalone_config).run(setup)
if __name__ == "__main__":
main()

Now if we run the following command again:

Terminal window
uv run standalone

The node should run without a crash. The standalone object allows us to load parameters from an external JSON file and pass them to the node, which in turn allows us to run our node as a regular Rust/Python program. Note that the standalone config is completely ignored when a node is run with peppy node start, all parameters provided during the node start operation take precedence.