synth-optimizers 0.2.6.dev20260622__tar.gz → 0.2.6.dev20260626__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.
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/PKG-INFO +1 -1
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/pyproject.toml +1 -1
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/src/synth_optimizers/docs/gelo/30-hosted.md +25 -6
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/src/synth_optimizers/gelo.py +209 -26
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/src/synth_optimizers/gepa.py +51 -1
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/src/synth_optimizers/hosted.py +82 -2
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/src/synth_optimizers/tunnels.py +5 -10
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/Cargo.lock +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/Cargo.toml +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/README.md +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_gepa/Cargo.toml +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_gepa/README.md +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_gepa/openapi/gepa-service-v1.yaml +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_gepa/src/codex_app_server.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_gepa/src/lib.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_gepa/src/machines.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_gepa/src/pipeline.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_gepa/src/planner.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_gepa/src/prompting_best_practices.md +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_gepa/src/runtime.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_gepa/src/service.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/Cargo.toml +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/README.md +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/agent_runtime/README.md +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/agent_runtime/app_server.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/agent_runtime/codex_home.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/agent_runtime/daytona.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/agent_runtime/docker.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/agent_runtime/jsonrpc_read_window.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/agent_runtime/limits.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/agent_runtime/local.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/agent_runtime/mod.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/agent_runtime/role_agent.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/agent_runtime/session.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/agent_runtime/substrate.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/agent_runtime/supervisor.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/agent_runtime/usage.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/artifact_store.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/artifacts.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/cache.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/candidates.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/checkpoints.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/config.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/configured_limits.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/container_contract.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/data_models.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/disk_budget.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/error.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/event_visualization.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/events.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/evidence.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/failures.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/fsm.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/http.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/invariants.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/jobs.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/levers.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/lib.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/limit_engine.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/limits.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/observability.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/operations.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/process.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/projections.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/prompt_program.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/registry.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/resources.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/rollouts.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/runtime_records.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/scores.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/sensors.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/state_machine.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/stopper.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/storage_maintenance.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/usage.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizer_platform/src/workspace.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizers_py/Cargo.toml +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizers_py/README.md +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/rust/crates/synth_optimizers_py/src/lib.rs +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/src/synth_optimizers/README.md +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/src/synth_optimizers/__init__.py +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/src/synth_optimizers/_synth_optimizers.pyi +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/src/synth_optimizers/board_server.py +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/src/synth_optimizers/cli.py +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/src/synth_optimizers/discovery.py +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/src/synth_optimizers/docs/gelo/00-index.md +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/src/synth_optimizers/docs/gelo/10-cli.md +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/src/synth_optimizers/docs/gelo/20-sdk.md +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/src/synth_optimizers/docs/gelo/containers/00-overview.md +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/src/synth_optimizers/docs/gelo/containers/20-contract.md +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/src/synth_optimizers/docs/gepa/00-index.md +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/src/synth_optimizers/docs/gepa/10-cli.md +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/src/synth_optimizers/docs/gepa/20-sdk.md +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/src/synth_optimizers/docs/gepa/algorithms/00-overview.md +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/src/synth_optimizers/docs/gepa/algorithms/10-pipeline-modes.md +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/src/synth_optimizers/docs/gepa/algorithms/20-proposer-and-policies.md +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/src/synth_optimizers/docs/gepa/assets/synth-logo.png +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/src/synth_optimizers/docs/gepa/containers/00-overview.md +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/src/synth_optimizers/docs/gepa/containers/10-catalog.md +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/src/synth_optimizers/docs/gepa/containers/20-contract.md +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/src/synth_optimizers/docs_server.py +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/src/synth_optimizers/eval_stats.py +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/src/synth_optimizers/hosted_config.py +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/src/synth_optimizers/o11y.py +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/src/synth_optimizers/observability.py +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/src/synth_optimizers/py.typed +0 -0
- {synth_optimizers-0.2.6.dev20260622 → synth_optimizers-0.2.6.dev20260626}/src/synth_optimizers/sdk.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "synth-optimizers"
|
|
3
|
-
version = "0.2.6.
|
|
3
|
+
version = "0.2.6.dev20260626"
|
|
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"
|
|
@@ -17,13 +17,13 @@ Durable status, events, and artifacts land in backend PG/Redis/S3 (see storage s
|
|
|
17
17
|
[container contract](#/containers/contract)). **Env checkpoint blobs for resume stay in your
|
|
18
18
|
container** — hosted storage does not replace them.
|
|
19
19
|
|
|
20
|
-
##
|
|
20
|
+
## 72-hour launch promo
|
|
21
21
|
|
|
22
|
-
The GELO launch promo gives
|
|
23
|
-
spend grant
|
|
24
|
-
|
|
25
|
-
`$1` of `optimizer_go_ex_llm_spend` headroom before the run is queued. In-container
|
|
26
|
-
LLM calls are still owned by the task container.
|
|
22
|
+
The June 26 GELO launch promo gives eligible organizations a 72-hour hosted Go-Ex
|
|
23
|
+
proposer-spend grant after claim. Hosted `go-ex` submits auto-claim the grant when
|
|
24
|
+
promo capacity remains, attach the GELO launch promo grant, and preflight at least
|
|
25
|
+
`$1` of `optimizer_go_ex_llm_spend` headroom before the run is queued. In-container
|
|
26
|
+
policy LLM calls are still owned by the task container.
|
|
27
27
|
|
|
28
28
|
The promo grant is reused for repeat submits from the same organization. Each organization
|
|
29
29
|
may have one hosted GELO run in `queued` or `running` status at once while using the promo.
|
|
@@ -35,6 +35,9 @@ Promo submits require GPT models for the five paid proposer roles:
|
|
|
35
35
|
`aux_consolidate_proposer`, and `aux_consolidate_hill_climb_proposer`.
|
|
36
36
|
`theme_verifier_agent` and `terminator_agent` are intentionally exempt.
|
|
37
37
|
|
|
38
|
+
The active campaign id is `gelo_free_72h_20260626`. Check the status route for the
|
|
39
|
+
exact grant expiry, remaining promo capacity, headroom snapshot, and first GELO run id.
|
|
40
|
+
|
|
38
41
|
## Public API routes
|
|
39
42
|
|
|
40
43
|
| Route | GELO use |
|
|
@@ -69,6 +72,22 @@ Supported providers are `synth_tunnel`, `cloudflared`, and `ngrok`. Only SynthTu
|
|
|
69
72
|
|
|
70
73
|
Container must be up and pass `GET /health` before submit.
|
|
71
74
|
|
|
75
|
+
## GameBench Rust presets
|
|
76
|
+
|
|
77
|
+
GameBench Rust task containers can be submitted through the same tunnel path:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
synth-optimizers gelo submit \
|
|
81
|
+
--preset craftax_gamebench_rust_smoke \
|
|
82
|
+
--tunnel-url http://127.0.0.1:8098 \
|
|
83
|
+
--tunnel-provider synth_tunnel \
|
|
84
|
+
--follow
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Use `rogue_gamebench_rust_smoke` for the Rogue prototype container. These presets
|
|
88
|
+
materialize explicit `gamebench/<task>/rust` substrate metadata so blog evidence can
|
|
89
|
+
separate GameBench Rust replications from legacy Craftax, NLE, or Crafter rows.
|
|
90
|
+
|
|
72
91
|
## Staging vs prod claims
|
|
73
92
|
|
|
74
93
|
| Claim | Minimum gate |
|
|
@@ -50,6 +50,8 @@ class GeloPresetName(StrEnum):
|
|
|
50
50
|
CRAFTER = "crafter"
|
|
51
51
|
CRAFTER_SMOKE = "crafter_smoke"
|
|
52
52
|
SOKOBAN_SMOKE = "sokoban_smoke"
|
|
53
|
+
CRAFTAX_GAMEBENCH_RUST_SMOKE = "craftax_gamebench_rust_smoke"
|
|
54
|
+
ROGUE_GAMEBENCH_RUST_SMOKE = "rogue_gamebench_rust_smoke"
|
|
53
55
|
NETHACK_SMOKE = "nethack_smoke"
|
|
54
56
|
DUNGEONGRID_PLUS_PICO = "dungeongrid_plus_pico"
|
|
55
57
|
|
|
@@ -284,9 +286,7 @@ _AGENT_ROLES: tuple[GeloProposerRole, ...] = (
|
|
|
284
286
|
|
|
285
287
|
_ALL_ROLES: tuple[GeloProposerRole, ...] = _PROPOSER_ROLES + _AGENT_ROLES
|
|
286
288
|
|
|
287
|
-
_GO_EX_FLAT_KEYS: frozenset[str] = frozenset(
|
|
288
|
-
field.name for field in fields(GeloEngineSection)
|
|
289
|
-
)
|
|
289
|
+
_GO_EX_FLAT_KEYS: frozenset[str] = frozenset(field.name for field in fields(GeloEngineSection))
|
|
290
290
|
|
|
291
291
|
_TASKSET_KEYS: frozenset[str] = frozenset(
|
|
292
292
|
{
|
|
@@ -373,6 +373,19 @@ class GeloPreset:
|
|
|
373
373
|
"policy_provider": "groq",
|
|
374
374
|
"policy_api_key_env": "GROQ_API_KEY",
|
|
375
375
|
}
|
|
376
|
+
elif preset_name in {
|
|
377
|
+
GeloPresetName.CRAFTAX_GAMEBENCH_RUST_SMOKE,
|
|
378
|
+
GeloPresetName.ROGUE_GAMEBENCH_RUST_SMOKE,
|
|
379
|
+
}:
|
|
380
|
+
defaults = {
|
|
381
|
+
"proposer_rounds": 1,
|
|
382
|
+
"train_seed_count": 4,
|
|
383
|
+
"heldout_seed_count": 2,
|
|
384
|
+
"max_rollouts": 64,
|
|
385
|
+
"policy_model": "openai/gpt-oss-120b",
|
|
386
|
+
"policy_provider": "groq",
|
|
387
|
+
"policy_api_key_env": "GROQ_API_KEY",
|
|
388
|
+
}
|
|
376
389
|
else:
|
|
377
390
|
raise GeloMaterializeError(
|
|
378
391
|
f"preset {preset_name.value!r} is not available in the public package yet"
|
|
@@ -392,6 +405,14 @@ class GeloPreset:
|
|
|
392
405
|
def sokoban_smoke(cls, **overrides: Any) -> "GeloPreset":
|
|
393
406
|
return cls.from_name(GeloPresetName.SOKOBAN_SMOKE, **overrides)
|
|
394
407
|
|
|
408
|
+
@classmethod
|
|
409
|
+
def craftax_gamebench_rust_smoke(cls, **overrides: Any) -> "GeloPreset":
|
|
410
|
+
return cls.from_name(GeloPresetName.CRAFTAX_GAMEBENCH_RUST_SMOKE, **overrides)
|
|
411
|
+
|
|
412
|
+
@classmethod
|
|
413
|
+
def rogue_gamebench_rust_smoke(cls, **overrides: Any) -> "GeloPreset":
|
|
414
|
+
return cls.from_name(GeloPresetName.ROGUE_GAMEBENCH_RUST_SMOKE, **overrides)
|
|
415
|
+
|
|
395
416
|
def to_config(
|
|
396
417
|
self,
|
|
397
418
|
*,
|
|
@@ -418,6 +439,28 @@ class GeloPreset:
|
|
|
418
439
|
run = GeloRunSection(run_id=run_id or _default_run_id(self.name))
|
|
419
440
|
if self.name == GeloPresetName.SOKOBAN_SMOKE:
|
|
420
441
|
config = _sokoban_config(self, run=run, container=container)
|
|
442
|
+
elif self.name == GeloPresetName.CRAFTAX_GAMEBENCH_RUST_SMOKE:
|
|
443
|
+
config = _gamebench_rust_config(
|
|
444
|
+
self,
|
|
445
|
+
run=run,
|
|
446
|
+
container=container,
|
|
447
|
+
task_family="craftax-singleplayer",
|
|
448
|
+
profile="craftax_singleplayer_rust",
|
|
449
|
+
target_objective_label="midgame_crafting_progress",
|
|
450
|
+
max_steps=300,
|
|
451
|
+
seed_prompt=_CRAFTAX_GAMEBENCH_SEED_PROMPT,
|
|
452
|
+
)
|
|
453
|
+
elif self.name == GeloPresetName.ROGUE_GAMEBENCH_RUST_SMOKE:
|
|
454
|
+
config = _gamebench_rust_config(
|
|
455
|
+
self,
|
|
456
|
+
run=run,
|
|
457
|
+
container=container,
|
|
458
|
+
task_family="rogue-singleplayer",
|
|
459
|
+
profile="rogue_singleplayer_rust",
|
|
460
|
+
target_objective_label="survive_and_explore",
|
|
461
|
+
max_steps=200,
|
|
462
|
+
seed_prompt=_ROGUE_GAMEBENCH_SEED_PROMPT,
|
|
463
|
+
)
|
|
421
464
|
else:
|
|
422
465
|
config = GeloHostedConfig(
|
|
423
466
|
run=run,
|
|
@@ -522,9 +565,7 @@ class GeloMaterializer:
|
|
|
522
565
|
if container_tunnel is not None:
|
|
523
566
|
if not hasattr(container_tunnel, "container_config"):
|
|
524
567
|
raise GeloMaterializeError("container_tunnel must expose container_config()")
|
|
525
|
-
container = _clean_config_value(
|
|
526
|
-
_container_section_from_tunnel(container_tunnel)
|
|
527
|
-
)
|
|
568
|
+
container = _clean_config_value(_container_section_from_tunnel(container_tunnel))
|
|
528
569
|
config["container"] = _deep_merge(_mapping(config.get("container")), container)
|
|
529
570
|
if container_url is not None:
|
|
530
571
|
container = dict(_mapping(config.get("container")))
|
|
@@ -633,9 +674,26 @@ _SOKOBAN_SEED_PROMPT = (
|
|
|
633
674
|
"move places the box on a target."
|
|
634
675
|
)
|
|
635
676
|
|
|
677
|
+
_CRAFTAX_GAMEBENCH_SEED_PROMPT = (
|
|
678
|
+
"You are playing the GameBench Craftax single-player Rust task. Use valid "
|
|
679
|
+
"container actions only. Prioritize survival, collect wood and stone, craft "
|
|
680
|
+
"basic tools, recover coal and iron, and make measurable achievement progress."
|
|
681
|
+
)
|
|
682
|
+
|
|
683
|
+
_ROGUE_GAMEBENCH_SEED_PROMPT = (
|
|
684
|
+
"You are playing the GameBench Rogue single-player Rust task. Use valid "
|
|
685
|
+
"container actions only. Prioritize survival, map coverage, safe combat, "
|
|
686
|
+
"inventory discipline, and progress toward deeper exploration."
|
|
687
|
+
)
|
|
688
|
+
|
|
636
689
|
# Mirrors goex_sokoban_gpt_oss_120b_budget.json taskset.context.milestone_ladder.
|
|
637
690
|
_SOKOBAN_MILESTONE_LADDER: tuple[Mapping[str, Any], ...] = (
|
|
638
|
-
{
|
|
691
|
+
{
|
|
692
|
+
"milestone_id": "first_push",
|
|
693
|
+
"title": "Make the first useful box push",
|
|
694
|
+
"reward": 0.2,
|
|
695
|
+
"region": {},
|
|
696
|
+
},
|
|
639
697
|
{
|
|
640
698
|
"milestone_id": "box_on_target",
|
|
641
699
|
"title": "Move a box onto a target",
|
|
@@ -768,6 +826,131 @@ def _sokoban_config(
|
|
|
768
826
|
)
|
|
769
827
|
|
|
770
828
|
|
|
829
|
+
def _gamebench_rust_engine(preset: "GeloPreset") -> GeloEngineSection:
|
|
830
|
+
return GeloEngineSection(
|
|
831
|
+
max_rollouts=preset.max_rollouts,
|
|
832
|
+
proposer_rounds=preset.proposer_rounds,
|
|
833
|
+
base_react_system_prompt=None,
|
|
834
|
+
submission_mode="async",
|
|
835
|
+
execute_live_proposers=True,
|
|
836
|
+
bootstrap_train_rollout_count=1,
|
|
837
|
+
heldout_measurement_rollouts=max(1, preset.heldout_seed_count),
|
|
838
|
+
all_candidate_holdout_seed_count=max(1, preset.heldout_seed_count),
|
|
839
|
+
full_rollout_lane_enabled=True,
|
|
840
|
+
full_rollout_initial_budget=8,
|
|
841
|
+
full_rollout_budget_per_round=4,
|
|
842
|
+
full_rollout_cadence=1,
|
|
843
|
+
fresh_rollouts_per_parent=1,
|
|
844
|
+
resume_rollouts_per_parent=1,
|
|
845
|
+
full_rollout_concurrency=4,
|
|
846
|
+
theme_rollout_concurrency=4,
|
|
847
|
+
closeout_heldout_concurrency=4,
|
|
848
|
+
closeout_heldout_candidate_parallelism=2,
|
|
849
|
+
heldout_measurement_concurrency=4,
|
|
850
|
+
heldout_measurement_candidate_parallelism=2,
|
|
851
|
+
full_rollout_checkpoint_cadence="per_llm_call",
|
|
852
|
+
full_rollout_checkpoint_budget=240,
|
|
853
|
+
data_miner_authority=True,
|
|
854
|
+
data_miner_min_new_checkpoints=1,
|
|
855
|
+
data_miner_rollouts_per_job=2,
|
|
856
|
+
data_miner_cadence="after_full_rollout_phase",
|
|
857
|
+
theme_finalize_min_checkpoints=1,
|
|
858
|
+
max_tentative_themes=4,
|
|
859
|
+
tentative_theme_max_age_rounds=2,
|
|
860
|
+
theme_start_score_band=(0.0, 0.95),
|
|
861
|
+
theme_partials_per_candidate=2,
|
|
862
|
+
theme_saturation_threshold=0.9,
|
|
863
|
+
theme_saturation_min_rollouts=2,
|
|
864
|
+
theme_aux_rounds_per_staircase=2,
|
|
865
|
+
theme_proposal_round_budget=2,
|
|
866
|
+
theme_aux_budget_per_theme=2,
|
|
867
|
+
theme_no_progress_rounds=2,
|
|
868
|
+
terminator_default="agent",
|
|
869
|
+
promotion_min_seeds=1,
|
|
870
|
+
promotion_margin=0.01,
|
|
871
|
+
holdout_consolidate_k=2,
|
|
872
|
+
auto_aux_hill_climb_calls_per_round=2,
|
|
873
|
+
auto_consolidate_min_mature_themes=1,
|
|
874
|
+
auto_consolidate_theme_count=2,
|
|
875
|
+
auto_consolidate_min_score=0.0,
|
|
876
|
+
allow_single_theme_consolidation=True,
|
|
877
|
+
consolidation_budget_per_round=2,
|
|
878
|
+
consolidation_max_themes=3,
|
|
879
|
+
target_new_candidate_count=3,
|
|
880
|
+
candidates_per_proposer=3,
|
|
881
|
+
segment_steps=50,
|
|
882
|
+
resume_segment_steps=25,
|
|
883
|
+
max_llm_turns=20,
|
|
884
|
+
max_actions_per_turn=1,
|
|
885
|
+
request_timeout_seconds=240.0,
|
|
886
|
+
rollout_state_poll_seconds=0.25,
|
|
887
|
+
rollout_terminator_poll_seconds=0.25,
|
|
888
|
+
rollout_stall_timeout_seconds=300.0,
|
|
889
|
+
container_connect_timeout_seconds=30.0,
|
|
890
|
+
allow_resume_fallback_to_fresh=False,
|
|
891
|
+
)
|
|
892
|
+
|
|
893
|
+
|
|
894
|
+
def _gamebench_rust_config(
|
|
895
|
+
preset: "GeloPreset",
|
|
896
|
+
*,
|
|
897
|
+
run: GeloRunSection,
|
|
898
|
+
container: GeloContainerSection,
|
|
899
|
+
task_family: str,
|
|
900
|
+
profile: str,
|
|
901
|
+
target_objective_label: str,
|
|
902
|
+
max_steps: int,
|
|
903
|
+
seed_prompt: str,
|
|
904
|
+
) -> GeloHostedConfig:
|
|
905
|
+
return GeloHostedConfig(
|
|
906
|
+
run=run,
|
|
907
|
+
container=container,
|
|
908
|
+
taskset=GeloTasksetSection(
|
|
909
|
+
train_seeds=_seed_tuple(101, preset.train_seed_count),
|
|
910
|
+
heldout_seeds=_seed_tuple(501, preset.heldout_seed_count),
|
|
911
|
+
profile=profile,
|
|
912
|
+
backend="gamebench_gold_rust",
|
|
913
|
+
reward_mode=GeloRewardMode.PROGRESS,
|
|
914
|
+
checkpoint_semantics=GeloCheckpointSemantics.TRUE_ENV_SNAPSHOT,
|
|
915
|
+
env_config={
|
|
916
|
+
"gamebench_task": task_family,
|
|
917
|
+
"substrate": "rust",
|
|
918
|
+
"max_steps": max_steps,
|
|
919
|
+
},
|
|
920
|
+
context={
|
|
921
|
+
"task_family": task_family,
|
|
922
|
+
"gamebench_task": task_family,
|
|
923
|
+
"substrate_label": f"gamebench/{task_family}/rust",
|
|
924
|
+
"checkpoint_restore_semantics": "true_environment_snapshot",
|
|
925
|
+
"allow_request_snapshot_resume": False,
|
|
926
|
+
"require_real_rewards": True,
|
|
927
|
+
"required_candidate_kind": "prompt",
|
|
928
|
+
"prompt_only_candidate_authoring": True,
|
|
929
|
+
"forbid_code_policy_candidates": True,
|
|
930
|
+
"target_objective_label": target_objective_label,
|
|
931
|
+
"source_ref": f"gamebench/tasks/{task_family}/gold_rust",
|
|
932
|
+
},
|
|
933
|
+
),
|
|
934
|
+
policy=GeloPolicySection(
|
|
935
|
+
model=preset.policy_model,
|
|
936
|
+
provider=preset.policy_provider,
|
|
937
|
+
api_key_env=preset.policy_api_key_env,
|
|
938
|
+
inference_url="https://api.groq.com/openai/v1/chat/completions",
|
|
939
|
+
max_tokens=512,
|
|
940
|
+
config={
|
|
941
|
+
"use_lm": True,
|
|
942
|
+
"temperature": 0.0,
|
|
943
|
+
"max_steps": max_steps,
|
|
944
|
+
},
|
|
945
|
+
),
|
|
946
|
+
go_ex=_gamebench_rust_engine(preset),
|
|
947
|
+
seed_candidate=GeloSeedCandidateSection(react_system_prompt=seed_prompt),
|
|
948
|
+
proposers=_default_crafter_roles(),
|
|
949
|
+
cache=GeloCacheSection(mode=GeloCacheMode.OFF),
|
|
950
|
+
disk_budget=GeloDiskBudgetSection(enabled=True, soft_limit_gb=5.0, hard_limit_gb=10.0),
|
|
951
|
+
)
|
|
952
|
+
|
|
953
|
+
|
|
771
954
|
def _default_crafter_roles() -> Mapping[GeloProposerRole, GeloProposerSection]:
|
|
772
955
|
roles: dict[GeloProposerRole, GeloProposerSection] = {}
|
|
773
956
|
for role in _PROPOSER_ROLES:
|
|
@@ -878,11 +1061,7 @@ def _structured_from_launcher(payload: Mapping[str, Any], source: Path | None) -
|
|
|
878
1061
|
|
|
879
1062
|
def _structured_from_flat(flat: Mapping[str, Any]) -> dict[str, Any]:
|
|
880
1063
|
result: dict[str, Any] = {}
|
|
881
|
-
run = {
|
|
882
|
-
key: flat[key]
|
|
883
|
-
for key in ("run_id", "output_dir", "seed")
|
|
884
|
-
if key in flat
|
|
885
|
-
}
|
|
1064
|
+
run = {key: flat[key] for key in ("run_id", "output_dir", "seed") if key in flat}
|
|
886
1065
|
if run:
|
|
887
1066
|
result["run"] = run
|
|
888
1067
|
service_url = flat.get("service_url") or flat.get("container_url")
|
|
@@ -947,8 +1126,7 @@ def _role_from_flat(flat: Mapping[str, Any], role: GeloProposerRole) -> dict[str
|
|
|
947
1126
|
continue
|
|
948
1127
|
if source_key == "backend" and str(value).strip() in _LEGACY_BACKEND_ALIASES:
|
|
949
1128
|
raise GeloMaterializeError(
|
|
950
|
-
f"GELO proposer {role.value} backend {value!r} is retired; "
|
|
951
|
-
"use 'codex_app_server'"
|
|
1129
|
+
f"GELO proposer {role.value} backend {value!r} is retired; use 'codex_app_server'"
|
|
952
1130
|
)
|
|
953
1131
|
config[source_key] = value
|
|
954
1132
|
return config
|
|
@@ -987,7 +1165,9 @@ def _container_payload_from_target(target: Any) -> Mapping[str, Any]:
|
|
|
987
1165
|
else:
|
|
988
1166
|
payload = _clean_config_value(target)
|
|
989
1167
|
if not isinstance(payload, Mapping):
|
|
990
|
-
raise GeloMaterializeError(
|
|
1168
|
+
raise GeloMaterializeError(
|
|
1169
|
+
"container_tunnel.container_config() must serialize to an object"
|
|
1170
|
+
)
|
|
991
1171
|
return payload
|
|
992
1172
|
|
|
993
1173
|
|
|
@@ -1065,9 +1245,7 @@ def _validate_container_inputs(
|
|
|
1065
1245
|
|
|
1066
1246
|
|
|
1067
1247
|
_GELO_SUPPORTED_PLUGIN_KINDS = frozenset({GeloPluginKind.SFT.value})
|
|
1068
|
-
_GELO_FUTURE_PLUGIN_KINDS = frozenset(
|
|
1069
|
-
{GeloPluginKind.RLVR.value, GeloPluginKind.OPSD.value}
|
|
1070
|
-
)
|
|
1248
|
+
_GELO_FUTURE_PLUGIN_KINDS = frozenset({GeloPluginKind.RLVR.value, GeloPluginKind.OPSD.value})
|
|
1071
1249
|
_GELO_PLUGIN_KIND_KEYS = frozenset({"kind", "type", "plugin_kind"})
|
|
1072
1250
|
_GELO_PLUGIN_COLLECTION_KEYS = frozenset({"lanes", "items"})
|
|
1073
1251
|
_GELO_PLUGIN_METADATA_KEYS = frozenset({"enabled", "status", "version", "config"})
|
|
@@ -1099,9 +1277,7 @@ def _validate_plugin_declaration(value: Any, *, path: str) -> None:
|
|
|
1099
1277
|
if key in value:
|
|
1100
1278
|
_validate_plugin_kind(value[key], path=f"{path}.{key}")
|
|
1101
1279
|
return
|
|
1102
|
-
raise GeloMaterializeError(
|
|
1103
|
-
f"GELO plugin declaration at {path} must include kind='sft'"
|
|
1104
|
-
)
|
|
1280
|
+
raise GeloMaterializeError(f"GELO plugin declaration at {path} must include kind='sft'")
|
|
1105
1281
|
|
|
1106
1282
|
|
|
1107
1283
|
def _validate_plugin_collection(value: Any, *, path: str) -> None:
|
|
@@ -1169,10 +1345,13 @@ def _validate_materialized_config(config: Mapping[str, Any]) -> None:
|
|
|
1169
1345
|
"GELO config container.auth_refresh.provider must be synth_tunnel"
|
|
1170
1346
|
)
|
|
1171
1347
|
if not str(raw_auth_refresh.get("lease_id") or "").strip():
|
|
1172
|
-
raise GeloMaterializeError(
|
|
1173
|
-
"GELO config container.auth_refresh.lease_id is required"
|
|
1174
|
-
)
|
|
1348
|
+
raise GeloMaterializeError("GELO config container.auth_refresh.lease_id is required")
|
|
1175
1349
|
taskset = _mapping(config.get("taskset"))
|
|
1350
|
+
if "checkpoint_semantics" in taskset:
|
|
1351
|
+
raise GeloMaterializeError(
|
|
1352
|
+
"GELO config taskset.checkpoint_semantics is not accepted by hosted "
|
|
1353
|
+
"go-ex; use taskset.context.checkpoint_restore_semantics instead"
|
|
1354
|
+
)
|
|
1176
1355
|
if not taskset.get("train_seeds") or not taskset.get("heldout_seeds"):
|
|
1177
1356
|
raise GeloMaterializeError("GELO config requires taskset.train_seeds and heldout_seeds")
|
|
1178
1357
|
go_ex = _mapping(config.get("go_ex"))
|
|
@@ -1188,8 +1367,7 @@ def _validate_materialized_config(config: Mapping[str, Any]) -> None:
|
|
|
1188
1367
|
backend = str(role_config.get("backend") or "").strip()
|
|
1189
1368
|
if backend in _LEGACY_BACKEND_ALIASES:
|
|
1190
1369
|
raise GeloMaterializeError(
|
|
1191
|
-
f"GELO proposer {role_name} backend {backend!r} is retired; "
|
|
1192
|
-
"use 'codex_app_server'"
|
|
1370
|
+
f"GELO proposer {role_name} backend {backend!r} is retired; use 'codex_app_server'"
|
|
1193
1371
|
)
|
|
1194
1372
|
|
|
1195
1373
|
|
|
@@ -1201,6 +1379,11 @@ def _clean_config_value(value: Any) -> Any:
|
|
|
1201
1379
|
if is_dataclass(value) and not isinstance(value, type):
|
|
1202
1380
|
result: dict[str, Any] = {}
|
|
1203
1381
|
for dataclass_field in fields(value):
|
|
1382
|
+
if (
|
|
1383
|
+
isinstance(value, GeloTasksetSection)
|
|
1384
|
+
and dataclass_field.name == "checkpoint_semantics"
|
|
1385
|
+
):
|
|
1386
|
+
continue
|
|
1204
1387
|
item = getattr(value, dataclass_field.name)
|
|
1205
1388
|
if item is None:
|
|
1206
1389
|
continue
|
|
@@ -1249,7 +1249,13 @@ class GepaConfig:
|
|
|
1249
1249
|
if not self.container_command:
|
|
1250
1250
|
self._preflight_container_capabilities()
|
|
1251
1251
|
config_path = self.write_toml()
|
|
1252
|
-
|
|
1252
|
+
try:
|
|
1253
|
+
result = _NativeGepaRun.from_toml(str(config_path)).execute()
|
|
1254
|
+
except Exception:
|
|
1255
|
+
self._register_usage_complete(status="failed")
|
|
1256
|
+
raise
|
|
1257
|
+
self._register_usage_complete(status="succeeded")
|
|
1258
|
+
return result
|
|
1253
1259
|
|
|
1254
1260
|
def _register_usage_submit(self) -> None:
|
|
1255
1261
|
if not self.usage_registration.enabled:
|
|
@@ -1268,10 +1274,54 @@ class GepaConfig:
|
|
|
1268
1274
|
)
|
|
1269
1275
|
client.register_usage_submit(
|
|
1270
1276
|
algorithm=OptimizerAlgorithmSlug.GEPA,
|
|
1277
|
+
models=self._usage_registration_models(),
|
|
1278
|
+
)
|
|
1279
|
+
except HostedOptimizerError:
|
|
1280
|
+
return
|
|
1281
|
+
|
|
1282
|
+
def _register_usage_complete(self, *, status: str) -> None:
|
|
1283
|
+
if not self.usage_registration.enabled:
|
|
1284
|
+
return
|
|
1285
|
+
from .hosted import (
|
|
1286
|
+
HostedOptimizerClient,
|
|
1287
|
+
HostedOptimizerError,
|
|
1288
|
+
OptimizerAlgorithmSlug,
|
|
1289
|
+
)
|
|
1290
|
+
|
|
1291
|
+
try:
|
|
1292
|
+
client = HostedOptimizerClient(
|
|
1293
|
+
api_key="",
|
|
1294
|
+
register_usage=None,
|
|
1295
|
+
require_api_key=False,
|
|
1296
|
+
)
|
|
1297
|
+
client.register_usage_complete(
|
|
1298
|
+
algorithm=OptimizerAlgorithmSlug.GEPA,
|
|
1299
|
+
status=status,
|
|
1300
|
+
models=self._usage_registration_models(),
|
|
1271
1301
|
)
|
|
1272
1302
|
except HostedOptimizerError:
|
|
1273
1303
|
return
|
|
1274
1304
|
|
|
1305
|
+
def _usage_registration_models(self) -> list[dict[str, str]]:
|
|
1306
|
+
models: list[dict[str, str]] = []
|
|
1307
|
+
if self.proposer.model:
|
|
1308
|
+
models.append(
|
|
1309
|
+
{
|
|
1310
|
+
"role": "proposer",
|
|
1311
|
+
"provider": self.proposer.provider,
|
|
1312
|
+
"model": self.proposer.model,
|
|
1313
|
+
}
|
|
1314
|
+
)
|
|
1315
|
+
if self.policy is not None:
|
|
1316
|
+
models.append(
|
|
1317
|
+
{
|
|
1318
|
+
"role": "policy",
|
|
1319
|
+
"provider": self.policy.provider,
|
|
1320
|
+
"model": self.policy.model,
|
|
1321
|
+
}
|
|
1322
|
+
)
|
|
1323
|
+
return models
|
|
1324
|
+
|
|
1275
1325
|
def _preflight_container_capabilities(self) -> None:
|
|
1276
1326
|
metadata = ContainerMetadataPayload.model_validate(
|
|
1277
1327
|
_http_json(
|
|
@@ -323,12 +323,18 @@ class HostedOptimizerClient:
|
|
|
323
323
|
"container_pool and container_tunnel are mutually exclusive"
|
|
324
324
|
)
|
|
325
325
|
usage_registration_enabled = _usage_registration_enabled_from_toml(config_toml)
|
|
326
|
+
usage_registration_models: list[dict[str, str]] = []
|
|
327
|
+
try:
|
|
328
|
+
usage_registration_models = _usage_registration_models_from_config(tomllib.loads(config_toml))
|
|
329
|
+
except tomllib.TOMLDecodeError:
|
|
330
|
+
usage_registration_models = []
|
|
326
331
|
if container_tunnel is not None:
|
|
327
332
|
try:
|
|
328
333
|
config_json = tomllib.loads(config_toml)
|
|
329
334
|
except tomllib.TOMLDecodeError as exc:
|
|
330
335
|
raise HostedOptimizerError(f"invalid GEPA config_toml: {exc}") from exc
|
|
331
336
|
usage_registration_enabled = _usage_registration_enabled_from_config(config_json)
|
|
337
|
+
usage_registration_models = _usage_registration_models_from_config(config_json)
|
|
332
338
|
config_json = _with_tunnel_container(config_json, container_tunnel)
|
|
333
339
|
payload: dict[str, Any] = {
|
|
334
340
|
"algorithm": OptimizerAlgorithmSlug.GEPA.value,
|
|
@@ -349,7 +355,10 @@ class HostedOptimizerClient:
|
|
|
349
355
|
response = self._json_request("POST", "/api/v1/optimizers/runs", payload)
|
|
350
356
|
submit_response = OptimizerRunSubmitResponse.from_payload(response)
|
|
351
357
|
if usage_registration_enabled:
|
|
352
|
-
self.register_usage_submit(
|
|
358
|
+
self.register_usage_submit(
|
|
359
|
+
algorithm=OptimizerAlgorithmSlug.GEPA,
|
|
360
|
+
models=usage_registration_models,
|
|
361
|
+
)
|
|
353
362
|
return submit_response
|
|
354
363
|
|
|
355
364
|
def submit_gepa_tunnel_toml(
|
|
@@ -583,6 +592,7 @@ class HostedOptimizerClient:
|
|
|
583
592
|
)
|
|
584
593
|
config_json = _config_to_json(config)
|
|
585
594
|
usage_registration_enabled = _usage_registration_enabled_from_config(config_json)
|
|
595
|
+
usage_registration_models = _usage_registration_models_from_config(config_json)
|
|
586
596
|
if container_tunnel is not None:
|
|
587
597
|
config_json = _with_tunnel_container(config_json, container_tunnel)
|
|
588
598
|
payload: dict[str, Any] = {
|
|
@@ -600,13 +610,14 @@ class HostedOptimizerClient:
|
|
|
600
610
|
response = self._json_request("POST", "/api/v1/optimizers/runs", payload)
|
|
601
611
|
submit_response = OptimizerRunSubmitResponse.from_payload(response)
|
|
602
612
|
if usage_registration_enabled:
|
|
603
|
-
self.register_usage_submit(algorithm=algorithm)
|
|
613
|
+
self.register_usage_submit(algorithm=algorithm, models=usage_registration_models)
|
|
604
614
|
return submit_response
|
|
605
615
|
|
|
606
616
|
def register_usage_submit(
|
|
607
617
|
self,
|
|
608
618
|
*,
|
|
609
619
|
algorithm: OptimizerAlgorithmSlug,
|
|
620
|
+
models: Sequence[Mapping[str, str]] | None = None,
|
|
610
621
|
) -> None:
|
|
611
622
|
if not self.register_usage:
|
|
612
623
|
return
|
|
@@ -619,6 +630,9 @@ class HostedOptimizerClient:
|
|
|
619
630
|
"internal": _usage_registration_internal(),
|
|
620
631
|
"install_id": _usage_install_id(),
|
|
621
632
|
}
|
|
633
|
+
usage_models = _sanitize_usage_registration_models(models)
|
|
634
|
+
if usage_models:
|
|
635
|
+
payload["models"] = usage_models
|
|
622
636
|
try:
|
|
623
637
|
self._json_request(
|
|
624
638
|
"POST",
|
|
@@ -637,6 +651,7 @@ class HostedOptimizerClient:
|
|
|
637
651
|
algorithm: OptimizerAlgorithmSlug,
|
|
638
652
|
status: str,
|
|
639
653
|
uplift: float | None = None,
|
|
654
|
+
models: Sequence[Mapping[str, str]] | None = None,
|
|
640
655
|
) -> None:
|
|
641
656
|
"""Fire a run-completion ping: terminal status + uplift number (if present).
|
|
642
657
|
Anonymous and best-effort, like the submit ping; never raises."""
|
|
@@ -654,6 +669,9 @@ class HostedOptimizerClient:
|
|
|
654
669
|
}
|
|
655
670
|
if uplift is not None:
|
|
656
671
|
payload["uplift"] = uplift
|
|
672
|
+
usage_models = _sanitize_usage_registration_models(models)
|
|
673
|
+
if usage_models:
|
|
674
|
+
payload["models"] = usage_models
|
|
657
675
|
try:
|
|
658
676
|
self._json_request(
|
|
659
677
|
"POST",
|
|
@@ -919,6 +937,68 @@ def _usage_registration_enabled_from_config(config_json: Mapping[str, Any]) -> b
|
|
|
919
937
|
return True
|
|
920
938
|
|
|
921
939
|
|
|
940
|
+
def _usage_registration_models_from_config(config_json: Mapping[str, Any]) -> list[dict[str, str]]:
|
|
941
|
+
models: list[Mapping[str, str]] = []
|
|
942
|
+
policy = config_json.get("policy")
|
|
943
|
+
if isinstance(policy, Mapping):
|
|
944
|
+
models.append(
|
|
945
|
+
{
|
|
946
|
+
"role": "policy",
|
|
947
|
+
"provider": str(policy.get("provider") or ""),
|
|
948
|
+
"model": str(policy.get("model") or ""),
|
|
949
|
+
}
|
|
950
|
+
)
|
|
951
|
+
proposer = config_json.get("proposer")
|
|
952
|
+
if isinstance(proposer, Mapping):
|
|
953
|
+
models.append(
|
|
954
|
+
{
|
|
955
|
+
"role": "proposer",
|
|
956
|
+
"provider": str(proposer.get("provider") or ""),
|
|
957
|
+
"model": str(proposer.get("model") or ""),
|
|
958
|
+
}
|
|
959
|
+
)
|
|
960
|
+
proposers = config_json.get("proposers")
|
|
961
|
+
if isinstance(proposers, Mapping):
|
|
962
|
+
defaults = proposers.get("defaults")
|
|
963
|
+
default_provider = str(defaults.get("provider") or "") if isinstance(defaults, Mapping) else ""
|
|
964
|
+
default_model = str(defaults.get("model") or "") if isinstance(defaults, Mapping) else ""
|
|
965
|
+
for role, raw in proposers.items():
|
|
966
|
+
if role == "defaults" or not isinstance(raw, Mapping):
|
|
967
|
+
continue
|
|
968
|
+
models.append(
|
|
969
|
+
{
|
|
970
|
+
"role": str(raw.get("role") or role),
|
|
971
|
+
"provider": str(raw.get("provider") or default_provider),
|
|
972
|
+
"model": str(raw.get("model") or default_model),
|
|
973
|
+
}
|
|
974
|
+
)
|
|
975
|
+
return _sanitize_usage_registration_models(models)
|
|
976
|
+
|
|
977
|
+
|
|
978
|
+
def _sanitize_usage_registration_models(
|
|
979
|
+
models: Sequence[Mapping[str, str]] | None,
|
|
980
|
+
) -> list[dict[str, str]]:
|
|
981
|
+
sanitized: list[dict[str, str]] = []
|
|
982
|
+
seen: set[tuple[str, str, str]] = set()
|
|
983
|
+
for raw in list(models or [])[:16]:
|
|
984
|
+
model = str(raw.get("model") or "").strip()[:160]
|
|
985
|
+
if not model:
|
|
986
|
+
continue
|
|
987
|
+
provider = str(raw.get("provider") or "").strip()[:80]
|
|
988
|
+
role = str(raw.get("role") or "").strip()[:64]
|
|
989
|
+
key = (role, provider, model)
|
|
990
|
+
if key in seen:
|
|
991
|
+
continue
|
|
992
|
+
seen.add(key)
|
|
993
|
+
entry = {"model": model}
|
|
994
|
+
if provider:
|
|
995
|
+
entry["provider"] = provider
|
|
996
|
+
if role:
|
|
997
|
+
entry["role"] = role
|
|
998
|
+
sanitized.append(entry)
|
|
999
|
+
return sanitized
|
|
1000
|
+
|
|
1001
|
+
|
|
922
1002
|
def _package_version() -> str | None:
|
|
923
1003
|
try:
|
|
924
1004
|
return version(_PACKAGE_NAME)
|
|
@@ -453,8 +453,7 @@ def _require_managed_localhost_target(
|
|
|
453
453
|
) -> None:
|
|
454
454
|
if target.host not in {"127.0.0.1", "localhost"}:
|
|
455
455
|
raise TunnelError(
|
|
456
|
-
f"{provider.value} managed leases require a localhost target; "
|
|
457
|
-
f"got {target.host!r}"
|
|
456
|
+
f"{provider.value} managed leases require a localhost target; got {target.host!r}"
|
|
458
457
|
)
|
|
459
458
|
|
|
460
459
|
|
|
@@ -675,8 +674,7 @@ class _SynthTunnelAgent:
|
|
|
675
674
|
if self._lease_id not in {str(item) for item in accepted}:
|
|
676
675
|
rejected = ", ".join(str(item) for item in payload.get("rejected_leases") or [])
|
|
677
676
|
raise TunnelError(
|
|
678
|
-
"SynthTunnel agent attach was rejected"
|
|
679
|
-
+ (f": {rejected}" if rejected else "")
|
|
677
|
+
"SynthTunnel agent attach was rejected" + (f": {rejected}" if rejected else "")
|
|
680
678
|
)
|
|
681
679
|
self._ready.set()
|
|
682
680
|
return
|
|
@@ -924,9 +922,7 @@ def _required_binary(name: str, provider: TunnelProvider) -> str:
|
|
|
924
922
|
binary = shutil.which(name)
|
|
925
923
|
if binary:
|
|
926
924
|
return binary
|
|
927
|
-
raise TunnelError(
|
|
928
|
-
f"{provider.value} tunnel provider requires the {name!r} binary in PATH"
|
|
929
|
-
)
|
|
925
|
+
raise TunnelError(f"{provider.value} tunnel provider requires the {name!r} binary in PATH")
|
|
930
926
|
|
|
931
927
|
|
|
932
928
|
def _start_cloudflared(binary: str, tunnel_token: str) -> subprocess.Popen[str]:
|
|
@@ -969,13 +965,12 @@ def _connect_websocket(url: str, *, headers: Mapping[str, str]) -> Any:
|
|
|
969
965
|
try:
|
|
970
966
|
import websocket
|
|
971
967
|
except ImportError as exc:
|
|
972
|
-
raise TunnelError(
|
|
973
|
-
"synth_tunnel provider requires the 'websocket-client' package"
|
|
974
|
-
) from exc
|
|
968
|
+
raise TunnelError("synth_tunnel provider requires the 'websocket-client' package") from exc
|
|
975
969
|
ws = websocket.WebSocket()
|
|
976
970
|
header_lines = [f"{key}: {value}" for key, value in headers.items()]
|
|
977
971
|
try:
|
|
978
972
|
ws.connect(url, header=header_lines, timeout=10)
|
|
973
|
+
ws.settimeout(None)
|
|
979
974
|
except Exception:
|
|
980
975
|
try:
|
|
981
976
|
ws.close()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|