Install & run

Clone the examples repo, install, and run from inside the example's folder.

terminal
$ 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.

Layout

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
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 each
01 · The claim

A 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.

brain.py
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
02 · The interface

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.

receptors/terminal.py
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()}"
neurons/pool.py
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"],
    )
Over a real synapse

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
# 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
http://127.0.0.1:7071 · -n quickstart
Prism renders every Signal on the bus as it fires - REGISTER, TASK, AGENT_OUTPUT, FINAL.