From dora-skills
Develop Dora operators using the operator API in Rust and Python, covering event handling, output sending, and shared runtime integration.
How this skill is triggered — by the user, by Claude, or both
Slash command
/dora-skills:operator-apiThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> Lightweight alternative to nodes that run in a shared runtime process
Lightweight alternative to nodes that run in a shared runtime process
Operators are lightweight alternatives to nodes:
[dependencies]
dora-operator-api = "0.4"
[lib]
crate-type = ["cdylib"]
use dora_operator_api::{
register_operator, DoraOperator, DoraOutputSender, DoraStatus, Event
};
use dora_operator_api::arrow::array::*;
#[derive(Default)]
struct MyOperator {
counter: u32,
}
impl DoraOperator for MyOperator {
fn on_event(
&mut self,
event: &Event,
output_sender: &mut DoraOutputSender,
) -> Result<DoraStatus, String> {
match event {
Event::Input { id, data } => {
self.counter += 1;
// Process input...
// Send output
let result = UInt32Array::from(vec![self.counter]);
output_sender.send("count".to_string(), result)?;
Ok(DoraStatus::Continue)
}
Event::InputClosed { id } => {
println!("Input {} closed", id);
Ok(DoraStatus::Continue)
}
Event::Stop => Ok(DoraStatus::Stop),
_ => Ok(DoraStatus::Continue),
}
}
}
register_operator!(MyOperator);
pub enum Event<'a> {
// Input received
Input {
id: &'a str,
data: ArrowData,
},
// Input parsing failed
InputParseError {
id: &'a str,
error: String,
},
// Input closed by sender
InputClosed {
id: &'a str,
},
// Stop signal
Stop,
}
pub enum DoraStatus {
Continue, // Keep running
Stop, // Stop the operator
}
impl DoraOutputSender<'_> {
/// Send output with Arrow data
pub fn send(&mut self, id: String, data: impl Array) -> Result<(), String>;
}
use dora_operator_api::arrow::array::*;
// Integer output
let data = Int32Array::from(vec![1, 2, 3]);
output_sender.send("numbers".to_string(), data)?;
// String output
let data = StringArray::from(vec!["hello", "world"]);
output_sender.send("text".to_string(), data)?;
// Binary output
let data = BinaryArray::from(vec![b"bytes".as_slice()]);
output_sender.send("binary".to_string(), data)?;
# my_operator.py
class Operator:
def __init__(self):
self.counter = 0
def on_event(
self,
dora_event,
send_output,
):
if dora_event["type"] == "INPUT":
self.counter += 1
# Process input
value = dora_event["value"]
# Send output
send_output("count", [self.counter])
return DoraStatus.CONTINUE
nodes:
- id: processor
operator:
python: my_operator.py
# Or for Rust:
# shared-library: target/release/libmy_operator
inputs:
data: source/output
outputs:
- processed
nodes:
- id: runtime-node
operators:
- id: op1
python: op1.py
inputs:
data: source/output
outputs:
- result1
- id: op2
python: op2.py
inputs:
input: op1/result1
outputs:
- result2
nodes:
- id: rust-operator
operator:
shared-library: target/release/my_operator
inputs:
data: source/output
outputs:
- processed
// src/lib.rs
use dora_operator_api::{
register_operator, DoraOperator, DoraOutputSender, DoraStatus, Event
};
use dora_operator_api::arrow::array::UInt64Array;
#[derive(Default)]
struct Counter {
count: u64,
}
impl DoraOperator for Counter {
fn on_event(
&mut self,
event: &Event,
output_sender: &mut DoraOutputSender,
) -> Result<DoraStatus, String> {
if let Event::Input { .. } = event {
self.count += 1;
let output = UInt64Array::from(vec![self.count]);
output_sender.send("count".to_string(), output)?;
}
if matches!(event, Event::Stop) {
return Ok(DoraStatus::Stop);
}
Ok(DoraStatus::Continue)
}
}
register_operator!(Counter);
[package]
name = "counter-operator"
version = "0.1.0"
edition = "2024"
[lib]
crate-type = ["cdylib"]
[dependencies]
dora-operator-api = "0.4"
nodes:
- id: timer
path: timer-source
inputs:
tick: dora/timer/millis/1000
outputs:
- tick
- id: counter
operator:
shared-library: target/release/libcounter_operator
inputs:
tick: timer/tick
outputs:
- count
- id: logger
path: logger-sink
inputs:
count: counter/count
| Feature | Operators | Nodes |
|---|---|---|
| Process | Shared runtime | Separate process |
| Overhead | Lower | Higher |
| Isolation | Less | More |
| Memory | Shared | Separate |
| Languages | Rust, Python | Any |
| Use case | Simple transforms | Complex logic |
npx claudepluginhub zhanghandong/dora-skills --plugin dora-skillsProvides behavioral guidelines to reduce common LLM coding mistakes, focusing on simplicity, surgical changes, assumption surfacing, and verifiable success criteria.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.