A second execution path landed on 2026-08-27: the OpenCode BrainPort. It is additive and not yet the default — be precise about which path you are reading about, because the two do not share a loop.
- The shipping path is unchanged: upstream's engine, driven through the OE-MAX provider broker on
:8787../scripts/run-evolution.shuses this, and every live measurement in docs/benchmarks.md came from it.- The BrainPort path (
oe_max/brain/) makes the model someone else's problem: OpenCode owns provider, credentials, catalog and model switching, andbrain.mode = inheritmeans whatever is selected there is what runs. It is driven by the OpenCode plugin inpackages/opencode-plugin/and runs its own lighter evolution loop inoe_max/brain/evolution.py— it does not go through upstream's controller, MAP-Elites or island machinery.Verification status, stated plainly: 37 tests cover the BrainPort, and 26 acceptance gates pass (
scripts/verify-brainport-acceptance.ps1). All of them run againstNullBrainPortor the stdio worker. No BrainPort run against a live OpenCode host has been recorded, so the claim "zero source changes when a model disappears" is proven structurally — no model ID or provider URL exists in the core, and a test enforces that — and not yet empirically.The legacy provider stack (
oe_max/providers,oe_max/router,oe_max/limiter,control_plane/providers) is marked deprecated and quarantined behindoe_max/brain/legacy_adapter.py. It is still what the default path runs on and must not be deleted until that path moves.scripts/legacy_deletion_gate.pyis the check that says when it may. See oe_max/brain/README.md.
A production fork of OpenEvolve that keeps the evolutionary engine exactly as upstream ships it and adds a real-time control plane around it: a browser Control Center, typed telemetry, a query and control API, health- and capability-aware model routing, and an isolated agent sandbox.
Forked from upstream 411fb59c (v0.3.2, Apache-2.0). The engine is
byte-identical to upstream — see docs/patch-surface.md.
Continuing this work? Start with docs/project/handoff.md — current state, the traps that cost real time to find, and live provider measurements. Then docs/project/roadmap.md for a prioritised queue.
OE-MAX — a local OpenAI-compatible provider broker on 127.0.0.1:8787.
OpenEvolve points at it and knows nothing about providers: routing, the NIM rate
contract, failover, retry and credential ownership all live behind that one
base URL. Routing is per role — reasoner, coder, judge, fast — each with its
own chain, addressed by model alias so the engine needs no changes.
Control plane — telemetry, storage, query/control APIs and a 20-view browser Control Center over live evolution.
Both sit around upstream. The engine is byte-identical.
- Control Center — 20 views over live evolution: lineage graph, MAP-Elites Lab with a generation scrubber, island and migration analysis, candidate inspector with parent diffs, model and evaluator observability, checkpoints, traces, run comparison, system health.
- Real telemetry — a typed event model instrumented at the engine boundary. Every value the UI shows traces to an emitted event; there are no fixtures or placeholder metrics anywhere in the frontend.
- Provider routing — NVIDIA NIM is the primary provider, leading every
role chain, with the keyless OpenCode Zen routes behind it and thirteen other
OpenAI-compatible providers available by configuration. Routing is driven by
live health and verified capabilities. A route whose credential is absent is
filtered out rather than attempted, so a checkout with no
NVIDIA_API_KEYfalls straight through to the keyless routes and still runs. Providers are configuration (configs/oe_max/providers.yaml) and their model ids are discovered from each provider's own listing rather than written down. - Route quality, not just route health — every candidate is attributed to
the model request that generated it, so the question "which route produces
better mutations, and at what cost?" is answerable from a run's own
telemetry.
./scripts/route-experiment.shruns one arm per route and refuses to name a winner on thin evidence. - Fully local mode — Ollama, LM Studio, vLLM and llama.cpp as first-class
providers, with
OE_MAX_LOCAL_ONLYguaranteeing offline by never constructing a commercial adapter at all. Local model ids are discovered from each server's own listing, never configured. - Scientific tools — structured problems routed to whatever computation
backends are installed locally (SymPy, NumPy, SciPy, NetworkX, and more when
present). Results carry a verification status rather than a boolean: there is
no
SUCCESSin the vocabulary, because a numeric result is not a proof. - Agent sandbox — OpenCode as an optional evaluation backend, in an environment that cannot reach the operator's own OpenCode installation.
- Everything upstream still works — the original CLI, configs, examples, checkpoint/resume, and the classic visualizer are preserved and reachable.
git clone https://github.com/ShumpZeke/OpenEvo.git
cd OpenEvo
./bootstrap.sh # environment, deps, storage, UI build, checksbootstrap creates .venv, installs the engine and control plane with the
[dev] extra, initialises storage, builds the Control Center and the OpenCode
plugin, and runs a smoke test. It installs nothing globally and modifies nothing
outside this directory. On Windows use .\bootstrap.ps1.
Requirements: Python 3.10+ and git. Node 20+ is optional — without it the API still runs, it just serves no browser UI.
Then, in two terminals:
./scripts/start-broker.sh # OE-MAX broker → 127.0.0.1:8787
./scripts/run-evolution.sh --task function_minimization --iterations 20Watch it live:
./scripts/dashboard.sh # providers, rate windows, routes, evolution
./run.sh # browser Control Center → 127.0.0.1:8000./scripts/memory.sh # where you left off
./scripts/memory.sh note "..." # leave yourself something
./scripts/memory.sh search "..." # find it againPrints your run history, the high-water score, every run you can resume with the exact command to do it, and your journal. Runs started from the shell are imported automatically, so history means the same thing however you launched. The same thing lives in the Control Center's Memory view.
No API key is required to try it. OpenCode Zen serves four free models
without one — verified 2026-08-26 — and ./scripts/verify-providers.sh shows
what is actually reachable right now. Every other provider is key-gated and
inert until you add its key, so the shipped configuration works as-is.
Windows: .\bootstrap.ps1 then .\run.ps1.
Try it with no API key at all — a local OpenAI-compatible endpoint is included:
./run.sh provider & # 127.0.0.1:8765
./run.sh # then start a run from Experiments,
# or via the API:
curl -X POST http://127.0.0.1:8000/api/control/runs \
-H 'Content-Type: application/json' -d '{
"initial_program": "examples/function_minimization/initial_program.py",
"evaluator": "examples/function_minimization/evaluator.py",
"config_path": "configs/evolution/local_test.yaml",
"iterations": 24, "name": "first-run" }'OE_MAX_LOCAL_ONLY=1 ./scripts/start-broker.sh
./scripts/run-evolution.sh --config configs/oe_max/local.yamlOllama, LM Studio, vLLM and llama.cpp are supported out of the box — all four
speak the OpenAI protocol, so none needed a new adapter. Point one elsewhere
with OE_MAX_OLLAMA_BASE and friends.
OE_MAX_LOCAL_ONLY is a guarantee rather than a preference: the commercial
adapters are never constructed, so there is no object holding a remote URL
and no code path that can dial one by accident. Model ids are read from each
server's own /v1/models at startup — nothing is written down, because what
your machine serves is whatever you pulled.
That listing is not a ranking, so if you have several models say which you want first:
export OE_MAX_LOCAL_MODELS="qwen-evo-text,qwen3:0.6b"Comma-separated, most preferred first, matched as substrings of the model id. Unset keeps the server's own order. Anything you do not name stays routable behind everything you do, so naming a model you have since deleted cannot empty the chain.
Verified end to end on 2026-08-29: a broker in local-only mode discovered twelve
local models and served a complete evolution run, 10 requests, 0 errors, at
+2 ms over calling Ollama directly. Fitting a model to your card is a
measured worked example in
docs/local-tuning.md — the single largest lever there
is reasoning_effort, worth 3× on a 27B and the difference between an
applicable diff and an empty answer.
Full detail, including what is and is not verified: docs/local-mode.md.
NIM is the primary provider and leads every role chain. One key turns it on:
NVIDIA_API_KEY=nvapi-... # primary — leads every role chain
OPENCODE_API_KEY=... # optional; Zen's free routes need no key at allFive of the nine configured NIM models serve, measured with a real key on
2026-08-28: nemotron-3-super-120b-a12b (732 ms with tools — the fastest
working route measured on any provider here), nemotron-3-ultra-550b-a55b
(4.5 s, the flagship reasoner), nemotron-3-nano-30b-a3b, kimi-k3 (11.5 s,
the code specialist) and deepseek-v4-flash-0731 (51 s). The other four are
shipped disabled with the reason attached — a hang, a 400, a 404 entitlement
error and a standing 429. See docs/providers.md for the table.
Without the key nothing breaks: a route whose credential is absent is filtered out rather than attempted, so the chain falls through to the keyless Zen routes and the run still works.
Then run the provider doctor (Models → run provider doctor) to probe what is actually available right now.
.venv/bin/openevolve-run --help # upstream CLI, untouched
./run.sh classic path/to/checkpoint_20 # classic visualizer :8080
./run.sh cli examples/function_minimization/initial_program.py \
examples/function_minimization/evaluator.py --iterations 50The classic visualizer runs as its own service on its own port. It is preserved, not reimplemented — it keeps working whether or not the Control Center is up.
openevolve/ upstream engine — byte-identical, never edited
oe_max/
limiter.py global rate limiter (hard rolling-window invariant)
providers/ adapter, registry, live discovery + smoke tests
broker/ OpenAI-compatible broker OpenEvolve points at
router.py chain selection, failover, retry, truncation escalation
route_quality.py per-route mutation quality (three scarcity views)
health.py circuit breaker and rolling health
evaluation/ G0 validity + G1 four-strength deduplication
search/ mutation taxonomy + discounted Thompson sampling
archives.py hall of fame, Pareto front, novelty, failure memory
verification/ V1 property/metamorphic/randomized checks + counterexamples
execution/ sandboxed candidate execution with real resource ceilings
dashboard.py terminal dashboard
scripts/ operator scripts (.sh and .ps1) + upstream visualizer
control_plane/
telemetry/ event model, redaction, bus, engine instrumentation
storage/ SQLite event log + derived projections
api/ control, query and SSE streaming
providers/ profiles, runtime doctor, health/capability router
analysis/ route quality built from stored telemetry
sandbox/ OpenCode isolation boundary
runner/ engine subprocess lifecycle
web/ Control Center (React + TypeScript + Tailwind)
tests/ upstream suite (untouched) + tests/evolution
./test.shWindows: . est.ps1. Measured on Windows 11 / CPython 3.11.9:
| Suite | Result |
|---|---|
| Upstream OpenEvolve (preserved) | 431 passed, 6 failed (Windows-only, below), 17 slow deselected, 43 subtests |
| Control plane | 562 passed, 10 skipped |
| OE-MAX (broker, limiter, gates, search, archives, verification, execution) | 430 passed, 25 skipped |
| BrainPort (OpenCode brain, worker, plugin contract) | 37 passed |
| Web typecheck | clean |
The six upstream failures are platform, not regression: four are
openevolve/config.py opening YAML with no encoding=, so Windows decodes it
as cp1252 and dies on a non-ASCII byte; one asserts a POSIX absolute path
survives unchanged; one expects ProcessLookupError, which is POSIX os.kill
semantics. All six pass on Linux, which is what CI runs. They are not fixable
here — openevolve/ is byte-identical and stays that way. The full table with
causes is in docs/testing.md.
The upstream suite runs first: a change that breaks it is a regression in the fork, not merely a control-plane bug.
Everything lives in docs/. The ones worth knowing about:
| CONTRIBUTING.md | clone, build, test, and the rules that are load-bearing |
| docs/gotchas.md | defects that produced no error, or the wrong one — read before debugging |
| docs/local-mode.md | running entirely on your own machine, with no credentials |
| docs/design/control-center.md | the browser UI: colour, type, primitives, and the no-invented-data rule |
| docs/architecture.md | how the pieces fit, and why the engine is untouched |
| docs/providers.md | routing policy, and what each provider actually does |
| docs/scientific-tools.md | local computation, and why a result is not a boolean |
| docs/project/status.md | what is built, what is verified, what is not |
| docs/project/roadmap.md | the prioritised queue, with rationale and counter-arguments |
A route can stop existing. A stealth-preview model was this project's
primary until the provider withdrew it — probed and found absent from /models,
answering ModelError: ... is not supported. It has since been removed from
service here entirely. Free status is a runtime-probed value, never rendered as
unlimited, and the model tables are reconciled against each provider's live
listing on every discovery so a withdrawal disables itself. That is also how
openai/gpt-oss-120b and mistralai/codestral-22b-instruct-v0.1 came out of the
NIM chains: probed with a real key, the first hung and the second returned a 404
entitlement error. See docs/providers.md.
A reasoning model can spend its whole budget thinking. One former primary was
measured using 7,986–7,997 of an 8,000-token budget on hidden reasoning,
truncating the visible diff — 5 of 8 iterations produced nothing from
~130-second requests. Several current routes reason too. The broker detects
finish_reason=length and retries with a doubled budget, and max_tokens, the
provider timeout and the client timeout are tuned together. It is also why
judging routes to a different model than mutation: ranking candidates does not
need hidden thought, so it goes to the cheapest fast route rather than the
flagship.
A listed model is not a working model, and an unlisted one is not a model.
Zen lists deepseek-v4-flash-free and returns "Model is unavailable" for it, so
discovery is two-stage: list, then smoke-test. The converse caught a withdrawn
primary and two NVIDIA models this project had configured that were never in
NVIDIA's catalogue at all. The sharpest case: nvidia/nemotron-nano-3-30b-a3b
returns 404 while nvidia/nemotron-3-nano-30b-a3b serves — two transposed
words, both in the catalogue, one of them fictional in practice.
Apache-2.0, inherited from OpenEvolve. Upstream retains copyright over
openevolve/, scripts/visualizer.py, configs/ and examples/.