Skip to main content

fly-harness

v0.4.0 — a sparse neural harness, not a fly-brain simulation.

Formula: Agent = deployed bio-sim Model + harness. The harness docks to a running sim through a stable contract. FlyWire / MaleCNS files are weight dumps, not that contract.

The public contract is:

  1. BrainState — membrane potentials, sparse synaptic weights (scipy.sparse CSR), timestamp, JSON save/load
  2. FlyHarness.step(obs) -> action — encode → ModelBackend.tick → decode (default backend is the in-process LIF/rate circuit)
  3. Encoder / Decoder — typing.Protocol contracts (optional BaseEncoder / BaseDecoder ABCs; no string class-name checks)
  4. load_connectome — load a circuit subset from a local sparse edge list (.npz / .csv) into BrainState
  5. ModelBackend — swappable Model port: tick (encoded input → neural output), reset, n_neurons, model_id

The 24-neuron touch-reflex loop is a test/demo fixture. It is not the model, and it is not a scaled-down FlyWire brain.

What this is not

  • Not a 140k-neuron FlyWire connectome, upload, or dense 140k×140k matrix
  • Not an arbitrary-scale whole-brain runtime (memory is bounded by the loaded subset)
  • Not caveclient or a built-in biomechanics engine — FlyGym/NeuroMechFly is an optional body extra, not the core product
  • Not an MCP microkernel or FastAPI service — MCP is an optional protocol extra, not the core product
  • Not OpenRouter-the-company, a marketplace, billing system, hosted cloud gateway, or public bio-sim catalog — BioSimRouter is a harness-side port; biorouter is an optional local HTTP process (stdlib, 127.0.0.1 by default)
  • Not a consciousness/upload claim

Install

Python 3.10+ with numpy and scipy. Core install stays numpy/scipy-only:

pip install -e ".[dev]"

FlyGym is optional:

pip install -e ".[flygym]"

MCP is optional (stdio server via FastMCP):

pip install -e ".[mcp]"

biorouter is optional and stdlib-only (no FastAPI). The extra exists so it stays an extension of this repo, not a second product or GitHub repository:

pip install -e ".[biorouter]"

The step loop

from fly_harness import BrainState, FlyHarness
from fly_harness.protocols import Decoder, Encoder

harness = FlyHarness(state, encoder, decoder)
result = harness.step(observation)
action = result.action

step encodes the observation into a length-n_neurons current vector, advances the docked Model one tick, and decodes an action from the neural output.

Default construction still wraps the in-process LIF/rate dynamics (InProcessLifBackend). FlyHarness.step(obs) -> action is the same public call.

BrainState can be snapshotted independently of the harness:

state.save("brain.json")
restored = BrainState.load("brain.json")

Docking modes

Two ways to attach a running Model. This package is the port, not a hosted catalog of sims.

1. Direct — one backend per vendor / deployed sim

from fly_harness import DirectBioSimBackend, FlyHarness

backend = DirectBioSimBackend(n_neurons=8, model_id="vendor.fake")
harness = FlyHarness(encoder=encoder, decoder=decoder, backend=backend)
result = harness.step(observation)

DirectBioSimBackend docks to one running sim. The in-process FakeDeployedSim is enough for tests. A thin HTTP client is available if you already host a sim (url=..., bodies include a model field). There are no FlyWire / CAVE / neuPrint clients and no 140k dense matrices.

2. Router — one entry, select by model id

from fly_harness import BioSimRouter, DirectBioSimBackend, FlyHarness, UnknownModelError

router = BioSimRouter(
    {
        "vendor.a": DirectBioSimBackend(n_neurons=8, model_id="vendor.a"),
        "vendor.b": DirectBioSimBackend(n_neurons=8, model_id="vendor.b"),
    },
    model_id="vendor.a",
)
harness = FlyHarness(encoder=encoder, decoder=decoder, backend=router)
harness.step(observation)
router.use("vendor.b")
harness.step(observation)

# unknown model_id raises UnknownModelError (this is a port, not a marketplace)

BioSimRouter is OpenRouter-shaped: one entry, a model / model_id field, unknown ids fail clearly. Optional remote_url= forwards that model field to biorouter (HttpModelBackend). This repo does not ship a hosted marketplace.

You can also pass a backend positionally: FlyHarness(backend, encoder, decoder).

biorouter

OpenRouter routes existing LLMs. biorouter routes existing deployed biological simulation models. Same idea (model id), different substrate.

This extra is not the harness, not FlyWire dumps, and not a marketplace. It is a local HTTP process (127.0.0.1 by default) that already-deployed harness clients call: HttpModelBackend, DirectBioSimBackend(url=...), BioSimRouter(remote_url=...). Stdlib http.server only — no FastAPI, no billing, no cloud gateway.

pip install -e ".[biorouter]"   # empty extra; core install is enough to run the CLI
biorouter
# or
python -m fly_harness.router --host 127.0.0.1 --port 8765

Endpoints (JSON; bodies/query include an OpenRouter-shaped model field):

  • POST /tick — {"model": "...", "input": [...]} → neural output
  • POST /reset — {"model": "...", "potentials": null | [...]}
  • GET /status?model=...

Default in-process registry: the 24-neuron LIF fixture (fly-harness.in-process-lif) plus FakeDeployedSim ids (fake.deployed, fake.deployed.gain). Unknown model → HTTP 404 → harness UnknownModelError.

from fly_harness import DirectBioSimBackend, FlyHarness

backend = DirectBioSimBackend(
    url="http://127.0.0.1:8765",
    model_id="fake.deployed",
    n_neurons=8,
)
harness = FlyHarness(encoder=encoder, decoder=decoder, backend=backend)
harness.step(observation)

FlyHarness.step(obs) -> action is unchanged. This extra is not a 140k FlyWire runtime and does not download MaleCNS.

Example: biorouter through FlyHarness.step

OpenRouter routes existing LLMs; biorouter routes existing deployed bio-sims. This example starts a local stdlib server (or attaches with --url), docks HttpModelBackend / DirectBioSimBackend(url=...), and calls FlyHarness.step. It uses the 8-neuron FakeDeployedSim fixture — not FlyWire / MaleCNS, not a marketplace.

python examples/biorouter_loop.py
# or, after install:
python -m fly_harness.demo.biorouter_loop
fly-harness-biorouter-demo
# attach to a process you already started:
biorouter --host 127.0.0.1 --port 8765
python examples/biorouter_loop.py --url http://127.0.0.1:8765

Load a sparse connectome

load_connectome maps global neuron ids (any integers) onto contiguous local indices and builds a CSR weight matrix of shape (n_subset, n_subset).

from fly_harness import FlyHarness, load_connectome

loaded = load_connectome("tests/fixtures/mini_circuit.npz")
# loaded.state.weights is scipy.sparse CSR, shape (n_subset, n_subset)
# loaded.neuron_ids maps local index -> global neuron id
# loaded.id_to_index maps global id -> local index

harness = FlyHarness(loaded.state, encoder=..., decoder=...)
result = harness.step(observation)
fly-harness-loader-demo
# or
python -m fly_harness.demo.loaded_circuit

Supported files:

  • NPZ — arrays pre, post, weight (int ids + float weights); optional neuron_ids for the circuit subset
  • CSV — header pre_id,post_id,weight

Pass an explicit subset with neuron_ids=[...] or neuron_ids_file="ids.txt" (one id per line). Only synapses whose pre and post are both in the subset are kept.

Fixture: 24-neuron reflex

Shipped only so the contract is executable without an external connectome file. Left touch biases a right turn (and the reverse). Do not treat this as a biological circuit.

fly-harness-reflex-demo
# or
python -m fly_harness.demo.reflex
from fly_harness import FlyHarness
from fly_harness.demo import ReflexDecoder, ReflexEncoder, build_reflex_connectome
from fly_harness.demo.codec import TouchObservation

state = build_reflex_connectome()
harness = FlyHarness(state, ReflexEncoder(), ReflexDecoder())
result = harness.step(TouchObservation(touch_left=1.0))
print(result.action)  # ReflexAction(turn=1, forward=..., brake=...)

Optional body: FlyGym / NeuroMechFly

This is an extension, not the core product. It does not ship a 140k-neuron brain, does not rewrite FlyGym, and does not run MuJoCo unless you install the extra.

FlyGymEncoder / FlyGymDecoder map NeuroMechFly observations (joint angles, contact forces) and actions (joint targets, per-leg adhesion, optional muscle/tendon commands) onto FlyHarness.step. They work on dicts — no MuJoCo import. FlyGymHarnessEnv is a thin wrapper around an env you construct with FlyGym.

from fly_harness import FlyHarness
from fly_harness.demo.connectome import SENSORY_INDICES, build_reflex_connectome
from fly_harness.flygym import FlyGymDecoder, FlyGymEncoder, FlyGymHarnessEnv

state = build_reflex_connectome()  # fixture circuit, not FlyWire
harness = FlyHarness(
    state,
    FlyGymEncoder(state.n_neurons),
    FlyGymDecoder(state.n_neurons),
    sensory_indices=SENSORY_INDICES,
)
# env = your FlyGym / NeuroMechFly simulation (not constructed here)
body = FlyGymHarnessEnv(env, harness)
obs, info = body.reset()
result = body.step()
# result.action.as_env_dict() -> {"joints": ..., "adhesion": ...}

Gymnasium FlyGym (flygym-gymnasium, installed by the extra; import name flygym or flygym_gymnasium) takes env.step({"joints", "adhesion"}). FlyGym 2.x Simulation is duck-typed via set_actuator_inputs / set_leg_adhesion_states when you pass actuator_type.

Optional protocol: MCP

This is an extension pack, not the microkernel. BrainState / FlyHarness stay numpy/scipy-only and do not import MCP. The default server steps the 24-neuron reflex fixture, not a 140k FlyWire brain.

pip install -e ".[mcp]"
# stdio (default FastMCP transport) — point an MCP host at this command:
fly-harness-mcp
# or
python -m fly_harness.mcp

Tools (no live client required to unit-test the handlers):

  • harness_step(touch_left, touch_right) — one FlyHarness.step
  • harness_reset() — zero potentials and the clock
  • harness_status() — neuron count, timestamp, package version

Handlers are importable without FastMCP:

from fly_harness.mcp import HarnessSession

session = HarnessSession()
print(session.step(touch_left=1.0)["action"])

Example: protocol + body through the harness

This proves optional MCP and FlyGym extras can attach to the same FlyHarness.step. It uses the 24-neuron reflex fixture. It is not a 140k FlyWire or ~166k MaleCNS upload, and it does not rewrite FlyGym or the MCP pack.

python examples/mcp_flygym_loop.py
# or, after install:
python -m fly_harness.demo.composed
fly-harness-composed-demo

Default run uses an in-process body stub (no MuJoCo). --try-flygym attempts a real NeuroMechFly sim and falls back to the stub. --serve starts the optional FastMCP stdio server on that composed session (pip install -e ".[mcp]"):

python examples/mcp_flygym_loop.py --serve

An MCP host that calls harness_step(touch_left, touch_right) therefore steps the harness and applies the decoded joint/adhesion command to the body.

Tests

GitHub Actions on main and pull requests runs pip install -e ".[dev]" then pytest — no FlyGym/MCP extras, no MuJoCo. Skip/mock tests in those extras still pass. The biorouter extra is stdlib-only, so its tests run in that same core CI.

Default local suite does not need MuJoCo. The FlyGym smoke test skips unless flygym imports and a NeuroMechFly sim can start:

pytest
# with the FlyGym extra, still skip-friendly if MuJoCo/display is missing:
pytest -q
# FlyGym smoke only:
pytest tests/test_flygym_adapter.py -k smoke

Model-backend docking (default LIF, fake deployed sim, router model_id switch, unknown-id error) is core and needs no extras:

pytest tests/test_backend.py tests/test_harness.py

The optional biorouter process starts a stdlib server and ticks through HttpModelBackend / FlyHarness (still no FastAPI, no extras required):

pytest tests/test_router.py
# after install:
biorouter --help
python -m fly_harness.router --help

The biorouter example is in-thread by default (no live biorouter daemon). Attaching to a real process is skip-gated on BIOROUTER_URL:

pytest tests/test_biorouter_example.py
python examples/biorouter_loop.py

MCP tests mock FastMCP and do not start a live MCP client. They pass without fly-harness[mcp]. With the extra, a factory smoke checks FastMCP constructs (still no stdio client):

pip install -e ".[mcp]"
pytest tests/test_mcp_extension.py

The composed MCP+FlyGym example is also mock/skip: no MuJoCo and no live MCP client.

pytest tests/test_composed_example.py
python examples/mcp_flygym_loop.py

License

MIT — see LICENSE.

Release files for fly-harness 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fly-harness 0.4.0
File Size Uploaded
fly_harness-0.4.0.tar.gz 42.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for fly-harness 0.4.0
File Interpreter ABI Platform
fly_harness-0.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 89.4 kB

Release files / fly_harness-0.4.0.tar.gz

Download URL fly_harness-0.4.0.tar.gz
Size 42.8 kB
Tags Source
SHA-256 checksum
How to use checksums
2b2d654e69f1effb1ff3e38376bafb56c0fe5d91bfe8ddf01d4b5cf50efe3c68
BLAKE2b-256 checksum
How to use checksums
45d41cd285faa6e7099cbf75ab5182d708046d5bca56c92af1177200607e9cf8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / fly_harness-0.4.0-py3-none-any.whl

Download URL fly_harness-0.4.0-py3-none-any.whl
Size 46.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
812ce6e3f11d922005ce70d45da239c5dc82a3ff619889090a031dedd52e23f6
BLAKE2b-256 checksum
How to use checksums
070f28f596fdbc7655a905e53aca86816f112779b2adf7352f5285baa2af9d50
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Release history Release notifications | RSS feed

0.5.6

2 release files

0.5.5

2 release files

0.5.4

2 release files

0.5.3

2 release files

0.5.2

2 release files

0.5.1

2 release files

0.5.0

2 release files

This release

0.4.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page