synth-optimizers 0.2.4__tar.gz → 0.2.6.dev20260622__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.
Files changed (109) hide show
  1. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/Cargo.lock +3 -3
  2. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/Cargo.toml +1 -1
  3. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/PKG-INFO +7 -21
  4. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/README.md +6 -20
  5. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/pyproject.toml +1 -1
  6. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_gepa/openapi/gepa-service-v1.yaml +34 -0
  7. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_gepa/src/codex_app_server.rs +73 -26
  8. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_gepa/src/lib.rs +1610 -163
  9. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_gepa/src/runtime.rs +53 -73
  10. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_gepa/src/service.rs +279 -7
  11. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/agent_runtime/daytona.rs +234 -1
  12. synth_optimizers-0.2.6.dev20260622/rust/crates/synth_optimizer_platform/src/agent_runtime/local.rs +32 -0
  13. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/agent_runtime/mod.rs +4 -1
  14. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/agent_runtime/session.rs +174 -1
  15. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/artifacts.rs +3 -0
  16. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/checkpoints.rs +54 -0
  17. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/config.rs +15 -6
  18. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/event_visualization.rs +3 -1
  19. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/http.rs +2 -2
  20. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/lib.rs +17 -7
  21. synth_optimizers-0.2.6.dev20260622/rust/crates/synth_optimizer_platform/src/observability.rs +91 -0
  22. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/registry.rs +9 -0
  23. synth_optimizers-0.2.6.dev20260622/rust/crates/synth_optimizer_platform/src/storage_maintenance.rs +1075 -0
  24. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/workspace.rs +311 -1
  25. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizers_py/src/lib.rs +80 -6
  26. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/src/synth_optimizers/__init__.py +18 -0
  27. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/src/synth_optimizers/_synth_optimizers.pyi +18 -0
  28. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/src/synth_optimizers/board_server.py +262 -12
  29. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/src/synth_optimizers/cli.py +351 -8
  30. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/src/synth_optimizers/docs/gelo/00-index.md +2 -2
  31. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/src/synth_optimizers/docs/gelo/10-cli.md +4 -3
  32. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/src/synth_optimizers/docs/gelo/20-sdk.md +27 -20
  33. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/src/synth_optimizers/docs/gelo/30-hosted.md +6 -4
  34. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/src/synth_optimizers/docs/gelo/containers/00-overview.md +11 -3
  35. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/src/synth_optimizers/docs/gelo/containers/20-contract.md +4 -3
  36. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/src/synth_optimizers/docs/gepa/10-cli.md +56 -2
  37. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/src/synth_optimizers/docs/gepa/20-sdk.md +12 -0
  38. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/src/synth_optimizers/docs/gepa/containers/00-overview.md +3 -3
  39. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/src/synth_optimizers/docs/gepa/containers/20-contract.md +2 -3
  40. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/src/synth_optimizers/gelo.py +222 -39
  41. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/src/synth_optimizers/gepa.py +31 -3
  42. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/src/synth_optimizers/hosted.py +191 -6
  43. synth_optimizers-0.2.6.dev20260622/src/synth_optimizers/hosted_config.py +12 -0
  44. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/src/synth_optimizers/o11y.py +218 -9
  45. synth_optimizers-0.2.6.dev20260622/src/synth_optimizers/observability.py +161 -0
  46. synth_optimizers-0.2.4/rust/crates/synth_optimizer_platform/src/agent_runtime/local.rs +0 -19
  47. synth_optimizers-0.2.4/rust/crates/synth_optimizer_platform/src/storage_maintenance.rs +0 -318
  48. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_gepa/Cargo.toml +0 -0
  49. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_gepa/README.md +0 -0
  50. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_gepa/src/machines.rs +0 -0
  51. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_gepa/src/pipeline.rs +0 -0
  52. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_gepa/src/planner.rs +0 -0
  53. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_gepa/src/prompting_best_practices.md +0 -0
  54. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/Cargo.toml +0 -0
  55. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/README.md +0 -0
  56. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/agent_runtime/README.md +0 -0
  57. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/agent_runtime/app_server.rs +0 -0
  58. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/agent_runtime/codex_home.rs +0 -0
  59. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/agent_runtime/docker.rs +0 -0
  60. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/agent_runtime/jsonrpc_read_window.rs +0 -0
  61. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/agent_runtime/limits.rs +0 -0
  62. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/agent_runtime/role_agent.rs +0 -0
  63. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/agent_runtime/substrate.rs +0 -0
  64. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/agent_runtime/supervisor.rs +0 -0
  65. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/agent_runtime/usage.rs +0 -0
  66. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/artifact_store.rs +0 -0
  67. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/cache.rs +0 -0
  68. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/candidates.rs +0 -0
  69. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/configured_limits.rs +0 -0
  70. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/container_contract.rs +0 -0
  71. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/data_models.rs +0 -0
  72. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/disk_budget.rs +0 -0
  73. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/error.rs +0 -0
  74. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/events.rs +0 -0
  75. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/evidence.rs +0 -0
  76. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/failures.rs +0 -0
  77. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/fsm.rs +0 -0
  78. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/invariants.rs +0 -0
  79. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/jobs.rs +0 -0
  80. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/levers.rs +0 -0
  81. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/limit_engine.rs +0 -0
  82. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/limits.rs +0 -0
  83. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/operations.rs +0 -0
  84. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/process.rs +0 -0
  85. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/projections.rs +0 -0
  86. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/prompt_program.rs +0 -0
  87. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/resources.rs +0 -0
  88. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/rollouts.rs +0 -0
  89. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/runtime_records.rs +0 -0
  90. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/scores.rs +0 -0
  91. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/sensors.rs +0 -0
  92. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/state_machine.rs +0 -0
  93. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/stopper.rs +0 -0
  94. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizer_platform/src/usage.rs +0 -0
  95. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizers_py/Cargo.toml +0 -0
  96. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/rust/crates/synth_optimizers_py/README.md +0 -0
  97. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/src/synth_optimizers/README.md +0 -0
  98. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/src/synth_optimizers/discovery.py +0 -0
  99. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/src/synth_optimizers/docs/gepa/00-index.md +0 -0
  100. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/src/synth_optimizers/docs/gepa/algorithms/00-overview.md +0 -0
  101. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/src/synth_optimizers/docs/gepa/algorithms/10-pipeline-modes.md +0 -0
  102. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/src/synth_optimizers/docs/gepa/algorithms/20-proposer-and-policies.md +0 -0
  103. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/src/synth_optimizers/docs/gepa/assets/synth-logo.png +0 -0
  104. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/src/synth_optimizers/docs/gepa/containers/10-catalog.md +0 -0
  105. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/src/synth_optimizers/docs_server.py +0 -0
  106. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/src/synth_optimizers/eval_stats.py +0 -0
  107. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/src/synth_optimizers/py.typed +0 -0
  108. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/src/synth_optimizers/sdk.py +0 -0
  109. {synth_optimizers-0.2.4 → synth_optimizers-0.2.6.dev20260622}/src/synth_optimizers/tunnels.py +0 -0
@@ -1192,7 +1192,7 @@ dependencies = [
1192
1192
 
1193
1193
  [[package]]
1194
1194
  name = "synth_gepa"
1195
- version = "0.2.0-dev.20260601"
1195
+ version = "0.2.5"
1196
1196
  dependencies = [
1197
1197
  "base64",
1198
1198
  "reqwest",
@@ -1206,7 +1206,7 @@ dependencies = [
1206
1206
 
1207
1207
  [[package]]
1208
1208
  name = "synth_optimizer_platform"
1209
- version = "0.2.0-dev.20260601"
1209
+ version = "0.2.5"
1210
1210
  dependencies = [
1211
1211
  "fs2",
1212
1212
  "libc",
@@ -1223,7 +1223,7 @@ dependencies = [
1223
1223
 
1224
1224
  [[package]]
1225
1225
  name = "synth_optimizers_py"
1226
- version = "0.2.0-dev.20260601"
1226
+ version = "0.2.5"
1227
1227
  dependencies = [
1228
1228
  "pyo3",
1229
1229
  "serde_json",
@@ -3,7 +3,7 @@ members = ["rust/crates/synth_optimizer_platform", "rust/crates/synth_gepa", "ru
3
3
  resolver = "2"
4
4
 
5
5
  [workspace.package]
6
- version = "0.2.0-dev.20260601"
6
+ version = "0.2.5"
7
7
  edition = "2021"
8
8
  license = "Apache-2.0"
9
9
  authors = ["Synth Laboratories <josh@usesynth.ai>"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: synth-optimizers
3
- Version: 0.2.4
3
+ Version: 0.2.6.dev20260622
4
4
  Requires-Dist: pydantic>=2.0.0
5
5
  Requires-Dist: synth-containers==0.2.0.20260604
6
6
  Requires-Dist: websocket-client>=1.8.0
@@ -24,14 +24,13 @@ Project-URL: Repository, https://github.com/synth-laboratories/optimizers
24
24
  <p align="center">A shared optimizer platform for AI applications — starting with open-source GEPA on a language-agnostic task contract.</p>
25
25
  <p align="center">
26
26
  <a href="https://pypi.org/project/synth-optimizers/">PyPI</a> ·
27
- <a href="https://github.com/synth-laboratories/containers">Containers</a> ·
27
+ <a href="src/synth_optimizers/docs/gepa/containers/20-contract.md">Task contract</a> ·
28
28
  <a href="https://github.com/synth-laboratories/synth-cookbooks-public/tree/main/cookbooks/optimizers/gepa">Cookbooks</a> ·
29
29
  <a href="docs/hosted-optimizers.md">Hosted jobs</a>
30
30
  </p>
31
31
 
32
32
  `synth-optimizers` provides a shared Rust optimizer core for running search
33
- algorithms against any task exposed through the
34
- [`synth-containers`](https://github.com/synth-laboratories/containers) HTTP
33
+ algorithms against any task exposed through the public optimizer HTTP task
35
34
  contract.
36
35
 
37
36
  - **Shared platform core** — reusable Rust machinery for container I/O, workspaces, cache profiles, budgets, telemetry, failure handling, and replayable evidence.
@@ -47,7 +46,8 @@ contract.
47
46
 
48
47
  The shared [`synth_optimizer_platform`](rust/crates/synth_optimizer_platform/)
49
48
  crate is the substrate for optimizer implementations; GEPA is the first public
50
- local algorithm; GELO is hosted-only in the public package (execution in optimizers-beta).
49
+ local algorithm; GELO is hosted-only in the public package and runs on Synth
50
+ hosted optimizer infrastructure.
51
51
  Hosted GEPA/GELO submission is covered in [`docs/hosted-optimizers.md`](docs/hosted-optimizers.md).
52
52
 
53
53
  ## Install
@@ -58,29 +58,15 @@ pip install synth-optimizers
58
58
  uv add synth-optimizers
59
59
  ```
60
60
 
61
- Latest daily dev build:
62
-
63
- ```bash
64
- pip install --pre \
65
- synth-containers==0.2.0.dev202605312141 \
66
- synth-optimizers==0.2.0.dev202606010003
67
-
68
- uv add --prerelease allow \
69
- synth-containers==0.2.0.dev202605312141 \
70
- synth-optimizers==0.2.0.dev202606010003
71
- ```
72
-
73
61
  Install [`uv`](https://github.com/astral-sh/uv) for local development and editable installs.
74
62
 
75
63
  ## Local development
76
64
 
77
- Pair this repo with [`synth-containers`](https://github.com/synth-laboratories/containers)
78
- at matching dev versions, then sync and install editable:
65
+ Sync the repo and install the local Python/Rust extension in editable mode:
79
66
 
80
67
  ```bash
81
68
  cd optimizers
82
69
  uv sync --group dev
83
- uv pip install -e ../containers
84
70
  uv pip install -e .
85
71
  uv run maturin develop --manifest-path rust/crates/synth_optimizers_py/Cargo.toml
86
72
  ```
@@ -201,7 +187,7 @@ Agent docs: [skills/gepa/SKILL.md](skills/gepa/SKILL.md).
201
187
  - [GEPA docs (gepa-ai)](https://gepa-ai.github.io/gepa/) — algorithm overview, case studies, and adapter guides
202
188
  - [GEPA paper](https://arxiv.org/abs/2507.19457) — *GEPA: Reflective Prompt Evolution Can Outperform Reinforcement Learning*
203
189
  - [Cookbooks](https://github.com/synth-laboratories/synth-cookbooks-public/tree/main/cookbooks/optimizers/gepa) — runnable GEPA examples
204
- - [synth-containers](https://github.com/synth-laboratories/containers) — the task contract
190
+ - [GEPA task contract](src/synth_optimizers/docs/gepa/containers/20-contract.md) — the public HTTP task contract
205
191
  - [uv](https://github.com/astral-sh/uv) — Python package and project manager
206
192
  - [GEPA service OpenAPI](rust/crates/synth_gepa/openapi/gepa-service-v1.yaml)
207
193
 
@@ -2,14 +2,13 @@
2
2
  <p align="center">A shared optimizer platform for AI applications — starting with open-source GEPA on a language-agnostic task contract.</p>
3
3
  <p align="center">
4
4
  <a href="https://pypi.org/project/synth-optimizers/">PyPI</a> ·
5
- <a href="https://github.com/synth-laboratories/containers">Containers</a> ·
5
+ <a href="src/synth_optimizers/docs/gepa/containers/20-contract.md">Task contract</a> ·
6
6
  <a href="https://github.com/synth-laboratories/synth-cookbooks-public/tree/main/cookbooks/optimizers/gepa">Cookbooks</a> ·
7
7
  <a href="docs/hosted-optimizers.md">Hosted jobs</a>
8
8
  </p>
9
9
 
10
10
  `synth-optimizers` provides a shared Rust optimizer core for running search
11
- algorithms against any task exposed through the
12
- [`synth-containers`](https://github.com/synth-laboratories/containers) HTTP
11
+ algorithms against any task exposed through the public optimizer HTTP task
13
12
  contract.
14
13
 
15
14
  - **Shared platform core** — reusable Rust machinery for container I/O, workspaces, cache profiles, budgets, telemetry, failure handling, and replayable evidence.
@@ -25,7 +24,8 @@ contract.
25
24
 
26
25
  The shared [`synth_optimizer_platform`](rust/crates/synth_optimizer_platform/)
27
26
  crate is the substrate for optimizer implementations; GEPA is the first public
28
- local algorithm; GELO is hosted-only in the public package (execution in optimizers-beta).
27
+ local algorithm; GELO is hosted-only in the public package and runs on Synth
28
+ hosted optimizer infrastructure.
29
29
  Hosted GEPA/GELO submission is covered in [`docs/hosted-optimizers.md`](docs/hosted-optimizers.md).
30
30
 
31
31
  ## Install
@@ -36,29 +36,15 @@ pip install synth-optimizers
36
36
  uv add synth-optimizers
37
37
  ```
38
38
 
39
- Latest daily dev build:
40
-
41
- ```bash
42
- pip install --pre \
43
- synth-containers==0.2.0.dev202605312141 \
44
- synth-optimizers==0.2.0.dev202606010003
45
-
46
- uv add --prerelease allow \
47
- synth-containers==0.2.0.dev202605312141 \
48
- synth-optimizers==0.2.0.dev202606010003
49
- ```
50
-
51
39
  Install [`uv`](https://github.com/astral-sh/uv) for local development and editable installs.
52
40
 
53
41
  ## Local development
54
42
 
55
- Pair this repo with [`synth-containers`](https://github.com/synth-laboratories/containers)
56
- at matching dev versions, then sync and install editable:
43
+ Sync the repo and install the local Python/Rust extension in editable mode:
57
44
 
58
45
  ```bash
59
46
  cd optimizers
60
47
  uv sync --group dev
61
- uv pip install -e ../containers
62
48
  uv pip install -e .
63
49
  uv run maturin develop --manifest-path rust/crates/synth_optimizers_py/Cargo.toml
64
50
  ```
@@ -179,7 +165,7 @@ Agent docs: [skills/gepa/SKILL.md](skills/gepa/SKILL.md).
179
165
  - [GEPA docs (gepa-ai)](https://gepa-ai.github.io/gepa/) — algorithm overview, case studies, and adapter guides
180
166
  - [GEPA paper](https://arxiv.org/abs/2507.19457) — *GEPA: Reflective Prompt Evolution Can Outperform Reinforcement Learning*
181
167
  - [Cookbooks](https://github.com/synth-laboratories/synth-cookbooks-public/tree/main/cookbooks/optimizers/gepa) — runnable GEPA examples
182
- - [synth-containers](https://github.com/synth-laboratories/containers) — the task contract
168
+ - [GEPA task contract](src/synth_optimizers/docs/gepa/containers/20-contract.md) — the public HTTP task contract
183
169
  - [uv](https://github.com/astral-sh/uv) — Python package and project manager
184
170
  - [GEPA service OpenAPI](rust/crates/synth_gepa/openapi/gepa-service-v1.yaml)
185
171
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "synth-optimizers"
3
- version = "0.2.4"
3
+ version = "0.2.6.dev20260622"
4
4
  description = "Public Synth optimizer tooling with a Rust GEPA core."
5
5
  authors = [{name = "Synth Laboratories", email = "josh@usesynth.ai"}]
6
6
  readme = "README.md"
@@ -143,6 +143,24 @@ paths:
143
143
  '404': { $ref: '#/components/responses/RunNotFound' }
144
144
  '409': { $ref: '#/components/responses/NotTerminal' }
145
145
 
146
+ /runs/{run_id}/storage:
147
+ parameters:
148
+ - $ref: '#/components/parameters/RunId'
149
+ get:
150
+ operationId: getRunStorage
151
+ summary: Inspect local storage for a Run
152
+ description: |
153
+ Returns a read-only local storage report for the Run directory:
154
+ artifact sizes, SQLite hot spots when available, existing compaction
155
+ state, safe actions, and cleanup recommendation.
156
+ responses:
157
+ '200':
158
+ description: Run storage report.
159
+ content:
160
+ application/json:
161
+ schema: { type: object }
162
+ '404': { $ref: '#/components/responses/RunNotFound' }
163
+
146
164
  /runs/{run_id}/state:
147
165
  parameters: [ { $ref: '#/components/parameters/RunId' } ]
148
166
  get:
@@ -525,6 +543,22 @@ paths:
525
543
  run_id: { type: string }
526
544
  bytes: { type: integer }
527
545
 
546
+ /workspace/storage:
547
+ get:
548
+ operationId: getWorkspaceStorage
549
+ summary: Workspace storage health report
550
+ description: |
551
+ Returns root-level storage health for the local workspace: total bytes,
552
+ reclaimable bytes, stale partial artifacts, threshold configuration, and
553
+ alerts for oversized roots/runs or stale partials. This route is
554
+ read-only and never deletes or compacts artifacts.
555
+ responses:
556
+ '200':
557
+ description: Workspace storage health report.
558
+ content:
559
+ application/json:
560
+ schema: { type: object }
561
+
528
562
  /workspace/prune:
529
563
  post:
530
564
  operationId: pruneWorkspace
@@ -24,6 +24,9 @@ const OPENROUTER_GROK43_CACHED_INPUT_USD_PER_MILLION: f64 = 0.20;
24
24
  const OPENROUTER_GROK43_OUTPUT_USD_PER_MILLION: f64 = 2.50;
25
25
  const DEEPSEEK_INPUT_USD_PER_MILLION: f64 = 0.27;
26
26
  const DEEPSEEK_OUTPUT_USD_PER_MILLION: f64 = 1.10;
27
+ const CHAT_COMPLETIONS_PROPOSER_MAX_TOKENS: u64 = 8_192;
28
+ const CHAT_COMPLETIONS_PROPOSER_MAX_EVIDENCE_CHARS_PER_FILE: usize = 32_000;
29
+ const CHAT_COMPLETIONS_PROPOSER_MAX_TOTAL_EVIDENCE_CHARS: usize = 160_000;
27
30
 
28
31
  pub(crate) struct CodexProposerInput<'a> {
29
32
  pub config: &'a SynthOptimizerConfig,
@@ -186,9 +189,10 @@ pub(crate) fn run_deepseek_chat_proposer(input: CodexProposerInput<'_>) -> Resul
186
189
  }
187
190
  ],
188
191
  "response_format": {"type": "json_object"},
189
- // Manifests for large minibatches can be long; small caps truncate the JSON
190
- // mid-string ("EOF while parsing").
191
- "max_tokens": 32768,
192
+ // Keep OpenRouter-compatible requests inside common 128k context windows.
193
+ // Two GEPA proposals fit comfortably in 8k while 32k can make the
194
+ // provider reject otherwise-valid evidence packets before generation.
195
+ "max_tokens": CHAT_COMPLETIONS_PROPOSER_MAX_TOKENS,
192
196
  "stream": false
193
197
  });
194
198
  // DeepSeek-specific switch that suppresses its reasoning channel so `content` is the
@@ -238,7 +242,7 @@ pub(crate) fn run_deepseek_chat_proposer(input: CodexProposerInput<'_>) -> Resul
238
242
  Some(pair) => pair,
239
243
  None => {
240
244
  return Err(OptimizerError::Proposer(format!(
241
- "DeepSeek chat proposer failed after 3 attempts: {}",
245
+ "chat-completions proposer failed after 3 attempts: {}",
242
246
  last_err.unwrap_or_else(|| "unknown error".to_string())
243
247
  )))
244
248
  }
@@ -250,13 +254,13 @@ pub(crate) fn run_deepseek_chat_proposer(input: CodexProposerInput<'_>) -> Resul
250
254
  let proposals = proposals_from_manifest(&manifest)?;
251
255
  let evidence_warnings = manifest_evidence_warnings(&input, &manifest, &proposals);
252
256
  let usage = chat_response.get("usage").cloned().ok_or_else(|| {
253
- OptimizerError::Proposer("DeepSeek chat proposer response missing usage".to_string())
257
+ OptimizerError::Proposer("chat-completions proposer response missing usage".to_string())
254
258
  })?;
255
259
  let usage = normalize_proposer_usage(input.config, &model, usage);
256
260
  write_deepseek_chat_artifacts(&input, &request, &chat_response)?;
257
261
  write_workspace_pack_manifest(&input.workspace_dir)?;
258
262
  Ok(json!({
259
- "backend": "deepseek_chat",
263
+ "backend": input.config.proposer.backend,
260
264
  "runtime_substrate": "local",
261
265
  "workspace": input.workspace_dir,
262
266
  "manifest": manifest,
@@ -275,6 +279,7 @@ fn deepseek_chat_prompt(input: &CodexProposerInput<'_>) -> Result<String> {
275
279
  prompt.push_str("\n\nManifest schema:\n");
276
280
  prompt.push_str(&proposal_schema(input));
277
281
  prompt.push_str("\n\nUse these workspace files as the complete evidence packet.\n");
282
+ let mut remaining_evidence_chars = CHAT_COMPLETIONS_PROPOSER_MAX_TOTAL_EVIDENCE_CHARS;
278
283
  for path in [
279
284
  "state/proposer_metadata.json",
280
285
  "state/task_info.json",
@@ -290,14 +295,17 @@ fn deepseek_chat_prompt(input: &CodexProposerInput<'_>) -> Result<String> {
290
295
  let file_path = input.workspace_dir.join(path);
291
296
  let text = fs::read_to_string(&file_path)
292
297
  .map_err(|source| OptimizerError::io(&file_path, source))?;
293
- // Bound each evidence file so the prompt stays within the model context
294
- // window. With large minibatches + accepted candidates, rollouts.json /
295
- // scores.json grow past 1M tokens otherwise (DeepSeek 400: context length).
296
- const MAX_EVIDENCE_CHARS: usize = 120_000;
297
- let text = if text.chars().count() > MAX_EVIDENCE_CHARS {
298
- let head: String = text.chars().take(MAX_EVIDENCE_CHARS).collect();
299
- format!("{head}\n…[truncated to {MAX_EVIDENCE_CHARS} chars to fit context budget]…")
298
+ let file_budget =
299
+ remaining_evidence_chars.min(CHAT_COMPLETIONS_PROPOSER_MAX_EVIDENCE_CHARS_PER_FILE);
300
+ let text_char_count = text.chars().count();
301
+ let text = if file_budget == 0 {
302
+ "[omitted: chat-completions proposer evidence budget exhausted]".to_string()
303
+ } else if text_char_count > file_budget {
304
+ let head: String = text.chars().take(file_budget).collect();
305
+ remaining_evidence_chars = remaining_evidence_chars.saturating_sub(file_budget);
306
+ format!("{head}\n…[truncated to {file_budget} chars to fit context budget]…")
300
307
  } else {
308
+ remaining_evidence_chars = remaining_evidence_chars.saturating_sub(text_char_count);
301
309
  text
302
310
  };
303
311
  prompt.push_str("\n\n--- ");
@@ -1198,6 +1206,8 @@ Rules:
1198
1206
  - Do not leave required evidence fields empty. `evidence.candidate_comparison` must be a non-empty summary of the parent prompt against observed reflection evidence, and `evidence.example_ids_used` must include at least one concrete example id inspected from the state files.
1199
1207
  - Proposals should aim to generalize. Add structural sections (role, task, output rules, examples) and domain-specific rules only when they are task-valid.
1200
1208
  - {proposal_policy}
1209
+ - `proposed_payload` keys must be exactly the mutable target module ids from `state/run_context.json`. Do not use chat-message keys such as `content`, `role`, or `modules` unless one of those strings is literally listed as a target module id.
1210
+ - For a single target module, write the target module id directly as the only `proposed_payload` key. For example, if `state/run_context.json.target_modules == ["stage2_system"]`, write `"proposed_payload": {{"stage2_system": "<full replacement instruction>"}}`, not `"content"` and not a `modules` array.
1201
1211
  - At most one proposal may be conservative. The remaining proposals must be very ambitious, high-variance, task-specific updates that could plausibly produce substantially better task performance than the parent, and each rationale must name the failure clusters it attacks.
1202
1212
  - Shoot for large wins. Mild parent clarifications are wasted candidate budget unless they are the single conservative control.
1203
1213
  - Do not waste candidates on generic output-contract polish, canonical-label reminders, or baseline paraphrases unless the dominant failures are actually output-format failures.
@@ -1290,7 +1300,7 @@ fn proposer_visible_frame(
1290
1300
  }
1291
1301
  if !matches!(
1292
1302
  frame.evaluation_stage.as_str(),
1293
- "candidate_minibatch" | "parent_minibatch_reference" | "reflection"
1303
+ "seed_full_train" | "candidate_minibatch" | "parent_minibatch_reference" | "reflection"
1294
1304
  ) {
1295
1305
  return false;
1296
1306
  }
@@ -1298,6 +1308,45 @@ fn proposer_visible_frame(
1298
1308
  visible_task_ids.is_empty() || visible_task_ids.contains(&frame.task_id)
1299
1309
  }
1300
1310
 
1311
+ fn frame_rollout_trace(
1312
+ input: &CodexProposerInput<'_>,
1313
+ frame: &synth_optimizer_platform::SensorFrame,
1314
+ ) -> Value {
1315
+ let inline = frame
1316
+ .metadata
1317
+ .get("rollout_trace")
1318
+ .cloned()
1319
+ .unwrap_or(Value::Null);
1320
+ if rollout_trace_has_proposer_evidence(&inline) {
1321
+ return inline;
1322
+ }
1323
+ read_frame_rollout_trace_artifact(input, frame).unwrap_or(inline)
1324
+ }
1325
+
1326
+ fn rollout_trace_has_proposer_evidence(trace: &Value) -> bool {
1327
+ json_path(trace, &["task_payload", "example"])
1328
+ .and_then(Value::as_object)
1329
+ .is_some()
1330
+ || string_path(trace, &["summary", "expected"]).is_some()
1331
+ || string_path(trace, &["summary", "prediction"]).is_some()
1332
+ }
1333
+
1334
+ fn read_frame_rollout_trace_artifact(
1335
+ input: &CodexProposerInput<'_>,
1336
+ frame: &synth_optimizer_platform::SensorFrame,
1337
+ ) -> Option<Value> {
1338
+ let sensor_frame_id = frame.sensor_frame_id.trim();
1339
+ if sensor_frame_id.is_empty() {
1340
+ return None;
1341
+ }
1342
+ let run_dir = input.workspace_dir.parent()?.parent()?;
1343
+ let path = run_dir
1344
+ .join("rollout_traces")
1345
+ .join(format!("{sensor_frame_id}.json"));
1346
+ let raw = fs::read_to_string(path).ok()?;
1347
+ serde_json::from_str(&raw).ok()
1348
+ }
1349
+
1301
1350
  fn rollouts_read_model(input: &CodexProposerInput<'_>) -> Value {
1302
1351
  let mut rows = Vec::new();
1303
1352
  for candidate in input.candidates {
@@ -1310,11 +1359,11 @@ fn rollouts_read_model(input: &CodexProposerInput<'_>) -> Value {
1310
1359
  if !proposer_visible_frame(input, frame) {
1311
1360
  continue;
1312
1361
  }
1313
- let rollout_trace = frame.metadata.get("rollout_trace").unwrap_or(&Value::Null);
1314
- let summary = json_path(rollout_trace, &["summary"])
1362
+ let rollout_trace = frame_rollout_trace(input, frame);
1363
+ let summary = json_path(&rollout_trace, &["summary"])
1315
1364
  .cloned()
1316
1365
  .unwrap_or(Value::Null);
1317
- let outcome = json_path(rollout_trace, &["outcome"])
1366
+ let outcome = json_path(&rollout_trace, &["outcome"])
1318
1367
  .cloned()
1319
1368
  .unwrap_or_else(|| {
1320
1369
  json!({
@@ -1323,7 +1372,7 @@ fn rollouts_read_model(input: &CodexProposerInput<'_>) -> Value {
1323
1372
  "reward": frame.reward,
1324
1373
  })
1325
1374
  });
1326
- let example = json_path(rollout_trace, &["task_payload", "example"])
1375
+ let example = json_path(&rollout_trace, &["task_payload", "example"])
1327
1376
  .cloned()
1328
1377
  .unwrap_or_else(|| {
1329
1378
  json!({
@@ -1474,11 +1523,11 @@ fn proposer_example_row(
1474
1523
  candidate: &CandidateRecord,
1475
1524
  frame: &synth_optimizer_platform::SensorFrame,
1476
1525
  ) -> Value {
1477
- let rollout_trace = frame.metadata.get("rollout_trace").unwrap_or(&Value::Null);
1478
- let summary = json_path(rollout_trace, &["summary"])
1526
+ let rollout_trace = frame_rollout_trace(input, frame);
1527
+ let summary = json_path(&rollout_trace, &["summary"])
1479
1528
  .cloned()
1480
1529
  .unwrap_or(Value::Null);
1481
- let outcome = json_path(rollout_trace, &["outcome"])
1530
+ let outcome = json_path(&rollout_trace, &["outcome"])
1482
1531
  .cloned()
1483
1532
  .unwrap_or_else(|| {
1484
1533
  json!({
@@ -1487,7 +1536,7 @@ fn proposer_example_row(
1487
1536
  "reward": frame.reward,
1488
1537
  })
1489
1538
  });
1490
- let example = json_path(rollout_trace, &["task_payload", "example"])
1539
+ let example = json_path(&rollout_trace, &["task_payload", "example"])
1491
1540
  .cloned()
1492
1541
  .unwrap_or_else(|| {
1493
1542
  json!({
@@ -2251,10 +2300,8 @@ fn reflective_frame_value(
2251
2300
  .find(|value| !value.trim().is_empty())
2252
2301
  .unwrap_or_default()
2253
2302
  .to_string();
2254
- let rollout_trace = frame
2255
- .metadata
2256
- .get("rollout_trace")
2257
- .and_then(Value::as_object);
2303
+ let rollout_trace = frame_rollout_trace(input, frame);
2304
+ let rollout_trace = rollout_trace.as_object();
2258
2305
  let trace_summary = rollout_trace
2259
2306
  .and_then(|trace| trace.get("summary"))
2260
2307
  .cloned()