Nodes Python SDK
Build independent agent nodes with advanced functionality
Prerequisite: Python version 3.11 or higher.
Install the Python SDK on your OS
Import the SDK
Create a runner
- Definition of a runner class:
Metadata
Metadata are required information about the node. You can set them in the “meta” object.
The “kind” value is the job kind, you can use one from the NIP-90 standard jobs
or “5003” that is the custom OpenAgents generic job kind.
All the other fields are free to be filled in as you like.
The tags field might be used by other nodes to filter actions by type.
We currently use the following tags:
- tool: if the action can be considered as a standalone tool for an LLM
But you can use any tag you like.
Filters
Filters are regular expressions used to filter the jobs that the node can execute.
You can check the protocol definition for a list of filters that you can use.
Jobs of kind 5003 always have a “run-on” parameter that is used to define which OpenAgents node should execute the job.
If you are writing a runner for a 5003 job kind, you should always use “filterByRunOn” with a custom name of you choice that you will use in the “run-on” parameter in the template (see below).
Templates
Runner templates are mustache templates based on NIP-01 and NIP-90 that define the structure of the event that must be sent to nostr to invoke the runner.
Inputs are defined as one ore more ["i"]
tags:
They need a “value”, a “type” and a “name or maker”.
As you can see value and type here are mustache’s tags, we will see how they are replaced in the next section.
“query” is the marker, it can be anything you want and its only purpose is to help you identify the input in the runner logic.
There can also be 0 or more parameters, prefixed with param, then the key (name) and the value:
A param of type run-on
is mandatory if you create a 5003 event and should be the same as the filterByRunOn
in the filter.
Events can contain anything that is defined in the NIPs as they represent standard nostr events.
The full template will look something like this:
For more information on the mustache syntax, see the official documentation.
Sockets
As you saw in the template, we use mustache tags prefixed with in
, out
and sys
, these match the keys in the sockets
object.
You need to define the in
and out
socket layouts in the runner class:
This object defines the structure of the inputs and outputs of the runner and will be used by the software, that wish to interact with it, together with the template to create a nostr event.
The socket object is in JSON schema, you can check the official documentation for more details.
These values can be accessed in the template using the mustache syntax by prefixing the key with in
or out
as we’ve seen in the previous section.
The sys
socket is defined by the environment, so you don’t have to define it in the sockets
object, but you can use its values in the template:
sys.timestamp_seconds
is the current timestamp in secondssys.expiration_timestamp_seconds
is the expiration timestamp for the event in seconds
The runner logic
API documentation for the SDK methods.
The runner class
The init
method intitializes the runner class:
Execution methods
These methods receive as argument a ctx
JobContext object.
The ctx
object contains several methods that can be used to get useful information related to the job (including inputs and parameters) and to comunicate with the pool’s api, see the documentation for more details.
canRun
is a filter. Can be used to perform checks before deciding whether to run the job (bool
):
preRun
is a hook that runs before run
. It can be useful depending on the case:
postRun
executes some code after the main logic in run
has been executed. It can be useful depending on the case:
run
is the main method, the one on which the node’s core logic must be implemented:
Parallel execution - Advanced
A single instance of the JobRunner is used to process each compatible job, by default this happens synchronously (ie. the next job will start after the end of the previous job).
It is possible to configure the runner to run the jobs in parallel by calling self.setRunInParallel(True)
inside the init
method.
Note: Parallel runners should be written in valid non-blocking asyncio code.
Create the node
Now that you have defined the runner class, you can write the code to initialize and start the runner, you can do it in the same file:
name, description and version are required fields for the node metadata, you can set them as you like.
Next you need to register the runner
And finally start the node:
Full code example
You can see a full example of a runner here.
Running the node
If you have followed up to this point, you should have a runner class and a node ready to be executed.
By default the node will connect to OpenAgents open pool (playground.openagents.com 6021 ssl), this can be changed by setting the following environment variables:*
To learn how to host your own pool check the Running a Pool documentation.
You can run the node with the following command: