No orchestrator.
pool, every peer runs the same pure function over its trace id, and only the owner answers.Clone the examples repo, install, and run from inside the example's folder.
$ git clone https://github.com/Cosmonapse/cosmonapse-examples $ pip install cosmonapse httpx python-dotenv # a Hugging Face token with read scope, in cosmonapse-examples/.env $ echo "HF_TOKEN=hf_xxxxxxxxxxxxxxxx" >> cosmonapse-examples/.env $ cd cosmonapse-examples/07-no-orchestrator $ python brain.py poems # four prompts - watch who claims each $ python brain.py "the sun"
worker-b answered: Golden light ascends ... worker-a answered: Pale moon in the dark ... worker-a answered: Endless blue expanse ... worker-b answered: Invisible hands ...
Which peer owns a trace depends on its random trace id; the split evens out over many TASKs.
One component per file, one Dendrite per node.
The skeleton cosmo init scaffolds. Modules under neurons/, engram/, effector/ and receptors/ declare behaviour; brain.py owns deployment - which node hosts what.
07-no-orchestrator/
config.py PEERS = ("worker-a", "worker-b")
neurons/pool.py make_axon(peer_id) - not attached to a Dendrite
receptors/terminal.py neuron="pool"; reports which peer answered
brain.py owner_of() + a peer node eachA pure function every peer agrees on.
owner_of hashes the trace id onto the peer list. Every peer computes the same answer independently, so exactly one claims each TASK and nobody coordinates. Adding a peer is adding an id to PEERS.
The peer observes TASKs with on_task_signal rather than attaching its Axon, because nothing is addressed to its id. The owner runs axon.handle_task itself and publishes the reply on the synapse.
def owner_of(trace_id: str) -> str: """Pure function: every peer computes the SAME owner - no coordination.""" h = int(hashlib.sha1(trace_id.encode()).hexdigest(), 16) return PEERS[h % len(PEERS)] def build_peer(synapse, peer_id: str) -> Dendrite: axon = pool.make_axon(peer_id) node = Dendrite(synapse=synapse, namespace=NAMESPACE, dendrite_id=peer_id, role="worker", heartbeat_s=0) @node.on_task_signal(neuron="pool") async def claim(task): if owner_of(task.trace_id) != peer_id: return # a peer owns this one reply = await axon.handle_task(task) # AGENT_OUTPUT / ERROR # The Dendrite never routed this TASK to an attached Axon, so the # reply goes out on the synapse directly. await synapse.publish(f"cosmonapse.{NAMESPACE}.{reply.type.value}", reply) return node
Drops work in, routes nothing.
The Receptor addresses pool, which no Axon is attached as. It still resolves, because it waits on the trace, not on a particular worker: the owner's AGENT_OUTPUT carries the same trace id.
from cosmonapse import CliReceptor, SignalType RECEPTOR = CliReceptor( neuron="pool", prog="no-orchestrator", description="Peers claim work by trace-id hash.", timeout_s=60.0, ) @RECEPTOR.command(help="drop a prompt into the pool") def ask(prompt: str): return {"prompt": prompt} @RECEPTOR.command("poems", local=True, help="four prompts, see who claims each") async def poems(): topics = ("the sun", "the moon", "the sea", "the wind") return "\n".join([await RECEPTOR.ask({"prompt": t}) for t in topics]) @RECEPTOR.on_result def render(sig): who = sig.directed.id if sig.directed else "?" if sig.type is SignalType.ERROR: return f"{who} failed: {sig.payload.get('message')}" return f"{who} answered: {sig.payload['output']['response'].strip()}"
from cosmonapse import Axon, Neuron from config import hf_token def make_axon(neuron_id: str) -> Axon: return Axon( neuron_id=neuron_id, neuron_fn=Neuron( source="huggingface", endpoint="https://router.huggingface.co", model="meta-llama/Llama-3.1-8B-Instruct", api_key=hf_token(), use_chat_api=True, max_new_tokens=128, ), capabilities=["chat"], )
Same code, different transport.
With SYNAPSE_URL unset the brain runs on an in-process bus. Point it at a synapse and nothing in the modules changes. Prism attaches to any shared synapse and animates every Signal as it crosses.
# terminal 1 - a dev synapse (TCP, single host) $ cosmo synapse start memory --namespace=quickstart # terminal 2 - the same brain, over that synapse $ SYNAPSE_URL=cosmo://127.0.0.1:7070 python brain.py poems # terminal 3 - Prism, the live view (http://127.0.0.1:7071) $ cosmo prism --url=cosmo://127.0.0.1:7070 -n quickstart # production transports - only the URL changes $ SYNAPSE_URL=nats://127.0.0.1:4222 python brain.py poems $ SYNAPSE_URL=kafka://127.0.0.1:9092 python brain.py poems