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 jobsor “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
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:
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 within
, 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:
The socket object is in JSON schema, you can check the official documentation for more details.
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
Theinit
method intitializes the runner class:
Execution methods
These methods receive as argument actx
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 callingself.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: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.