← Examples/Orchestrator + Round Robin
Pick your stack

The same topology, expressed across language and transport. Slide between tabs — the routing logic never changes; only the imports, the synapse you connect to, and how you launch it do.

01 · Install

The dev devsynapse ships in the CLI — no external broker to install.

# SDK + CLI + httpx (used by the HuggingFace Neuron wrapper)
pip install -e cosmonapse-core/packages/python-sdk
pip install -e cosmonapse-core/packages/cli
pip install httpx

# Hugging Face token — read scope is enough
$ export HF_TOKEN=hf_xxxxxxxxxxxxxxxxxxxxxxxx
02 · Start the Synapse

One bus, one namespace. The CLI also streams every Signal that crosses it to stdout, so the synapse terminal doubles as a Doppler.

$ cosmo synapse start memory --namespace=quickstart

  URL:        cosmo://127.0.0.1:7070
  Namespace:  quickstart
  Transport:  TCP + NDJSON  (single-host dev only)
  ────────────────────────────────────────────────
03 · Worker — a Neuron behind an Axon

A Worker Dendrite that hosts a single Axon wrapping a HuggingFace Neuron. Copy it to worker_b.py and change only MY_ID to "worker-b" (and the model, if you like).

worker_a.py
import asyncio
import os
from cosmonapse import Axon, Neuron, Dendrite, connect_synapse

SYNAPSE_URL = "cosmo://127.0.0.1:7070"   # ← the only line that changes per transport
NAMESPACE   = "quickstart"
MY_ID       = "worker-a"      # worker-b is identical but for this line

async def main():
    axon = Axon(
        neuron_id=MY_ID,
        neuron_fn=Neuron(
            source="huggingface",
            endpoint="https://router.huggingface.co",
            model="meta-llama/Llama-3.1-8B-Instruct",
            api_key=os.environ["HF_TOKEN"],
            use_chat_api=True, max_new_tokens=128,
        ),
        capabilities=["text-generation", "chat"],
    )

    synapse  = await connect_synapse(SYNAPSE_URL)
    dendrite = Dendrite(synapse=synapse, namespace=NAMESPACE,
                        dendrite_id=MY_ID)
    dendrite.attach_axon(axon)

    try:
        async with dendrite:
            print(f"{MY_ID} ready")
            await asyncio.Event().wait()
    finally:
        await synapse.close()

asyncio.run(main())
04 · The Cortex — a round-robin Dendrite

A Cortex is just a Dendrite that dispatches tasks and collects results. itertools.cycle(WORKERS) picks the next target; a trace_id → Future map resolves the caller when the matching AGENT_OUTPUT returns.

cortex.py
import asyncio, itertools
from cosmonapse import Dendrite, connect_synapse, new_trace_id

SYNAPSE_URL = "cosmo://127.0.0.1:7070"
WORKERS = ("worker-a", "worker-b")

class RoundRobinCortex:
    """A Dendrite that round-robins prompts across a worker pool."""

    def __init__(self, dendrite, workers):
        self._dendrite = dendrite
        self._cycle    = itertools.cycle(workers)
        self._pending  = {}

        @dendrite.on_agent_output
        async def _on_output(sig):
            fut = self._pending.pop(sig.trace_id, None)
            if fut and not fut.done():
                fut.set_result(sig.payload.get("output", {}))

    async def ask(self, prompt, timeout=60.0):
        target   = next(self._cycle)        # ← round-robin pick
        trace_id = new_trace_id()
        fut      = asyncio.get_running_loop().create_future()
        self._pending[trace_id] = fut
        await self._dendrite.dispatch_task(
            neuron=target, input={"prompt": prompt}, trace_id=trace_id,
        )
        print(f"→ dispatched to {target}")
        return await asyncio.wait_for(fut, timeout=timeout)

async def main():
    synapse  = await connect_synapse(SYNAPSE_URL)
    dendrite = Dendrite(synapse=synapse, namespace="quickstart",
                        dendrite_id="cortex", heartbeat_s=0)
    cortex   = RoundRobinCortex(dendrite, WORKERS)

    prompts = ["haiku: the sun", "haiku: the moon",
               "haiku: the sea", "haiku: the wind"]
    try:
        async with dendrite:
            for p in prompts:
                result = await cortex.ask(p)
                print(f"   ← {result.get('response', '').strip()}")
    finally:
        await synapse.close()

asyncio.run(main())
05 · Run the topology

Separate terminals, one Synapse shared by all. Start the bus first, then the workers, then the driver.

# terminal 1 — the bus
$ cosmo synapse start memory --namespace=quickstart

# terminal 2 — first worker
$ python worker_a.py

# terminal 3 — second worker
$ python worker_b.py

# terminal 4 — the cortex
$ python cortex.py

Watch the prompts alternate A, B, A, B in the cortex output:

$ python cortex.py
→ dispatched to worker-a
   ← Golden disc ascends — silence breaks into light.
→ dispatched to worker-b
   ← Pale lantern in the dark — tides remember her face.
→ dispatched to worker-a
   ← Salt sighs against stone, an old song the wind forgot.
→ dispatched to worker-b
   ← Invisible river — it bends the wheat into prayer.
Extend the pattern

More workers. Add worker-c and extend the WORKERS list — the cycle handles any length.

Weighted round-robin. Replace the cycle with a custom sequence — e.g. ["a", "a", "b"] to send 2-of-3 to worker A.

Dynamic membership. Pass a registry_store to the Cortex and call find_neurons(capability="chat") instead of a static list — workers can join and leave at runtime.

Change transport. Every other tab is the same topology — only the install, the synapse you connect to, and the launch commands change. The routing logic is byte-for-byte identical.