synth-optimizers 0.2.12.dev20260816__tar.gz → 0.2.13__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 (129) hide show
  1. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/PKG-INFO +31 -1
  2. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/README.md +30 -0
  3. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/pyproject.toml +2 -2
  4. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/workspace.rs +55 -9
  5. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/src/synth_optimizers/__init__.py +20 -0
  6. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/src/synth_optimizers/cli.py +156 -0
  7. synth_optimizers-0.2.13/src/synth_optimizers/eval/__init__.py +118 -0
  8. synth_optimizers-0.2.13/src/synth_optimizers/eval/__main__.py +10 -0
  9. synth_optimizers-0.2.13/src/synth_optimizers/eval/catalog/eval.craftax.code-policy.smoke.v1.toml +55 -0
  10. synth_optimizers-0.2.13/src/synth_optimizers/eval/catalog/eval.craftax.llm-policy.smoke.v1.toml +71 -0
  11. synth_optimizers-0.2.13/src/synth_optimizers/eval/catalog/eval.fixture.policy-smoke.v1.toml +53 -0
  12. synth_optimizers-0.2.13/src/synth_optimizers/eval/catalog/eval.gamebench.craftax-code-policy.confirm.v1.toml +62 -0
  13. synth_optimizers-0.2.13/src/synth_optimizers/eval/catalog/eval.gamebench.llm-policy.confirm.v1.toml +71 -0
  14. synth_optimizers-0.2.13/src/synth_optimizers/eval/commands.py +205 -0
  15. synth_optimizers-0.2.13/src/synth_optimizers/eval/executor.py +285 -0
  16. synth_optimizers-0.2.13/src/synth_optimizers/eval/home.py +205 -0
  17. synth_optimizers-0.2.13/src/synth_optimizers/eval/models.py +873 -0
  18. synth_optimizers-0.2.13/src/synth_optimizers/eval/recipes.py +196 -0
  19. synth_optimizers-0.2.13/src/synth_optimizers/eval/runner.py +895 -0
  20. synth_optimizers-0.2.13/src/synth_optimizers/eval/scoring.py +283 -0
  21. synth_optimizers-0.2.13/src/synth_optimizers/eval/semaphore.py +177 -0
  22. synth_optimizers-0.2.13/src/synth_optimizers/eval/staging.py +114 -0
  23. synth_optimizers-0.2.13/src/synth_optimizers/sft.py +649 -0
  24. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/Cargo.lock +0 -0
  25. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/Cargo.toml +0 -0
  26. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_gepa/Cargo.toml +0 -0
  27. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_gepa/README.md +0 -0
  28. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_gepa/openapi/gepa-service-v1.yaml +0 -0
  29. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_gepa/src/codex_app_server.rs +0 -0
  30. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_gepa/src/jesterky_workflow.rs +0 -0
  31. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_gepa/src/lib.rs +0 -0
  32. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_gepa/src/machines.rs +0 -0
  33. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_gepa/src/pipeline.rs +0 -0
  34. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_gepa/src/planner.rs +0 -0
  35. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_gepa/src/prompting_best_practices.md +0 -0
  36. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_gepa/src/runtime.rs +0 -0
  37. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_gepa/src/service.rs +0 -0
  38. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/Cargo.toml +0 -0
  39. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/README.md +0 -0
  40. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/agent_runtime/README.md +0 -0
  41. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/agent_runtime/app_server.rs +0 -0
  42. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/agent_runtime/codex_home.rs +0 -0
  43. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/agent_runtime/daytona.rs +0 -0
  44. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/agent_runtime/docker.rs +0 -0
  45. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/agent_runtime/jsonrpc_read_window.rs +0 -0
  46. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/agent_runtime/limits.rs +0 -0
  47. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/agent_runtime/local.rs +0 -0
  48. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/agent_runtime/mod.rs +0 -0
  49. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/agent_runtime/nano.rs +0 -0
  50. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/agent_runtime/role_agent.rs +0 -0
  51. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/agent_runtime/session.rs +0 -0
  52. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/agent_runtime/substrate.rs +0 -0
  53. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/agent_runtime/supervisor.rs +0 -0
  54. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/agent_runtime/usage.rs +0 -0
  55. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/artifact_store.rs +0 -0
  56. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/artifacts.rs +0 -0
  57. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/cache.rs +0 -0
  58. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/candidates.rs +0 -0
  59. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/checkpoints.rs +0 -0
  60. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/config.rs +0 -0
  61. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/configured_limits.rs +0 -0
  62. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/container_contract.rs +0 -0
  63. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/data_models.rs +0 -0
  64. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/disk_budget.rs +0 -0
  65. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/error.rs +0 -0
  66. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/event_visualization.rs +0 -0
  67. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/events.rs +0 -0
  68. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/evidence.rs +0 -0
  69. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/failures.rs +0 -0
  70. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/fsm.rs +0 -0
  71. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/http.rs +0 -0
  72. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/invariants.rs +0 -0
  73. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/jesterky.rs +0 -0
  74. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/jobs.rs +0 -0
  75. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/levers.rs +0 -0
  76. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/lib.rs +0 -0
  77. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/limit_engine.rs +0 -0
  78. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/limits.rs +0 -0
  79. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/observability.rs +0 -0
  80. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/operations.rs +0 -0
  81. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/process.rs +0 -0
  82. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/projections.rs +0 -0
  83. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/prompt_program.rs +0 -0
  84. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/registry.rs +0 -0
  85. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/resources.rs +0 -0
  86. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/rollouts.rs +0 -0
  87. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/runtime_records.rs +0 -0
  88. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/scores.rs +0 -0
  89. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/sensors.rs +0 -0
  90. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/state_machine.rs +0 -0
  91. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/stopper.rs +0 -0
  92. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/storage_maintenance.rs +0 -0
  93. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizer_platform/src/usage.rs +0 -0
  94. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizers_py/Cargo.toml +0 -0
  95. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizers_py/README.md +0 -0
  96. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/rust/crates/synth_optimizers_py/src/lib.rs +0 -0
  97. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/src/synth_optimizers/README.md +0 -0
  98. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/src/synth_optimizers/_synth_optimizers.pyi +0 -0
  99. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/src/synth_optimizers/board_server.py +0 -0
  100. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/src/synth_optimizers/discovery.py +0 -0
  101. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/src/synth_optimizers/docs/gelo/00-index.md +0 -0
  102. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/src/synth_optimizers/docs/gelo/10-cli.md +0 -0
  103. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/src/synth_optimizers/docs/gelo/20-sdk.md +0 -0
  104. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/src/synth_optimizers/docs/gelo/30-hosted.md +0 -0
  105. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/src/synth_optimizers/docs/gelo/containers/00-overview.md +0 -0
  106. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/src/synth_optimizers/docs/gelo/containers/20-contract.md +0 -0
  107. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/src/synth_optimizers/docs/gepa/00-index.md +0 -0
  108. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/src/synth_optimizers/docs/gepa/10-cli.md +0 -0
  109. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/src/synth_optimizers/docs/gepa/20-sdk.md +0 -0
  110. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/src/synth_optimizers/docs/gepa/algorithms/00-overview.md +0 -0
  111. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/src/synth_optimizers/docs/gepa/algorithms/10-pipeline-modes.md +0 -0
  112. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/src/synth_optimizers/docs/gepa/algorithms/20-proposer-and-policies.md +0 -0
  113. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/src/synth_optimizers/docs/gepa/assets/synth-logo.png +0 -0
  114. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/src/synth_optimizers/docs/gepa/containers/00-overview.md +0 -0
  115. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/src/synth_optimizers/docs/gepa/containers/10-catalog.md +0 -0
  116. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/src/synth_optimizers/docs/gepa/containers/20-contract.md +0 -0
  117. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/src/synth_optimizers/docs_server.py +0 -0
  118. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/src/synth_optimizers/eval_stats.py +0 -0
  119. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/src/synth_optimizers/future_algorithms.py +0 -0
  120. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/src/synth_optimizers/gelo.py +0 -0
  121. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/src/synth_optimizers/gepa.py +0 -0
  122. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/src/synth_optimizers/hosted.py +0 -0
  123. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/src/synth_optimizers/hosted_config.py +0 -0
  124. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/src/synth_optimizers/o11y.py +0 -0
  125. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/src/synth_optimizers/observability.py +0 -0
  126. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/src/synth_optimizers/py.typed +0 -0
  127. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/src/synth_optimizers/sdk.py +0 -0
  128. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/src/synth_optimizers/tunnels.py +0 -0
  129. {synth_optimizers-0.2.12.dev20260816 → synth_optimizers-0.2.13}/src/synth_optimizers/victorialogs.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: synth-optimizers
3
- Version: 0.2.12.dev20260816
3
+ Version: 0.2.13
4
4
  Requires-Dist: pydantic>=2.0.0
5
5
  Requires-Dist: synth-containers==0.4.1.dev20260814
6
6
  Requires-Dist: websocket-client>=1.8.0
@@ -51,6 +51,36 @@ local algorithm; GELO and SFT are hosted-only in the public package and run on
51
51
  Synth hosted optimizer infrastructure. Hosted GEPA, GELO, and SFT submission is
52
52
  covered in [`docs/hosted-optimizers.md`](docs/hosted-optimizers.md).
53
53
 
54
+ ### Hosted SFT control plane
55
+
56
+ SFT is served by `synth-optimizers`; Optimizers-beta is an internal training executor,
57
+ not a Workshop-facing API. For local QA, start beta with its executor token and then
58
+ start the public façade:
59
+
60
+ ```bash
61
+ # In the Optimizers-beta checkout:
62
+ OPTIMIZERS_BETA_SERVICE_TOKEN=local-dev-token \
63
+ cargo run --bin optimizers-beta -- serve --bind 127.0.0.1:8879
64
+
65
+ # In this checkout:
66
+ export SYNTH_OPTIMIZERS_BETA_URL=http://127.0.0.1:8879
67
+ export OPTIMIZERS_BETA_SERVICE_TOKEN=local-dev-token # held only by the façade
68
+ export SYNTH_OPTIMIZERS_SFT_SERVICE_TOKEN=local-qa-token # Workshop / CLI callers
69
+ synth-optimizers sft service --db .sft/service.sqlite --bind 127.0.0.1:8878
70
+ ```
71
+
72
+ Submit, inspect, and cancel only through the façade:
73
+
74
+ ```bash
75
+ synth-optimizers sft validate --config sft.toml
76
+ synth-optimizers sft submit --config sft.toml --follow
77
+ synth-optimizers sft watch RUN_ID --events
78
+ synth-optimizers sft cancel RUN_ID
79
+ ```
80
+
81
+ The façade keeps executor-only workspace paths and service credentials private. Its
82
+ artifact proxy is available at `/v1/runs/RUN_ID/artifacts/{manifest,events}`.
83
+
54
84
  ## Future hosted-algorithm compatibility
55
85
 
56
86
  MAPO, OHCO, Online Reflexion, and MARL prompt-optimization identifiers are retained
@@ -29,6 +29,36 @@ local algorithm; GELO and SFT are hosted-only in the public package and run on
29
29
  Synth hosted optimizer infrastructure. Hosted GEPA, GELO, and SFT submission is
30
30
  covered in [`docs/hosted-optimizers.md`](docs/hosted-optimizers.md).
31
31
 
32
+ ### Hosted SFT control plane
33
+
34
+ SFT is served by `synth-optimizers`; Optimizers-beta is an internal training executor,
35
+ not a Workshop-facing API. For local QA, start beta with its executor token and then
36
+ start the public façade:
37
+
38
+ ```bash
39
+ # In the Optimizers-beta checkout:
40
+ OPTIMIZERS_BETA_SERVICE_TOKEN=local-dev-token \
41
+ cargo run --bin optimizers-beta -- serve --bind 127.0.0.1:8879
42
+
43
+ # In this checkout:
44
+ export SYNTH_OPTIMIZERS_BETA_URL=http://127.0.0.1:8879
45
+ export OPTIMIZERS_BETA_SERVICE_TOKEN=local-dev-token # held only by the façade
46
+ export SYNTH_OPTIMIZERS_SFT_SERVICE_TOKEN=local-qa-token # Workshop / CLI callers
47
+ synth-optimizers sft service --db .sft/service.sqlite --bind 127.0.0.1:8878
48
+ ```
49
+
50
+ Submit, inspect, and cancel only through the façade:
51
+
52
+ ```bash
53
+ synth-optimizers sft validate --config sft.toml
54
+ synth-optimizers sft submit --config sft.toml --follow
55
+ synth-optimizers sft watch RUN_ID --events
56
+ synth-optimizers sft cancel RUN_ID
57
+ ```
58
+
59
+ The façade keeps executor-only workspace paths and service credentials private. Its
60
+ artifact proxy is available at `/v1/runs/RUN_ID/artifacts/{manifest,events}`.
61
+
32
62
  ## Future hosted-algorithm compatibility
33
63
 
34
64
  MAPO, OHCO, Online Reflexion, and MARL prompt-optimization identifiers are retained
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "synth-optimizers"
3
- version = "0.2.12.dev20260816"
3
+ version = "0.2.13"
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"
@@ -48,7 +48,7 @@ module-name = "synth_optimizers._synth_optimizers"
48
48
  python-source = "src"
49
49
  # Ship the bundled console docs (markdown + logo) as package data so a
50
50
  # pip-installed `gepa console` can serve them without a repo checkout.
51
- include = ["src/synth_optimizers/docs/**/*"]
51
+ include = ["src/synth_optimizers/docs/**/*", "src/synth_optimizers/eval/catalog/*.toml"]
52
52
 
53
53
  [tool.uv]
54
54
  package = true
@@ -4819,15 +4819,7 @@ impl WorkspaceStore {
4819
4819
  let mut stmt = self.conn.prepare(
4820
4820
  r#"
4821
4821
  SELECT run_id, frame_json
4822
- FROM sensor_frames sf
4823
- WHERE NOT EXISTS (
4824
- SELECT 1
4825
- FROM candidate_seed_rewards csr
4826
- WHERE csr.run_id = sf.run_id
4827
- AND csr.candidate_id = sf.candidate_id
4828
- AND csr.evaluation_stage = sf.evaluation_stage
4829
- AND csr.seed_id = sf.task_id
4830
- )
4822
+ FROM sensor_frames
4831
4823
  "#,
4832
4824
  )?;
4833
4825
  let mut rows = stmt.query([])?;
@@ -4860,6 +4852,10 @@ impl WorkspaceStore {
4860
4852
  }
4861
4853
 
4862
4854
  fn ensure_runtime_schema(&self) -> Result<()> {
4855
+ // v0.2.0 workspaces called this identity `seed`. The v0.2.12
4856
+ // backfill used `task_id` before migrating the column, which made the
4857
+ // sidecar fail during startup even when the legacy table was empty.
4858
+ self.ensure_column("sensor_frames", "task_id", "TEXT NOT NULL DEFAULT ''")?;
4863
4859
  self.ensure_column(
4864
4860
  "budget_commits",
4865
4861
  "wall_seconds",
@@ -9351,4 +9347,54 @@ mod nullable_cost_migration_tests {
9351
9347
  let _ = fs::remove_file(path.with_extension("sqlite-wal"));
9352
9348
  let _ = fs::remove_file(path.with_extension("sqlite-shm"));
9353
9349
  }
9350
+
9351
+ #[test]
9352
+ fn opening_legacy_workspace_adds_sensor_frame_task_identity_before_backfill() {
9353
+ let path = std::env::temp_dir().join(format!(
9354
+ "synth-sensor-frame-migration-{}-{}.sqlite",
9355
+ std::process::id(),
9356
+ OffsetDateTime::now_utc().unix_timestamp_nanos()
9357
+ ));
9358
+ let conn = Connection::open(&path).unwrap();
9359
+ conn.execute_batch(
9360
+ r#"
9361
+ CREATE TABLE sensor_frames (
9362
+ run_id TEXT NOT NULL,
9363
+ sensor_frame_id TEXT NOT NULL,
9364
+ candidate_id TEXT NOT NULL,
9365
+ rollout_id TEXT,
9366
+ example_id TEXT NOT NULL,
9367
+ seed INTEGER NOT NULL,
9368
+ split TEXT NOT NULL,
9369
+ evaluation_stage TEXT NOT NULL,
9370
+ reward REAL NOT NULL,
9371
+ status TEXT NOT NULL,
9372
+ trace_digest_json TEXT,
9373
+ usage_json TEXT NOT NULL,
9374
+ failure_json TEXT,
9375
+ frame_json TEXT NOT NULL,
9376
+ updated_at TEXT NOT NULL,
9377
+ PRIMARY KEY(run_id, sensor_frame_id)
9378
+ );
9379
+ "#,
9380
+ )
9381
+ .unwrap();
9382
+ drop(conn);
9383
+
9384
+ let store = WorkspaceStore::open_existing(&path).unwrap();
9385
+ let mut columns = store
9386
+ .conn
9387
+ .prepare("PRAGMA table_info(sensor_frames)")
9388
+ .unwrap();
9389
+ let task_id = columns
9390
+ .query_map([], |row| row.get::<_, String>(1))
9391
+ .unwrap()
9392
+ .any(|name| name.unwrap() == "task_id");
9393
+ assert!(task_id);
9394
+ drop(columns);
9395
+ drop(store);
9396
+ let _ = fs::remove_file(&path);
9397
+ let _ = fs::remove_file(path.with_extension("sqlite-wal"));
9398
+ let _ = fs::remove_file(path.with_extension("sqlite-shm"));
9399
+ }
9354
9400
  }
@@ -84,6 +84,17 @@ from .hosted import (
84
84
  validate_online_reflexion_evidence_notes,
85
85
  )
86
86
  from .hosted_config import HostedOptimizerConfig
87
+ from .sft import (
88
+ SFT_ALGORITHM_ID,
89
+ BetaSftExecutorClient,
90
+ SftArtifact,
91
+ SftConfig,
92
+ SftPublicServiceClient,
93
+ SftService,
94
+ SftServiceError,
95
+ create_sft_http_server,
96
+ serve_sft_service,
97
+ )
87
98
  from .future_algorithms import (
88
99
  FUTURE_HOSTED_ALGORITHMS,
89
100
  FutureHostedAlgorithm,
@@ -226,6 +237,15 @@ __all__ = [
226
237
  "HostedOptimizerClient",
227
238
  "HostedOptimizerError",
228
239
  "HostedOptimizerConfig",
240
+ "SFT_ALGORITHM_ID",
241
+ "BetaSftExecutorClient",
242
+ "SftArtifact",
243
+ "SftConfig",
244
+ "SftPublicServiceClient",
245
+ "SftService",
246
+ "SftServiceError",
247
+ "create_sft_http_server",
248
+ "serve_sft_service",
229
249
  "FUTURE_HOSTED_ALGORITHMS",
230
250
  "FutureHostedAlgorithm",
231
251
  "FutureHostedAlgorithmSlug",
@@ -34,6 +34,7 @@ from .hosted import (
34
34
  HostedOptimizerError,
35
35
  validate_online_reflexion_evidence_notes,
36
36
  )
37
+ from .sft import SftConfig, SftPublicServiceClient, SftServiceError, serve_sft_service
37
38
  from .tunnels import TunnelError, TunnelProvider
38
39
  from .victorialogs import project_gepa_run_artifacts, project_gepa_run_started
39
40
 
@@ -751,6 +752,97 @@ def _config_file_object(path: str) -> dict:
751
752
  return data
752
753
 
753
754
 
755
+ def _sft_service_client(args: argparse.Namespace) -> SftPublicServiceClient:
756
+ token = os.environ.get(args.service_token_env) if args.service_token_env else None
757
+ return SftPublicServiceClient(args.service_url, token, timeout_seconds=args.timeout_seconds)
758
+
759
+
760
+ def _sft_validate(args: argparse.Namespace) -> int:
761
+ try:
762
+ config = SftConfig.from_toml(
763
+ Path(args.config).read_text(encoding="utf-8"), run_id=args.run_id
764
+ )
765
+ except OSError as exc:
766
+ raise SystemExit(f"cannot read {args.config}: {exc}") from exc
767
+ except SftServiceError as exc:
768
+ raise SystemExit(str(exc)) from exc
769
+ payload = {
770
+ "algorithm": "sft",
771
+ "run_id": config.run_id,
772
+ "backend": config.backend,
773
+ "base_model": config.base_model,
774
+ "checkpoint_steps": list(config.checkpoint_steps),
775
+ "accelerator_slots": config.accelerator_slots,
776
+ }
777
+ print(
778
+ json.dumps(payload, indent=2, sort_keys=True)
779
+ if args.json
780
+ else f"valid SFT config run_id={config.run_id} backend={config.backend}"
781
+ )
782
+ return 0
783
+
784
+
785
+ def _sft_submit(args: argparse.Namespace) -> int:
786
+ try:
787
+ config_toml = Path(args.config).read_text(encoding="utf-8")
788
+ client = _sft_service_client(args)
789
+ submitted = client.submit_toml(
790
+ config_toml,
791
+ run_id=args.run_id,
792
+ idempotency_key=args.idempotency_key,
793
+ )
794
+ if args.json and not args.follow:
795
+ print(json.dumps(submitted, indent=2, sort_keys=True))
796
+ return 0
797
+ run_id = str(submitted["run_id"])
798
+ print(f"submitted run_id={run_id} status={submitted.get('status', 'queued')}")
799
+ if not args.follow:
800
+ return 0
801
+ while True:
802
+ record = client.get(run_id)
803
+ status = str(record.get("status", "unknown"))
804
+ print(f"status={status}")
805
+ if status in {"succeeded", "failed", "cancelled"}:
806
+ if args.json:
807
+ print(json.dumps(record, indent=2, sort_keys=True))
808
+ return 1 if status == "failed" else 0
809
+ time.sleep(args.poll_seconds)
810
+ except (OSError, SftServiceError) as exc:
811
+ raise SystemExit(str(exc)) from exc
812
+
813
+
814
+ def _sft_watch(args: argparse.Namespace) -> int:
815
+ try:
816
+ client = _sft_service_client(args)
817
+ record = client.get(args.run_id)
818
+ if args.events:
819
+ page = client.optimizer_events(
820
+ args.run_id, after_sequence=args.after_seq, limit=args.limit
821
+ )
822
+ record["events"] = page.get("events", [])
823
+ print(
824
+ json.dumps(record, indent=2, sort_keys=True)
825
+ if args.json
826
+ else f"run_id={args.run_id} status={record.get('status')}"
827
+ )
828
+ return 1 if record.get("status") == "failed" else 0
829
+ except SftServiceError as exc:
830
+ raise SystemExit(str(exc)) from exc
831
+
832
+
833
+ def _sft_cancel(args: argparse.Namespace) -> int:
834
+ try:
835
+ record = _sft_service_client(args).cancel(args.run_id)
836
+ except SftServiceError as exc:
837
+ raise SystemExit(str(exc)) from exc
838
+ print(
839
+ json.dumps(record, indent=2, sort_keys=True)
840
+ if args.json
841
+ else f"run_id={args.run_id} status={record.get('status')}"
842
+ )
843
+ return 0
844
+
845
+
754
846
  def _submit_hosted_gelo(args: argparse.Namespace) -> int:
755
847
  tunnel: Any | None = None
756
848
  try:
@@ -1577,6 +1669,45 @@ def build_parser() -> argparse.ArgumentParser:
1577
1669
  "--docs-set", default="gelo", help="Bundled docs set to serve (default: gelo)."
1578
1670
  )
1579
1671
 
1672
+ sft = subcommands.add_parser("sft", help="Operate the public SFT control-plane service.")
1673
+ sft_subcommands = sft.add_subparsers(dest="sft_command", required=True)
1674
+ sft_validate = sft_subcommands.add_parser("validate")
1675
+ sft_validate.add_argument("--config", required=True)
1676
+ sft_validate.add_argument("--run-id")
1677
+ sft_validate.add_argument("--json", action="store_true")
1678
+
1679
+ sft_service = sft_subcommands.add_parser("service")
1680
+ sft_service.add_argument("--db", default=".sft/service.sqlite")
1681
+ sft_service.add_argument("--bind", default="127.0.0.1:8878")
1682
+ sft_service.add_argument(
1683
+ "--service-token-env",
1684
+ default="SYNTH_OPTIMIZERS_SFT_SERVICE_TOKEN",
1685
+ help="Optional inbound bearer-token environment variable.",
1686
+ )
1687
+
1688
+ for command_name in ("submit", "watch", "cancel"):
1689
+ command = sft_subcommands.add_parser(command_name)
1690
+ command.add_argument(
1691
+ "--service-url",
1692
+ default=os.environ.get("SYNTH_OPTIMIZERS_SFT_SERVICE_URL", "http://127.0.0.1:8878"),
1693
+ )
1694
+ command.add_argument("--service-token-env", default="SYNTH_OPTIMIZERS_SFT_SERVICE_TOKEN")
1695
+ command.add_argument("--timeout-seconds", type=float, default=300.0)
1696
+ command.add_argument("--json", action="store_true")
1697
+ sft_submit = sft_subcommands.choices["submit"]
1698
+ sft_submit.add_argument("--config", required=True)
1699
+ sft_submit.add_argument("--run-id")
1700
+ sft_submit.add_argument("--idempotency-key")
1701
+ sft_submit.add_argument("--follow", action="store_true")
1702
+ sft_submit.add_argument("--poll-seconds", type=float, default=1.0)
1703
+ sft_watch = sft_subcommands.choices["watch"]
1704
+ sft_watch.add_argument("run_id")
1705
+ sft_watch.add_argument("--events", action="store_true")
1706
+ sft_watch.add_argument("--after-seq", type=int, default=0)
1707
+ sft_watch.add_argument("--limit", type=int, default=500)
1708
+ sft_cancel = sft_subcommands.choices["cancel"]
1709
+ sft_cancel.add_argument("run_id")
1710
+
1580
1711
  mapo = subcommands.add_parser("mapo")
1581
1712
  mapo_subcommands = mapo.add_subparsers(dest="mapo_command", required=True)
1582
1713
  mapo_startup = mapo_subcommands.add_parser("startup")
@@ -2087,6 +2218,10 @@ def build_parser() -> argparse.ArgumentParser:
2087
2218
  gepa_runs_delete.add_argument("--yes", action="store_true", help="Apply the deletion.")
2088
2219
  gepa_runs_delete.add_argument("--json", action="store_true")
2089
2220
 
2221
+ from .eval.commands import register as register_eval
2222
+
2223
+ register_eval(subcommands)
2224
+
2090
2225
  events = subcommands.add_parser("events")
2091
2226
  events_subcommands = events.add_subparsers(dest="events_command", required=True)
2092
2227
  replay = events_subcommands.add_parser("replay")
@@ -2100,6 +2235,15 @@ def build_parser() -> argparse.ArgumentParser:
2100
2235
 
2101
2236
  def main(argv: Sequence[str] | None = None) -> int:
2102
2237
  args = build_parser().parse_args(argv)
2238
+ if args.command == "eval":
2239
+ from .eval.commands import dispatch as dispatch_eval
2240
+ from .eval.models import EvalContractError
2241
+
2242
+ try:
2243
+ return dispatch_eval(args)
2244
+ except EvalContractError as exc:
2245
+ print(f"error: {exc}", file=sys.stderr)
2246
+ return 1
2103
2247
  if args.command == "gepa" and args.gepa_command == "run":
2104
2248
  from .gepa import GepaRun, UsageRegistrationConfig
2105
2249
 
@@ -2200,6 +2344,18 @@ def main(argv: Sequence[str] | None = None) -> int:
2200
2344
  docs = DocsSource([docs_root], title=args.title)
2201
2345
  serve_console(board, docs, host=args.host, port=args.port)
2202
2346
  return 0
2347
+ if args.command == "sft" and args.sft_command == "validate":
2348
+ return _sft_validate(args)
2349
+ if args.command == "sft" and args.sft_command == "submit":
2350
+ return _sft_submit(args)
2351
+ if args.command == "sft" and args.sft_command == "watch":
2352
+ return _sft_watch(args)
2353
+ if args.command == "sft" and args.sft_command == "cancel":
2354
+ return _sft_cancel(args)
2355
+ if args.command == "sft" and args.sft_command == "service":
2356
+ token = os.environ.get(args.service_token_env) if args.service_token_env else None
2357
+ serve_sft_service(args.db, args.bind, service_token=token)
2358
+ return 0
2203
2359
  if args.command == "mapo" and args.mapo_command == "startup":
2204
2360
  return _gelo_startup(args)
2205
2361
  if args.command == "mapo" and args.mapo_command == "submit":
@@ -0,0 +1,118 @@
1
+ """`eval`: a local Optimizers algorithm that scores policy candidates.
2
+
3
+ `eval` takes an immutable set of policy candidates and an allowlisted recipe,
4
+ expands them into a fair `candidate × seed × scenario` matrix, runs each trial
5
+ in the recipe's pinned container, and returns a promotable winner only when the
6
+ declared gates pass.
7
+
8
+ It is deliberately local. `eval` is not a hosted algorithm and must never be
9
+ added to `synth_optimizers.hosted.OptimizerAlgorithmSlug` or to
10
+ `future_algorithms.py`: that enum describes hosted API compatibility, which
11
+ this is not.
12
+
13
+ There are no Craftax-, GameBench-, or Harbor-specific execution paths here.
14
+ Those are containers implementing one standard contract, `eval.target.v1`.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from .executor import (
20
+ ContainerRuntimeError,
21
+ OciTrialExecutor,
22
+ TrialExecution,
23
+ TrialExecutor,
24
+ TrialRunRequest,
25
+ )
26
+ from .home import EvalHome, RuntimeConfig
27
+ from .models import (
28
+ BENCHMARK_STATUSES,
29
+ CANDIDATE_SET_SCHEMA,
30
+ CONTAINER_RESULT_SCHEMA,
31
+ CONTAINER_STATUSES,
32
+ EVAL_ALGORITHM_ID,
33
+ EVAL_ALGORITHM_VERSION,
34
+ POLICY_CANDIDATE_SCHEMA,
35
+ RUN_MANIFEST_SCHEMA,
36
+ SEED_LEDGER_SCHEMA,
37
+ SELECTION_STATUSES,
38
+ TARGET_MANIFEST_SCHEMA,
39
+ TRIAL_MANIFEST_SCHEMA,
40
+ TRIAL_RECORD_SCHEMA,
41
+ TRIAL_STATUSES,
42
+ WORKER_EVENT_SCHEMA,
43
+ WORKER_MANIFEST_SCHEMA,
44
+ CandidateScorecard,
45
+ CandidateSet,
46
+ ContainerResult,
47
+ EvalContractError,
48
+ MetricSpec,
49
+ PolicyCandidate,
50
+ SeedLedger,
51
+ SelectionDecision,
52
+ SelectionSpec,
53
+ TargetManifest,
54
+ TrialKey,
55
+ TrialLimits,
56
+ TrialRecord,
57
+ digest_of,
58
+ digest_of_tree,
59
+ )
60
+ from .recipes import EvalRecipe, catalog, get_recipe
61
+ from .runner import EvalRunner, WorkerManifest, request_cancel, run_worker
62
+ from .scoring import apply_elimination, decide, paired_lift, summarize_candidate
63
+ from .semaphore import Lease, SemaphoreTimeout, TrialSemaphore
64
+
65
+ __all__ = [
66
+ "BENCHMARK_STATUSES",
67
+ "CANDIDATE_SET_SCHEMA",
68
+ "CONTAINER_RESULT_SCHEMA",
69
+ "CONTAINER_STATUSES",
70
+ "EVAL_ALGORITHM_ID",
71
+ "EVAL_ALGORITHM_VERSION",
72
+ "POLICY_CANDIDATE_SCHEMA",
73
+ "RUN_MANIFEST_SCHEMA",
74
+ "SEED_LEDGER_SCHEMA",
75
+ "SELECTION_STATUSES",
76
+ "TARGET_MANIFEST_SCHEMA",
77
+ "TRIAL_MANIFEST_SCHEMA",
78
+ "TRIAL_RECORD_SCHEMA",
79
+ "TRIAL_STATUSES",
80
+ "WORKER_EVENT_SCHEMA",
81
+ "WORKER_MANIFEST_SCHEMA",
82
+ "CandidateScorecard",
83
+ "CandidateSet",
84
+ "ContainerResult",
85
+ "ContainerRuntimeError",
86
+ "EvalContractError",
87
+ "EvalHome",
88
+ "EvalRecipe",
89
+ "EvalRunner",
90
+ "Lease",
91
+ "MetricSpec",
92
+ "OciTrialExecutor",
93
+ "PolicyCandidate",
94
+ "RuntimeConfig",
95
+ "SeedLedger",
96
+ "SelectionDecision",
97
+ "SelectionSpec",
98
+ "SemaphoreTimeout",
99
+ "TargetManifest",
100
+ "TrialExecution",
101
+ "TrialExecutor",
102
+ "TrialKey",
103
+ "TrialLimits",
104
+ "TrialRecord",
105
+ "TrialRunRequest",
106
+ "TrialSemaphore",
107
+ "WorkerManifest",
108
+ "apply_elimination",
109
+ "catalog",
110
+ "decide",
111
+ "digest_of",
112
+ "digest_of_tree",
113
+ "get_recipe",
114
+ "paired_lift",
115
+ "request_cancel",
116
+ "run_worker",
117
+ "summarize_candidate",
118
+ ]
@@ -0,0 +1,10 @@
1
+ """`python -m synth_optimizers.eval …` for app-owned worker launches."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+
7
+ from .commands import main
8
+
9
+ if __name__ == "__main__":
10
+ raise SystemExit(main(sys.argv[1:]))
@@ -0,0 +1,55 @@
1
+ # Craftax native code-policy smoke. Report-only: it never promotes, so a green
2
+ # run means "the rig and the three policies produced valid evidence", not "a
3
+ # winner was found".
4
+ #
5
+ # `image_digest` is intentionally absent until craftax-eval-target is published
6
+ # and pinned; the recipe reports `unavailable` rather than pretending to a tag.
7
+
8
+ [recipe]
9
+ id = "eval.craftax.code-policy.smoke.v1"
10
+ title = "Craftax code-policy smoke"
11
+ description = "Score staged Craftax code policies on one pinned world, report only."
12
+ task = "craftax"
13
+ policy_kind = "python-code.craftax-choose-actions.v1"
14
+ image = "craftax-eval-target"
15
+ prerequisites = [
16
+ "Published craftax-eval-target image pinned by digest",
17
+ "OCI runtime available to the Desktop app",
18
+ ]
19
+ scenarios = ["craftax-default-48x48"]
20
+ screening_seeds = [101, 102]
21
+ confirmation_seeds = []
22
+ secrets = []
23
+
24
+ [recipe.target]
25
+ schema_version = "eval.target.v1"
26
+ policy_kinds = ["python-code.craftax-choose-actions.v1"]
27
+ trial_mode = "one-policy-one-seed"
28
+ required_gates = ["policy_loaded", "verifier_completed"]
29
+ required_artifacts = ["trace"]
30
+ supports_live_events = true
31
+ network = "none"
32
+
33
+ [[recipe.target.metrics]]
34
+ id = "reward"
35
+ direction = "maximize"
36
+
37
+ [[recipe.target.metrics]]
38
+ id = "achievements"
39
+ direction = "maximize"
40
+
41
+ [recipe.selection]
42
+ primary_metric = "reward"
43
+ min_lift = 0.0
44
+ min_valid_trials = 2
45
+ decision_mode = "report_only"
46
+
47
+ [recipe.selection.elimination]
48
+ kind = "none"
49
+
50
+ [recipe.limits]
51
+ max_parallel_trials = 2
52
+ timeout_seconds = 300
53
+ cpus = 2.0
54
+ memory_mb = 4096
55
+ max_output_bytes = 268435456
@@ -0,0 +1,71 @@
1
+ # Craftax LLM code-policy smoke. The candidate is data — an allowlisted model
2
+ # and reasoning effort — so comparing two efforts is a candidate set, not a
3
+ # code change.
4
+ #
5
+ # `network = "bridge"` and one declared secret are the minimum a paid policy
6
+ # needs; the route, the rates, and the caps stay recipe-owned so a candidate
7
+ # cannot bill against an endpoint the product did not name.
8
+
9
+ [recipe]
10
+ id = "eval.craftax.llm-policy.smoke.v1"
11
+ title = "Craftax LLM policy smoke"
12
+ description = "Score allowlisted LLM policies on one pinned Craftax world, report only."
13
+ task = "craftax"
14
+ policy_kind = "llm-policy.v1"
15
+ image = "craftax-eval-target"
16
+ prerequisites = [
17
+ "Published craftax-eval-target image pinned by digest",
18
+ "OPENAI_API_KEY in the eval home's secrets.toml",
19
+ ]
20
+ scenarios = ["craftax-default-48x48"]
21
+ screening_seeds = [101, 102]
22
+ confirmation_seeds = []
23
+ secrets = ["OPENAI_API_KEY"]
24
+
25
+ [recipe.budget]
26
+ max_llm_calls = 20
27
+ max_usd = 0.30
28
+
29
+ [[recipe.models]]
30
+ id = "gpt-5.6-luna"
31
+ route = "https://api.openai.com/v1/chat/completions"
32
+ secret = "OPENAI_API_KEY"
33
+ efforts = ["none", "low", "medium", "high"]
34
+ usd_per_1m_input = 0.2
35
+ usd_per_1m_output = 1.2
36
+ usd_per_1m_cached_input = 0.02
37
+ price_source = "https://developers.openai.com/api/docs/pricing (standard tier)"
38
+ price_as_of = "2026-07-30"
39
+
40
+ [recipe.target]
41
+ schema_version = "eval.target.v1"
42
+ policy_kinds = ["llm-policy.v1"]
43
+ trial_mode = "one-policy-one-seed"
44
+ required_gates = ["policy_loaded", "verifier_completed"]
45
+ required_artifacts = ["trace"]
46
+ supports_live_events = true
47
+ network = "bridge"
48
+
49
+ [[recipe.target.metrics]]
50
+ id = "reward"
51
+ direction = "maximize"
52
+
53
+ [[recipe.target.metrics]]
54
+ id = "achievements"
55
+ direction = "maximize"
56
+
57
+ [recipe.selection]
58
+ primary_metric = "reward"
59
+ min_lift = 0.0
60
+ min_valid_trials = 2
61
+ decision_mode = "report_only"
62
+
63
+ [recipe.selection.elimination]
64
+ kind = "none"
65
+
66
+ [recipe.limits]
67
+ max_parallel_trials = 4
68
+ timeout_seconds = 1800
69
+ cpus = 2.0
70
+ memory_mb = 4096
71
+ max_output_bytes = 268435456