Python SDK for Norns — durable agent runtime on BEAM
Project description
norns-sdk
Python SDK for Norns — durable agent runtime on BEAM.
Two classes, two roles:
Norns— worker. Registers agents and tools, handles dispatched LLM/tool tasks. Blocks forever, like a Temporal worker.NornsClient— client. Sends messages, polls for results, streams events. For your Slack bot, web backend, CLI, etc.
pip install norns-sdk
Worker
import os
from norns import Norns, Agent, tool
@tool
def search_docs(query: str) -> str:
"""Search product documentation."""
return db.vector_search(query)
@tool(side_effect=True)
def send_email(to: str, subject: str, body: str) -> str:
"""Send an email to a customer."""
smtp.send(to=to, subject=subject, body=body)
return f"Email sent to {to}"
agent = Agent(
name="support-bot",
model="claude-sonnet-4-20250514",
system_prompt="You are a customer support agent. Look up docs and help customers.",
tools=[search_docs, send_email],
mode="conversation",
on_failure="retry_last_step",
)
norns = Norns("http://localhost:4000", api_key=os.environ["NORNS_API_KEY"])
norns.run(agent) # LLM API keys read from env (ANTHROPIC_API_KEY, OPENAI_API_KEY, etc.)
This connects to Norns via WebSocket, registers the agent, and sits in a loop handling llm_task and tool_task dispatches. LLM calls are routed through LiteLLM, so any supported provider works. Norns never sees your API keys — your worker makes all external calls.
Norns Orchestrator Your Python Worker
│ (pure state machine) │ (this SDK)
│ │
│ dispatches llm_task ──────────────► │ calls LLM (via LiteLLM)
│ ◄── llm_response ───────────────── │
│ dispatches tool_task ─────────────► │ calls search_docs()
│ ◄── tool_result ────────────────── │
│ logs events, checkpoints │
Client
import os
from norns import NornsClient
client = NornsClient("http://localhost:4000", api_key=os.environ["NORNS_API_KEY"])
# Fire-and-forget
run = client.send_message("support-bot", "Where's my order?")
# run.run_id, run.status == "accepted"
# Wait for completion
result = client.send_message("support-bot", "Where's my order?", wait=True, timeout=30)
print(result.output)
# Multi-turn with a conversation key
result = client.send_message("support-bot", "And the tracking number?",
conversation_key="slack:U01ABC", wait=True)
# Inspect a run
run = client.get_run(42)
events = client.get_events(42)
# Stream events as they happen
for event in client.stream("support-bot", "Research quantum computing"):
if event.type == "completed":
print(event.data.get("output", "")[:80])
break
Tools
The @tool decorator infers JSON Schema from type hints:
@tool
def lookup_customer(email: str) -> str:
"""Look up a customer by email."""
customer = db.query("SELECT * FROM customers WHERE email = ?", email)
return f"Found: {customer['name']} ({customer['plan']})"
Mark side-effecting tools so Norns can enforce idempotency:
@tool(side_effect=True)
def charge_card(customer_id: str, amount: float) -> str:
"""Charge a credit card."""
result = stripe.charges.create(customer=customer_id, amount=int(amount * 100))
return f"Charged ${amount}: {result['id']}"
Async handlers work too:
@tool
async def fetch_page(url: str) -> str:
"""Fetch a web page."""
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
return await resp.text()
Agent options
agent = Agent(
name="my-agent",
model="claude-sonnet-4-20250514",
system_prompt="You are helpful.",
tools=[search, send_email],
mode="conversation", # "task" or "conversation"
checkpoint_policy="on_tool_call", # "every_step", "on_tool_call", "manual"
context_window=20,
max_steps=50,
on_failure="retry_last_step", # "stop" or "retry_last_step"
)
Docs
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file norns_sdk-0.2.0.tar.gz.
File metadata
- Download URL: norns_sdk-0.2.0.tar.gz
- Upload date:
- Size: 179.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
416a81189d0987c1f86c860d11edb1592bb02a173c398ede1529e7288a5a7580
|
|
| MD5 |
df7f16e06044028b01c41665ecc2b4f5
|
|
| BLAKE2b-256 |
6f6a6f1b32c2ab779580339c9c87b31daec343ed753e4cbd15e13dbf7f8e00df
|
Provenance
The following attestation bundles were made for norns_sdk-0.2.0.tar.gz:
Publisher:
release.yml on amackera/norns-sdk-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
norns_sdk-0.2.0.tar.gz -
Subject digest:
416a81189d0987c1f86c860d11edb1592bb02a173c398ede1529e7288a5a7580 - Sigstore transparency entry: 1196920070
- Sigstore integration time:
-
Permalink:
amackera/norns-sdk-python@ef13515a6e9b84454f64593147eeac30c7a50e6c -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/amackera
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ef13515a6e9b84454f64593147eeac30c7a50e6c -
Trigger Event:
push
-
Statement type:
File details
Details for the file norns_sdk-0.2.0-py3-none-any.whl.
File metadata
- Download URL: norns_sdk-0.2.0-py3-none-any.whl
- Upload date:
- Size: 12.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
baae74e0c688cb9d7d645ad3617f6e65502a8c6bc1b4efbd38f2e35096659e64
|
|
| MD5 |
aadd75cebaaf321373d85138a5c508ec
|
|
| BLAKE2b-256 |
f3d6ee4df65d62916739df086a3bd0536ea51e0e3702d6e9477df466cbf4fead
|
Provenance
The following attestation bundles were made for norns_sdk-0.2.0-py3-none-any.whl:
Publisher:
release.yml on amackera/norns-sdk-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
norns_sdk-0.2.0-py3-none-any.whl -
Subject digest:
baae74e0c688cb9d7d645ad3617f6e65502a8c6bc1b4efbd38f2e35096659e64 - Sigstore transparency entry: 1196920131
- Sigstore integration time:
-
Permalink:
amackera/norns-sdk-python@ef13515a6e9b84454f64593147eeac30c7a50e6c -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/amackera
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ef13515a6e9b84454f64593147eeac30c7a50e6c -
Trigger Event:
push
-
Statement type: