Skip to content

Commit 68afe5a

Browse files
authored
chore(axon): add axon to sdk readme (#769)
1 parent 0ef4d1a commit 68afe5a

1 file changed

Lines changed: 111 additions & 0 deletions

File tree

README-SDK.md

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ The `RunloopSDK` builds on top of the underlying REST client and provides a Pyth
2525
- [StorageObject](#storageobject)
2626
- [Storage Object Upload Helpers](#storage-object-upload-helpers)
2727
- [Mounting Storage Objects to Devboxes](#mounting-storage-objects-to-devboxes)
28+
- [Axon](#axon)
29+
- [Publishing Events](#publishing-events)
30+
- [Subscribing to Events (SSE)](#subscribing-to-events-sse)
31+
- [SQL Operations](#sql-operations)
2832
- [Accessing the Underlying REST Client](#accessing-the-underlying-rest-client)
2933
- [Error Handling](#error-handling)
3034
- [Advanced Configuration](#advanced-configuration)
@@ -116,6 +120,7 @@ The SDK provides object-oriented interfaces for all major Runloop resources:
116120
- **`runloop.blueprint`** - Blueprint management (create, list, build blueprints)
117121
- **`runloop.snapshot`** - Snapshot management (list disk snapshots)
118122
- **`runloop.storage_object`** - Storage object management (upload, download, list objects)
123+
- **`runloop.axon`** - [Beta] Axon management (create, publish events, subscribe to SSE streams, SQL queries)
119124
- **`runloop.secret`** - Secret management (create, update, list, delete encrypted key-value pairs)
120125
- **`runloop.api`** - Direct access to the underlying REST API client
121126

@@ -679,6 +684,112 @@ result = devbox_with_archive.cmd.exec("ls -la /home/user/project/")
679684
print(result.stdout())
680685
```
681686

687+
### Axon
688+
689+
> **Beta:** Axon APIs are in beta and may change.
690+
691+
Object-oriented interface for working with axons — event communication channels that support
692+
publishing events, subscribing to event streams via SSE, and executing SQL queries against an
693+
embedded SQLite database. Created via `runloop.axon.create()` or `runloop.axon.from_id()`:
694+
695+
```python
696+
# Create a new axon
697+
axon = runloop.axon.create(name="my-axon")
698+
699+
# Or get an existing one
700+
axon = runloop.axon.from_id(axon_id="axn_123")
701+
702+
# List all axons
703+
axons = runloop.axon.list()
704+
705+
# Get axon details
706+
info = axon.get_info()
707+
print(f"Axon {info.name} created at {info.created_at_ms}")
708+
```
709+
710+
#### Publishing Events
711+
712+
Publish structured events to an axon:
713+
714+
```python
715+
result = axon.publish(
716+
event_type="task_completed",
717+
origin="AGENT_EVENT", # EXTERNAL_EVENT | AGENT_EVENT | USER_EVENT
718+
payload='{"task_id": 42, "status": "done"}',
719+
source="my-agent",
720+
)
721+
print(f"Sequence: {result.sequence}, Timestamp: {result.timestamp_ms}")
722+
```
723+
724+
#### Subscribing to Events (SSE)
725+
726+
Subscribe to real-time events via server-sent events:
727+
728+
```python
729+
with axon.subscribe_sse() as stream:
730+
for event in stream:
731+
print(f"[{event.source}] {event.event_type}: {event.payload}")
732+
# event.axon_id, event.origin, event.sequence, event.timestamp_ms also available
733+
```
734+
735+
Async:
736+
737+
```python
738+
async with await axon.subscribe_sse() as stream:
739+
async for event in stream:
740+
print(f"[{event.source}] {event.event_type}: {event.payload}")
741+
```
742+
743+
#### SQL Operations
744+
745+
Each axon has an embedded SQLite database accessible via `axon.sql`:
746+
747+
```python
748+
# Create a table
749+
axon.sql.query(sql="CREATE TABLE tasks (id INTEGER PRIMARY KEY, name TEXT, done INTEGER)")
750+
751+
# Insert with parameterized queries (? placeholders)
752+
axon.sql.query(
753+
sql="INSERT INTO tasks (id, name, done) VALUES (?, ?, ?)",
754+
params=[1, "Write docs", 0],
755+
)
756+
757+
# Query
758+
result = axon.sql.query(sql="SELECT * FROM tasks WHERE done = ?", params=[0])
759+
for row in result.rows:
760+
print(row) # e.g. [1, "Write docs", 0]
761+
print(result.columns) # [SqlColumnMetaView(name="id", type="INTEGER"), ...]
762+
print(result.meta) # SqlResultMetaView(changes=0, duration_ms=0.5, rows_read_limit_reached=False)
763+
```
764+
765+
Execute multiple statements atomically with `batch`:
766+
767+
```python
768+
result = axon.sql.batch(
769+
statements=[
770+
{"sql": "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)"},
771+
{"sql": "INSERT INTO users (id, name) VALUES (?, ?)", "params": [1, "Alice"]},
772+
{"sql": "INSERT INTO users (id, name) VALUES (?, ?)", "params": [2, "Bob"]},
773+
{"sql": "SELECT * FROM users ORDER BY id"},
774+
],
775+
)
776+
777+
# Each statement gets a result
778+
for step in result.results:
779+
if step.success:
780+
print(f"Rows: {step.success.rows}, Changes: {step.success.meta.changes}")
781+
elif step.error:
782+
print(f"Error: {step.error}")
783+
```
784+
785+
**Key methods:**
786+
787+
- `axon.get_info()` – Get axon details (name, created_at_ms)
788+
- `axon.publish()` – Publish an event (event_type, origin, payload, source)
789+
- `axon.subscribe_sse()` – Subscribe to event stream (returns `Stream[AxonEventView]`)
790+
- `axon.sql.query()` – Execute a single SQL statement with optional params
791+
- `axon.sql.batch()` – Execute multiple SQL statements atomically
792+
682793
## Accessing the Underlying REST Client
683794

684795
The SDK always exposes the underlying client through the `.api` attribute:

0 commit comments

Comments
 (0)