Highlights
- Pluggable island selection and population strategies (#493): an optional island selector plus decision-returning hooks for admission, MAP-Elites cell replacement, archive membership, eviction and migration.
ProgramDatabasevalidates the decisions and still owns every mutation. Default behaviour is unchanged. - LLM token usage tracking (#490):
prompt_tokens,completion_tokensandtotal_tokensare captured from OpenAI-compatible responses, carried back from workers, stored in program metadata and prompt logs, and reported per iteration and as a run total. - GitHub Copilot CLI backend (#482): a
copilot_cliprovider that drivescopilot -pnon-interactively, using the CLI's own login, so no API key is needed. - Opt-in EVOLVE-BLOCK enforcement (#494): with
enforce_evolve_blocks: true, every edit outside theEVOLVE-BLOCK-START/ENDregions is reverted to the parent code before evaluation, and responses that change or remove the markers are rejected. Markers are now recognised after any comment syntax (#,//,/*,--,%), so evolve blocks work in C, Java and other languages. Defaultfalse. - Custom embedding endpoint (#494):
database.embedding_api_base(orOPENAI_EMBEDDING_BASE_URL) points novelty embeddings at any OpenAI-compatible endpoint, such as OpenRouter or a local server.
Bug fixes
- Malformed SEARCH/REPLACE diffs are rejected (#494, thanks @brriorda): an extra or unmatched
=======,<<<<<<< SEARCHor>>>>>>> REPLACEline used to be written into the candidate program. The whole response is now rejected before anything is applied. Customdiff_patternvalues keep their own grammar. - Unapplied and no-op diffs are no longer evaluated (#494): a SEARCH block that matched nothing was skipped silently, so the unchanged parent was evaluated again as a "new" child. Responses where no block matches, or where the result equals the parent (including identical full rewrites), are now reported as errors, and partial matches log a warning. SEARCH lines also match when they differ only in trailing whitespace.
- Timed-out evaluations no longer hang the worker (#485): evaluations run on a dedicated executor, so a timed-out stage returns promptly instead of blocking
asyncio.run()shutdown. NOT_NOVELresponses are parsed correctly (#486), including explained and Markdown-wrapped answers.- Non-streaming chat completions (#494): requests are sent with
stream=False. Endpoints that stream by default no longer fail with'str' object has no attribute 'choices', and a raw streamed response now raises a clear error.
Security
- Visualizer debug mode is off by default (#494): the Flask debugger is now opt-in with
--debug.
Performance
- Checkpoints create the
programs/directory once per save instead of once per program (#478).
Docs
- Corrected the
OpenEvolveconfig usage examples (#479). - Documented
enforce_evolve_blocksandembedding_api_base.
Behaviour changes to note
- Diffs that change nothing, and full rewrites identical to the parent, are now rejected instead of evaluated.
extract_diffsandapply_diffraiseValueErroron malformed delimiters when using the default diff pattern.
Closes #106, #346, #422, #426, #436.
Full changelog: v0.3.2...v0.4.0