openevolve 0.3.0__tar.gz → 0.3.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {openevolve-0.3.0/openevolve.egg-info → openevolve-0.3.2}/PKG-INFO +40 -5
- {openevolve-0.3.0 → openevolve-0.3.2}/README.md +39 -4
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/_version.py +1 -1
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/api.py +6 -2
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/config.py +15 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/controller.py +0 -29
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/database.py +18 -15
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/embedding.py +14 -2
- openevolve-0.3.2/openevolve/llm/__init__.py +16 -0
- openevolve-0.3.2/openevolve/llm/claude_code.py +138 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/llm/ensemble.py +20 -4
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/llm/openai.py +3 -1
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/process_parallel.py +59 -3
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/utils/format_utils.py +10 -3
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/utils/metrics_utils.py +6 -2
- {openevolve-0.3.0 → openevolve-0.3.2/openevolve.egg-info}/PKG-INFO +40 -5
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve.egg-info/SOURCES.txt +7 -0
- openevolve-0.3.2/tests/test_boolean_metrics.py +75 -0
- openevolve-0.3.2/tests/test_claude_code_llm.py +160 -0
- openevolve-0.3.2/tests/test_embedding.py +75 -0
- openevolve-0.3.2/tests/test_format_utils.py +15 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_process_parallel.py +59 -1
- openevolve-0.3.2/tests/test_provider_propagation.py +83 -0
- openevolve-0.3.2/tests/test_sample_from_island_inspirations.py +135 -0
- openevolve-0.3.0/openevolve/llm/__init__.py +0 -9
- {openevolve-0.3.0 → openevolve-0.3.2}/LICENSE +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/MANIFEST.in +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/__init__.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/cli.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/evaluation_result.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/evaluator.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/evolution_trace.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/llm/base.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/novelty_judge.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/prompt/__init__.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/prompt/sampler.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/prompt/templates.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/prompts/defaults/diff_user.txt +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/prompts/defaults/evaluation.txt +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/prompts/defaults/evaluator_system_message.txt +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/prompts/defaults/evolution_history.txt +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/prompts/defaults/fragments.json +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/prompts/defaults/full_rewrite_user.txt +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/prompts/defaults/inspiration_program.txt +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/prompts/defaults/inspirations_section.txt +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/prompts/defaults/previous_attempt.txt +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/prompts/defaults/system_message.txt +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/prompts/defaults/system_message_changes_description.txt +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/prompts/defaults/system_message_with_changes_description.txt +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/prompts/defaults/top_program.txt +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/prompts/defaults/user_message_with_changes_description.txt +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/test_regional_endpoint.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/utils/__init__.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/utils/async_utils.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/utils/code_utils.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve/utils/trace_export_utils.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve.egg-info/dependency_links.txt +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve.egg-info/entry_points.txt +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve.egg-info/requires.txt +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/openevolve.egg-info/top_level.txt +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/pyproject.toml +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/setup.cfg +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/setup.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_api.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_api_key_from_env.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_artifacts.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_artifacts_integration.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_cascade_validation.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_changes_description.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_checkpoint_resume.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_cli_model_override.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_code_utils.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_concurrent_island_access.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_database.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_database_cleanup.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_early_stopping_config.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_evaluator_timeout.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_evolution_trace.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_feature_stats_persistence.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_file_extension_preservation.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_grid_stability.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_initial_program_artifacts.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_island_child_placement.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_island_isolation.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_island_map_elites.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_island_migration.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_island_parent_consistency.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_island_tracking.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_iteration_counting.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_llm_config_optional_params.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_llm_ensemble.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_map_elites_features.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_migration_no_duplicates.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_model_parameter_demo.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_novelty_asyncio_issue.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_openai_model_detection.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_orphan_program_removal.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_population_elite_protection.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_process_parallel_fix.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_prompt_inspiration_dedup.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_prompt_sampler.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_prompt_sampler_comprehensive.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_reasoning_effort_config.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_regional_endpoint.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_sample_from_island_ratios.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_snapshot_artifacts_limit.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_template_dir_resolution.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_utils.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_valid_configs.py +0 -0
- {openevolve-0.3.0 → openevolve-0.3.2}/tests/test_visualization_sanitization.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openevolve
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Open-source implementation of AlphaEvolve
|
|
5
5
|
Author: codelion
|
|
6
6
|
License: Apache-2.0
|
|
@@ -34,9 +34,8 @@ Dynamic: license-file
|
|
|
34
34
|
*Turn your LLMs into autonomous code optimizers that discover breakthrough algorithms*
|
|
35
35
|
|
|
36
36
|
<p align="center">
|
|
37
|
-
<a href="https://github.com/algorithmicsuperintelligence/openevolve/stargazers"><img src="https://img.shields.io/github/stars/algorithmicsuperintelligence/openevolve?style=social" alt="GitHub stars"></a>
|
|
38
37
|
<a href="https://pypi.org/project/openevolve/"><img src="https://img.shields.io/pypi/v/openevolve" alt="PyPI version"></a>
|
|
39
|
-
<a href="https://
|
|
38
|
+
<a href="https://pepy.tech/projects/openevolve"><img src="https://static.pepy.tech/personalized-badge/openevolve?period=monthly&units=INTERNATIONAL_SYSTEM&left_color=GREY&right_color=GREEN&left_text=downloads%2Fmonth" alt="PyPI Downloads"></a>
|
|
40
39
|
<a href="https://github.com/algorithmicsuperintelligence/openevolve/blob/main/LICENSE"><img src="https://img.shields.io/github/license/algorithmicsuperintelligence/openevolve" alt="License"></a>
|
|
41
40
|
</p>
|
|
42
41
|
|
|
@@ -227,7 +226,7 @@ OpenEvolve implements a sophisticated **evolutionary coding pipeline** that goes
|
|
|
227
226
|
<details>
|
|
228
227
|
<summary><b>Advanced LLM Integration</b></summary>
|
|
229
228
|
|
|
230
|
-
- **Universal API**: Works with OpenAI, Google, local models, and proxies
|
|
229
|
+
- **Universal API**: Works with OpenAI, Google, Claude Code CLI, local models, and proxies
|
|
231
230
|
- **Intelligent Ensembles**: Weighted combinations with sophisticated fallback
|
|
232
231
|
- **Test-Time Compute**: Enhanced reasoning through proxy systems (see [OptiLLM setup](#llm-provider-setup))
|
|
233
232
|
- **Plugin Ecosystem**: Support for advanced reasoning plugins
|
|
@@ -258,7 +257,7 @@ OpenEvolve implements a sophisticated **evolutionary coding pipeline** that goes
|
|
|
258
257
|
|
|
259
258
|
### Requirements
|
|
260
259
|
- **Python**: 3.10+
|
|
261
|
-
- **LLM Access**: Any OpenAI-compatible API
|
|
260
|
+
- **LLM Access**: Any OpenAI-compatible API, or [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code)
|
|
262
261
|
- **Optional**: Docker for containerized runs
|
|
263
262
|
|
|
264
263
|
### Installation Options
|
|
@@ -381,6 +380,35 @@ llm:
|
|
|
381
380
|
|
|
382
381
|
</details>
|
|
383
382
|
|
|
383
|
+
<details>
|
|
384
|
+
<summary><b>🔮 Claude Code CLI (No API Key)</b></summary>
|
|
385
|
+
|
|
386
|
+
Use the [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code) as the LLM backend — no API keys needed, authentication uses the CLI's OAuth session.
|
|
387
|
+
|
|
388
|
+
```bash
|
|
389
|
+
# Install and authenticate
|
|
390
|
+
npm install -g @anthropic-ai/claude-code
|
|
391
|
+
claude login
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
```yaml
|
|
395
|
+
# config.yaml
|
|
396
|
+
llm:
|
|
397
|
+
provider: "claude_code"
|
|
398
|
+
models:
|
|
399
|
+
- name: "sonnet"
|
|
400
|
+
weight: 0.8
|
|
401
|
+
max_tokens: 16000
|
|
402
|
+
max_budget_usd: 1.0
|
|
403
|
+
- name: "haiku"
|
|
404
|
+
weight: 0.2
|
|
405
|
+
max_tokens: 8000
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
See the [Claude Code quickstart example](examples/claude_code_quickstart/) for a complete walkthrough.
|
|
409
|
+
|
|
410
|
+
</details>
|
|
411
|
+
|
|
384
412
|
## Examples Gallery
|
|
385
413
|
|
|
386
414
|
<div align="center">
|
|
@@ -489,6 +517,10 @@ database:
|
|
|
489
517
|
migration_interval: 20
|
|
490
518
|
feature_dimensions: ["complexity", "diversity", "performance"]
|
|
491
519
|
|
|
520
|
+
# Optional novelty filtering with Gemini embeddings
|
|
521
|
+
embedding_model: "gemini-embedding-001"
|
|
522
|
+
similarity_threshold: 0.99
|
|
523
|
+
|
|
492
524
|
evaluator:
|
|
493
525
|
enable_artifacts: true # Error feedback to LLM
|
|
494
526
|
cascade_evaluation: true # Multi-stage testing
|
|
@@ -505,6 +537,9 @@ prompt:
|
|
|
505
537
|
use_template_stochasticity: true # Randomized prompts
|
|
506
538
|
```
|
|
507
539
|
|
|
540
|
+
For Gemini embeddings, set `GEMINI_API_KEY`. `GOOGLE_API_KEY` is also supported
|
|
541
|
+
as a fallback. OpenEvolve uses Google's OpenAI-compatible endpoint automatically.
|
|
542
|
+
|
|
508
543
|
<details>
|
|
509
544
|
<summary><b>🎯 Feature Engineering</b></summary>
|
|
510
545
|
|
|
@@ -9,9 +9,8 @@
|
|
|
9
9
|
*Turn your LLMs into autonomous code optimizers that discover breakthrough algorithms*
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
12
|
-
<a href="https://github.com/algorithmicsuperintelligence/openevolve/stargazers"><img src="https://img.shields.io/github/stars/algorithmicsuperintelligence/openevolve?style=social" alt="GitHub stars"></a>
|
|
13
12
|
<a href="https://pypi.org/project/openevolve/"><img src="https://img.shields.io/pypi/v/openevolve" alt="PyPI version"></a>
|
|
14
|
-
<a href="https://
|
|
13
|
+
<a href="https://pepy.tech/projects/openevolve"><img src="https://static.pepy.tech/personalized-badge/openevolve?period=monthly&units=INTERNATIONAL_SYSTEM&left_color=GREY&right_color=GREEN&left_text=downloads%2Fmonth" alt="PyPI Downloads"></a>
|
|
15
14
|
<a href="https://github.com/algorithmicsuperintelligence/openevolve/blob/main/LICENSE"><img src="https://img.shields.io/github/license/algorithmicsuperintelligence/openevolve" alt="License"></a>
|
|
16
15
|
</p>
|
|
17
16
|
|
|
@@ -202,7 +201,7 @@ OpenEvolve implements a sophisticated **evolutionary coding pipeline** that goes
|
|
|
202
201
|
<details>
|
|
203
202
|
<summary><b>Advanced LLM Integration</b></summary>
|
|
204
203
|
|
|
205
|
-
- **Universal API**: Works with OpenAI, Google, local models, and proxies
|
|
204
|
+
- **Universal API**: Works with OpenAI, Google, Claude Code CLI, local models, and proxies
|
|
206
205
|
- **Intelligent Ensembles**: Weighted combinations with sophisticated fallback
|
|
207
206
|
- **Test-Time Compute**: Enhanced reasoning through proxy systems (see [OptiLLM setup](#llm-provider-setup))
|
|
208
207
|
- **Plugin Ecosystem**: Support for advanced reasoning plugins
|
|
@@ -233,7 +232,7 @@ OpenEvolve implements a sophisticated **evolutionary coding pipeline** that goes
|
|
|
233
232
|
|
|
234
233
|
### Requirements
|
|
235
234
|
- **Python**: 3.10+
|
|
236
|
-
- **LLM Access**: Any OpenAI-compatible API
|
|
235
|
+
- **LLM Access**: Any OpenAI-compatible API, or [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code)
|
|
237
236
|
- **Optional**: Docker for containerized runs
|
|
238
237
|
|
|
239
238
|
### Installation Options
|
|
@@ -356,6 +355,35 @@ llm:
|
|
|
356
355
|
|
|
357
356
|
</details>
|
|
358
357
|
|
|
358
|
+
<details>
|
|
359
|
+
<summary><b>🔮 Claude Code CLI (No API Key)</b></summary>
|
|
360
|
+
|
|
361
|
+
Use the [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code) as the LLM backend — no API keys needed, authentication uses the CLI's OAuth session.
|
|
362
|
+
|
|
363
|
+
```bash
|
|
364
|
+
# Install and authenticate
|
|
365
|
+
npm install -g @anthropic-ai/claude-code
|
|
366
|
+
claude login
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
```yaml
|
|
370
|
+
# config.yaml
|
|
371
|
+
llm:
|
|
372
|
+
provider: "claude_code"
|
|
373
|
+
models:
|
|
374
|
+
- name: "sonnet"
|
|
375
|
+
weight: 0.8
|
|
376
|
+
max_tokens: 16000
|
|
377
|
+
max_budget_usd: 1.0
|
|
378
|
+
- name: "haiku"
|
|
379
|
+
weight: 0.2
|
|
380
|
+
max_tokens: 8000
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
See the [Claude Code quickstart example](examples/claude_code_quickstart/) for a complete walkthrough.
|
|
384
|
+
|
|
385
|
+
</details>
|
|
386
|
+
|
|
359
387
|
## Examples Gallery
|
|
360
388
|
|
|
361
389
|
<div align="center">
|
|
@@ -464,6 +492,10 @@ database:
|
|
|
464
492
|
migration_interval: 20
|
|
465
493
|
feature_dimensions: ["complexity", "diversity", "performance"]
|
|
466
494
|
|
|
495
|
+
# Optional novelty filtering with Gemini embeddings
|
|
496
|
+
embedding_model: "gemini-embedding-001"
|
|
497
|
+
similarity_threshold: 0.99
|
|
498
|
+
|
|
467
499
|
evaluator:
|
|
468
500
|
enable_artifacts: true # Error feedback to LLM
|
|
469
501
|
cascade_evaluation: true # Multi-stage testing
|
|
@@ -480,6 +512,9 @@ prompt:
|
|
|
480
512
|
use_template_stochasticity: true # Randomized prompts
|
|
481
513
|
```
|
|
482
514
|
|
|
515
|
+
For Gemini embeddings, set `GEMINI_API_KEY`. `GOOGLE_API_KEY` is also supported
|
|
516
|
+
as a fallback. OpenEvolve uses Google's OpenAI-compatible endpoint automatically.
|
|
517
|
+
|
|
483
518
|
<details>
|
|
484
519
|
<summary><b>🎯 Feature Engineering</b></summary>
|
|
485
520
|
|
|
@@ -37,6 +37,8 @@ def run_evolution(
|
|
|
37
37
|
iterations: Optional[int] = None,
|
|
38
38
|
output_dir: Optional[str] = None,
|
|
39
39
|
cleanup: bool = True,
|
|
40
|
+
target_score: Optional[float] = None,
|
|
41
|
+
checkpoint_path: Optional[str] = None,
|
|
40
42
|
) -> EvolutionResult:
|
|
41
43
|
"""
|
|
42
44
|
Run evolution with flexible inputs - the main library API
|
|
@@ -90,7 +92,7 @@ def run_evolution(
|
|
|
90
92
|
)
|
|
91
93
|
"""
|
|
92
94
|
return asyncio.run(
|
|
93
|
-
_run_evolution_async(initial_program, evaluator, config, iterations, output_dir, cleanup)
|
|
95
|
+
_run_evolution_async(initial_program, evaluator, config, iterations, output_dir, cleanup, target_score, checkpoint_path)
|
|
94
96
|
)
|
|
95
97
|
|
|
96
98
|
|
|
@@ -101,6 +103,8 @@ async def _run_evolution_async(
|
|
|
101
103
|
iterations: Optional[int],
|
|
102
104
|
output_dir: Optional[str],
|
|
103
105
|
cleanup: bool,
|
|
106
|
+
target_score: Optional[float] = None,
|
|
107
|
+
checkpoint_path: Optional[str] = None,
|
|
104
108
|
) -> EvolutionResult:
|
|
105
109
|
"""Async implementation of run_evolution"""
|
|
106
110
|
|
|
@@ -156,7 +160,7 @@ async def _run_evolution_async(
|
|
|
156
160
|
output_dir=actual_output_dir,
|
|
157
161
|
)
|
|
158
162
|
|
|
159
|
-
best_program = await controller.run(iterations=iterations)
|
|
163
|
+
best_program = await controller.run(iterations=iterations,target_score=target_score,checkpoint_path=checkpoint_path)
|
|
160
164
|
|
|
161
165
|
# Prepare result
|
|
162
166
|
best_score = 0.0
|
|
@@ -56,6 +56,9 @@ class LLMModelConfig:
|
|
|
56
56
|
api_key: Optional[str] = None
|
|
57
57
|
name: str = None
|
|
58
58
|
|
|
59
|
+
# LLM provider: "openai" (default), "claude_code" (Claude Code CLI)
|
|
60
|
+
provider: Optional[str] = None
|
|
61
|
+
|
|
59
62
|
# Custom LLM client
|
|
60
63
|
init_client: Optional[Callable] = None
|
|
61
64
|
|
|
@@ -79,6 +82,9 @@ class LLMModelConfig:
|
|
|
79
82
|
# Reasoning parameters
|
|
80
83
|
reasoning_effort: Optional[str] = None
|
|
81
84
|
|
|
85
|
+
# Claude Code CLI budget per call (USD)
|
|
86
|
+
max_budget_usd: Optional[float] = None
|
|
87
|
+
|
|
82
88
|
# Manual mode (human-in-the-loop)
|
|
83
89
|
manual_mode: Optional[bool] = None
|
|
84
90
|
_manual_queue_dir: Optional[str] = None
|
|
@@ -168,6 +174,12 @@ class LLMConfig(LLMModelConfig):
|
|
|
168
174
|
|
|
169
175
|
# Update models with shared configuration values
|
|
170
176
|
shared_config = {
|
|
177
|
+
# `provider` must be shared: without it a top-level `llm.provider`
|
|
178
|
+
# (e.g. "claude_code") never reaches the per-model configs, every model
|
|
179
|
+
# keeps provider=None, and LLMEnsemble silently routes them all to the
|
|
180
|
+
# OpenAI backend. Propagation uses overwrite=False, so an explicit
|
|
181
|
+
# per-model provider still wins.
|
|
182
|
+
"provider": self.provider,
|
|
171
183
|
"api_base": self.api_base,
|
|
172
184
|
"api_key": self.api_key,
|
|
173
185
|
"temperature": self.temperature,
|
|
@@ -222,6 +234,9 @@ class LLMConfig(LLMModelConfig):
|
|
|
222
234
|
|
|
223
235
|
# Update models with shared configuration values
|
|
224
236
|
shared_config = {
|
|
237
|
+
# See the note in __post_init__: `provider` has to be propagated here too,
|
|
238
|
+
# or rebuilding the models drops a top-level provider back to None.
|
|
239
|
+
"provider": self.provider,
|
|
225
240
|
"api_base": self.api_base,
|
|
226
241
|
"api_key": self.api_key,
|
|
227
242
|
"temperature": self.temperature,
|
|
@@ -25,34 +25,6 @@ from openevolve.utils.format_utils import format_improvement_safe, format_metric
|
|
|
25
25
|
logger = logging.getLogger(__name__)
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
def _format_metrics(metrics: Dict[str, Any]) -> str:
|
|
29
|
-
"""Safely format metrics, handling both numeric and string values"""
|
|
30
|
-
formatted_parts = []
|
|
31
|
-
for name, value in metrics.items():
|
|
32
|
-
if isinstance(value, (int, float)) and not isinstance(value, bool):
|
|
33
|
-
try:
|
|
34
|
-
formatted_parts.append(f"{name}={value:.4f}")
|
|
35
|
-
except (ValueError, TypeError):
|
|
36
|
-
formatted_parts.append(f"{name}={value}")
|
|
37
|
-
else:
|
|
38
|
-
formatted_parts.append(f"{name}={value}")
|
|
39
|
-
return ", ".join(formatted_parts)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def _format_improvement(improvement: Dict[str, Any]) -> str:
|
|
43
|
-
"""Safely format improvement metrics"""
|
|
44
|
-
formatted_parts = []
|
|
45
|
-
for name, diff in improvement.items():
|
|
46
|
-
if isinstance(diff, (int, float)) and not isinstance(diff, bool):
|
|
47
|
-
try:
|
|
48
|
-
formatted_parts.append(f"{name}={diff:+.4f}")
|
|
49
|
-
except (ValueError, TypeError):
|
|
50
|
-
formatted_parts.append(f"{name}={diff}")
|
|
51
|
-
else:
|
|
52
|
-
formatted_parts.append(f"{name}={diff}")
|
|
53
|
-
return ", ".join(formatted_parts)
|
|
54
|
-
|
|
55
|
-
|
|
56
28
|
class OpenEvolve:
|
|
57
29
|
"""
|
|
58
30
|
Main controller for OpenEvolve
|
|
@@ -268,7 +240,6 @@ class OpenEvolve:
|
|
|
268
240
|
Best program found
|
|
269
241
|
"""
|
|
270
242
|
max_iterations = iterations or self.config.max_iterations
|
|
271
|
-
|
|
272
243
|
# Determine starting iteration
|
|
273
244
|
start_iteration = 0
|
|
274
245
|
if checkpoint_path and os.path.exists(checkpoint_path):
|
|
@@ -467,21 +467,13 @@ class ProgramDatabase:
|
|
|
467
467
|
parent = self._sample_from_island_weighted(island_id)
|
|
468
468
|
sampling_mode = "weighted"
|
|
469
469
|
|
|
470
|
-
# Select inspirations
|
|
470
|
+
# Select inspirations using the same elite/diversity-aware strategy as sample().
|
|
471
|
+
# Pass the requested island explicitly so parallel workers remain isolated even
|
|
472
|
+
# if an archive fallback returns a parent whose metadata points elsewhere.
|
|
471
473
|
if num_inspirations is None:
|
|
472
474
|
num_inspirations = 5 # Default for backward compatibility
|
|
473
475
|
|
|
474
|
-
|
|
475
|
-
other_programs = [pid for pid in island_programs if pid != parent.id]
|
|
476
|
-
|
|
477
|
-
if len(other_programs) < num_inspirations:
|
|
478
|
-
# Not enough programs in island, use what we have
|
|
479
|
-
inspiration_ids = other_programs
|
|
480
|
-
else:
|
|
481
|
-
# Sample inspirations
|
|
482
|
-
inspiration_ids = random.sample(other_programs, num_inspirations)
|
|
483
|
-
|
|
484
|
-
inspirations = [self.programs[pid] for pid in inspiration_ids if pid in self.programs]
|
|
476
|
+
inspirations = self._sample_inspirations(parent, n=num_inspirations, island_id=island_id)
|
|
485
477
|
|
|
486
478
|
logger.debug(
|
|
487
479
|
f"Sampled parent {parent.id} and {len(inspirations)} inspirations from island {island_id} "
|
|
@@ -1569,7 +1561,9 @@ class ProgramDatabase:
|
|
|
1569
1561
|
parent_id = random.choice(valid_archive)
|
|
1570
1562
|
return self.programs[parent_id]
|
|
1571
1563
|
|
|
1572
|
-
def _sample_inspirations(
|
|
1564
|
+
def _sample_inspirations(
|
|
1565
|
+
self, parent: Program, n: int = 5, island_id: Optional[int] = None
|
|
1566
|
+
) -> List[Program]:
|
|
1573
1567
|
"""
|
|
1574
1568
|
Sample inspiration programs for the next evolution step.
|
|
1575
1569
|
|
|
@@ -1579,14 +1573,23 @@ class ProgramDatabase:
|
|
|
1579
1573
|
Args:
|
|
1580
1574
|
parent: Parent program
|
|
1581
1575
|
n: Number of inspirations to sample
|
|
1576
|
+
island_id: Explicit island to sample from. If omitted, use the
|
|
1577
|
+
parent program's island metadata.
|
|
1582
1578
|
|
|
1583
1579
|
Returns:
|
|
1584
1580
|
List of inspiration programs from the current island
|
|
1585
1581
|
"""
|
|
1586
1582
|
inspirations = []
|
|
1587
1583
|
|
|
1588
|
-
#
|
|
1589
|
-
|
|
1584
|
+
# Prefer an explicitly requested island. This matters for
|
|
1585
|
+
# sample_from_island(), where archive fallback may return a parent whose
|
|
1586
|
+
# metadata belongs to a different island.
|
|
1587
|
+
if island_id is None:
|
|
1588
|
+
parent_island = parent.metadata.get("island", self.current_island)
|
|
1589
|
+
else:
|
|
1590
|
+
parent_island = island_id
|
|
1591
|
+
|
|
1592
|
+
parent_island %= len(self.islands)
|
|
1590
1593
|
|
|
1591
1594
|
# Get all programs from the current island
|
|
1592
1595
|
island_program_ids = list(self.islands[parent_island])
|
|
@@ -3,10 +3,11 @@ Adapted from SakanaAI/ShinkaEvolve (Apache-2.0 License)
|
|
|
3
3
|
Original source: https://github.com/SakanaAI/ShinkaEvolve/blob/main/shinka/llm/embedding.py
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
+
import logging
|
|
6
7
|
import os
|
|
8
|
+
from typing import List, Union
|
|
9
|
+
|
|
7
10
|
import openai
|
|
8
|
-
from typing import Union, List
|
|
9
|
-
import logging
|
|
10
11
|
|
|
11
12
|
logger = logging.getLogger(__name__)
|
|
12
13
|
|
|
@@ -22,6 +23,10 @@ AZURE_EMBEDDING_MODELS = [
|
|
|
22
23
|
"azure-text-embedding-3-large",
|
|
23
24
|
]
|
|
24
25
|
|
|
26
|
+
GEMINI_EMBEDDING_MODELS = [
|
|
27
|
+
"gemini-embedding-001",
|
|
28
|
+
]
|
|
29
|
+
|
|
25
30
|
OPENAI_EMBEDDING_COSTS = {
|
|
26
31
|
"text-embedding-3-small": 0.02 / M,
|
|
27
32
|
"text-embedding-3-large": 0.13 / M,
|
|
@@ -53,6 +58,13 @@ class EmbeddingClient:
|
|
|
53
58
|
api_version=os.getenv("AZURE_API_VERSION"),
|
|
54
59
|
azure_endpoint=os.getenv("AZURE_API_ENDPOINT"),
|
|
55
60
|
)
|
|
61
|
+
elif model_name in GEMINI_EMBEDDING_MODELS:
|
|
62
|
+
gemini_api_key = os.getenv("GEMINI_API_KEY") or os.getenv("GOOGLE_API_KEY")
|
|
63
|
+
client = openai.OpenAI(
|
|
64
|
+
api_key=gemini_api_key,
|
|
65
|
+
base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
|
|
66
|
+
)
|
|
67
|
+
model_to_use = model_name
|
|
56
68
|
else:
|
|
57
69
|
raise ValueError(f"Invalid embedding model: {model_name}")
|
|
58
70
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""
|
|
2
|
+
LLM module initialization
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from openevolve.llm.base import LLMInterface
|
|
6
|
+
from openevolve.llm.ensemble import LLMEnsemble
|
|
7
|
+
from openevolve.llm.openai import OpenAILLM
|
|
8
|
+
from openevolve.llm.claude_code import ClaudeCodeLLM, init_claude_code_client
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"LLMInterface",
|
|
12
|
+
"OpenAILLM",
|
|
13
|
+
"ClaudeCodeLLM",
|
|
14
|
+
"init_claude_code_client",
|
|
15
|
+
"LLMEnsemble",
|
|
16
|
+
]
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Claude Code CLI interface for LLMs.
|
|
3
|
+
|
|
4
|
+
Uses the Claude Code CLI (`claude -p`) as a non-interactive LLM backend,
|
|
5
|
+
enabling OpenEvolve to run with Anthropic's Claude models without requiring
|
|
6
|
+
direct API keys — authentication is handled by the CLI's OAuth session.
|
|
7
|
+
|
|
8
|
+
Usage in config.yaml:
|
|
9
|
+
llm:
|
|
10
|
+
provider: "claude_code"
|
|
11
|
+
models:
|
|
12
|
+
- name: "sonnet"
|
|
13
|
+
weight: 1.0
|
|
14
|
+
max_tokens: 16000
|
|
15
|
+
timeout: 300
|
|
16
|
+
|
|
17
|
+
Or inject programmatically:
|
|
18
|
+
from openevolve.llm.claude_code import init_claude_code_client
|
|
19
|
+
for model_cfg in config.llm.models:
|
|
20
|
+
model_cfg.init_client = init_claude_code_client
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
import asyncio
|
|
24
|
+
import logging
|
|
25
|
+
import subprocess
|
|
26
|
+
from typing import Dict, List, Optional
|
|
27
|
+
|
|
28
|
+
from openevolve.llm.base import LLMInterface
|
|
29
|
+
|
|
30
|
+
logger = logging.getLogger(__name__)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ClaudeCodeLLM(LLMInterface):
|
|
34
|
+
"""LLM interface that uses the Claude Code CLI for generation.
|
|
35
|
+
|
|
36
|
+
Requires `claude` CLI to be installed and authenticated
|
|
37
|
+
(run `claude login` first).
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def __init__(self, model_cfg=None):
|
|
41
|
+
self.model = getattr(model_cfg, "name", "sonnet")
|
|
42
|
+
self.system_message = getattr(model_cfg, "system_message", None)
|
|
43
|
+
self.max_tokens = getattr(model_cfg, "max_tokens", 16000)
|
|
44
|
+
self.timeout = getattr(model_cfg, "timeout", 300)
|
|
45
|
+
self.weight = getattr(model_cfg, "weight", 1.0)
|
|
46
|
+
self.retries = getattr(model_cfg, "retries", 3)
|
|
47
|
+
self.retry_delay = getattr(model_cfg, "retry_delay", 5)
|
|
48
|
+
self.max_budget_usd = getattr(model_cfg, "max_budget_usd", 1.0)
|
|
49
|
+
self.cwd = getattr(model_cfg, "cwd", None)
|
|
50
|
+
logger.info(f"Initialized ClaudeCodeLLM with model: {self.model}")
|
|
51
|
+
|
|
52
|
+
async def generate(self, prompt: str, **kwargs) -> str:
|
|
53
|
+
sys_msg = kwargs.pop("system_message", self.system_message) or ""
|
|
54
|
+
return await self.generate_with_context(
|
|
55
|
+
system_message=sys_msg,
|
|
56
|
+
messages=[{"role": "user", "content": prompt}],
|
|
57
|
+
**kwargs,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
async def generate_with_context(
|
|
61
|
+
self, system_message: str, messages: List[Dict[str, str]], **kwargs
|
|
62
|
+
) -> str:
|
|
63
|
+
user_content = "\n\n".join(
|
|
64
|
+
m.get("content", "") for m in messages if m.get("role") == "user"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
cmd = [
|
|
68
|
+
"claude",
|
|
69
|
+
"-p",
|
|
70
|
+
"--model",
|
|
71
|
+
self.model,
|
|
72
|
+
"--no-session-persistence",
|
|
73
|
+
"--output-format",
|
|
74
|
+
"text",
|
|
75
|
+
]
|
|
76
|
+
if system_message:
|
|
77
|
+
cmd.extend(["--system-prompt", system_message])
|
|
78
|
+
|
|
79
|
+
budget = kwargs.get("max_budget_usd", self.max_budget_usd)
|
|
80
|
+
cmd.extend(["--max-budget-usd", str(budget)])
|
|
81
|
+
|
|
82
|
+
cmd.append(user_content)
|
|
83
|
+
|
|
84
|
+
timeout = kwargs.get("timeout", self.timeout)
|
|
85
|
+
retries = kwargs.get("retries", self.retries)
|
|
86
|
+
retry_delay = kwargs.get("retry_delay", self.retry_delay)
|
|
87
|
+
|
|
88
|
+
loop = asyncio.get_event_loop()
|
|
89
|
+
for attempt in range(retries + 1):
|
|
90
|
+
try:
|
|
91
|
+
result = await asyncio.wait_for(
|
|
92
|
+
loop.run_in_executor(None, lambda: self._run_cli(cmd, timeout)),
|
|
93
|
+
timeout=timeout + 30,
|
|
94
|
+
)
|
|
95
|
+
return result
|
|
96
|
+
except asyncio.TimeoutError:
|
|
97
|
+
if attempt < retries:
|
|
98
|
+
logger.warning(
|
|
99
|
+
f"Claude Code CLI timeout on attempt {attempt + 1}/{retries + 1}. Retrying..."
|
|
100
|
+
)
|
|
101
|
+
await asyncio.sleep(retry_delay)
|
|
102
|
+
else:
|
|
103
|
+
logger.error(f"All {retries + 1} attempts failed with timeout")
|
|
104
|
+
raise
|
|
105
|
+
except Exception as e:
|
|
106
|
+
if attempt < retries:
|
|
107
|
+
logger.warning(
|
|
108
|
+
f"Claude Code CLI error on attempt {attempt + 1}/{retries + 1}: {e}. Retrying..."
|
|
109
|
+
)
|
|
110
|
+
await asyncio.sleep(retry_delay)
|
|
111
|
+
else:
|
|
112
|
+
logger.error(f"All {retries + 1} attempts failed with error: {e}")
|
|
113
|
+
raise
|
|
114
|
+
|
|
115
|
+
def _run_cli(self, cmd: list, timeout: int) -> str:
|
|
116
|
+
try:
|
|
117
|
+
result = subprocess.run(
|
|
118
|
+
cmd,
|
|
119
|
+
capture_output=True,
|
|
120
|
+
text=True,
|
|
121
|
+
timeout=timeout,
|
|
122
|
+
cwd=self.cwd,
|
|
123
|
+
)
|
|
124
|
+
if result.returncode != 0:
|
|
125
|
+
stderr = result.stderr.strip()
|
|
126
|
+
if stderr:
|
|
127
|
+
logger.warning(f"Claude CLI stderr: {stderr[:500]}")
|
|
128
|
+
output = result.stdout.strip()
|
|
129
|
+
if not output:
|
|
130
|
+
raise RuntimeError(f"Empty response from Claude CLI. stderr: {result.stderr[:500]}")
|
|
131
|
+
return output
|
|
132
|
+
except subprocess.TimeoutExpired:
|
|
133
|
+
raise asyncio.TimeoutError("Claude CLI subprocess timed out")
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def init_claude_code_client(model_cfg):
|
|
137
|
+
"""Factory function compatible with OpenEvolve's init_client config hook."""
|
|
138
|
+
return ClaudeCodeLLM(model_cfg)
|
|
@@ -13,6 +13,25 @@ from openevolve.config import LLMModelConfig
|
|
|
13
13
|
|
|
14
14
|
logger = logging.getLogger(__name__)
|
|
15
15
|
|
|
16
|
+
_PROVIDER_REGISTRY = {
|
|
17
|
+
"openai": lambda cfg: OpenAILLM(cfg),
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
try:
|
|
21
|
+
from openevolve.llm.claude_code import ClaudeCodeLLM
|
|
22
|
+
_PROVIDER_REGISTRY["claude_code"] = lambda cfg: ClaudeCodeLLM(cfg)
|
|
23
|
+
except ImportError:
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _create_model(model_cfg: LLMModelConfig) -> LLMInterface:
|
|
28
|
+
if model_cfg.init_client:
|
|
29
|
+
return model_cfg.init_client(model_cfg)
|
|
30
|
+
provider = getattr(model_cfg, "provider", None)
|
|
31
|
+
if provider and provider in _PROVIDER_REGISTRY:
|
|
32
|
+
return _PROVIDER_REGISTRY[provider](model_cfg)
|
|
33
|
+
return OpenAILLM(model_cfg)
|
|
34
|
+
|
|
16
35
|
|
|
17
36
|
class LLMEnsemble:
|
|
18
37
|
"""Ensemble of LLMs"""
|
|
@@ -21,10 +40,7 @@ class LLMEnsemble:
|
|
|
21
40
|
self.models_cfg = models_cfg
|
|
22
41
|
|
|
23
42
|
# Initialize models from the configuration
|
|
24
|
-
self.models = [
|
|
25
|
-
model_cfg.init_client(model_cfg) if model_cfg.init_client else OpenAILLM(model_cfg)
|
|
26
|
-
for model_cfg in models_cfg
|
|
27
|
-
]
|
|
43
|
+
self.models = [_create_model(model_cfg) for model_cfg in models_cfg]
|
|
28
44
|
|
|
29
45
|
# Extract and normalize model weights
|
|
30
46
|
self.weights = [model.weight for model in models_cfg]
|
|
@@ -155,9 +155,11 @@ class OpenAILLM(LLMInterface):
|
|
|
155
155
|
"model": self.model,
|
|
156
156
|
"messages": formatted_messages,
|
|
157
157
|
"temperature": kwargs.get("temperature", self.temperature),
|
|
158
|
-
"top_p": kwargs.get("top_p", self.top_p),
|
|
159
158
|
"max_tokens": kwargs.get("max_tokens", self.max_tokens),
|
|
160
159
|
}
|
|
160
|
+
top_p = kwargs.get("top_p", self.top_p)
|
|
161
|
+
if top_p is not None:
|
|
162
|
+
params["top_p"] = top_p
|
|
161
163
|
|
|
162
164
|
# Handle reasoning_effort for open source reasoning models.
|
|
163
165
|
reasoning_effort = kwargs.get("reasoning_effort", self.reasoning_effort)
|