synth-ai 0.2.6.dev1__py3-none-any.whl → 0.4.3__py3-none-any.whl

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 (738) hide show
  1. synth_ai/__init__.py +44 -24
  2. synth_ai/__main__.py +30 -3
  3. synth_ai/cli/__init__.py +103 -48
  4. synth_ai/cli/__main__.py +42 -0
  5. synth_ai/cli/_internal/__init__.py +5 -0
  6. synth_ai/cli/_internal/modal_wrapper.py +31 -0
  7. synth_ai/cli/_internal/storage.py +20 -0
  8. synth_ai/cli/_internal/typer_patch.py +47 -0
  9. synth_ai/cli/_internal/validate_task_app.py +29 -0
  10. synth_ai/cli/agents/__init__.py +17 -0
  11. synth_ai/cli/agents/claude.py +77 -0
  12. synth_ai/cli/agents/codex.py +265 -0
  13. synth_ai/cli/agents/opencode.py +253 -0
  14. synth_ai/cli/commands/__init__.py +18 -0
  15. synth_ai/cli/commands/artifacts/__init__.py +13 -0
  16. synth_ai/cli/commands/artifacts/client.py +119 -0
  17. synth_ai/cli/commands/artifacts/config.py +57 -0
  18. synth_ai/cli/commands/artifacts/core.py +24 -0
  19. synth_ai/cli/commands/artifacts/download.py +188 -0
  20. synth_ai/cli/commands/artifacts/export.py +186 -0
  21. synth_ai/cli/commands/artifacts/list.py +156 -0
  22. synth_ai/cli/commands/artifacts/parsing.py +250 -0
  23. synth_ai/cli/commands/artifacts/show.py +336 -0
  24. synth_ai/cli/commands/demo/__init__.py +3 -0
  25. synth_ai/cli/commands/demo/core.py +153 -0
  26. synth_ai/cli/commands/eval/__init__.py +10 -0
  27. synth_ai/cli/commands/eval/config.py +338 -0
  28. synth_ai/cli/commands/eval/core.py +256 -0
  29. synth_ai/cli/commands/eval/runner.py +704 -0
  30. synth_ai/cli/commands/eval/validation.py +60 -0
  31. synth_ai/cli/commands/filter/__init__.py +12 -0
  32. synth_ai/cli/commands/filter/core.py +424 -0
  33. synth_ai/cli/commands/filter/errors.py +55 -0
  34. synth_ai/cli/commands/filter/validation.py +77 -0
  35. synth_ai/cli/commands/help/__init__.py +185 -0
  36. synth_ai/cli/commands/help/core.py +72 -0
  37. synth_ai/cli/commands/scan/__init__.py +19 -0
  38. synth_ai/cli/commands/scan/cloudflare_scanner.py +403 -0
  39. synth_ai/cli/commands/scan/core.py +344 -0
  40. synth_ai/cli/commands/scan/health_checker.py +242 -0
  41. synth_ai/cli/commands/scan/local_scanner.py +278 -0
  42. synth_ai/cli/commands/scan/models.py +83 -0
  43. synth_ai/cli/commands/smoke/__init__.py +7 -0
  44. synth_ai/cli/commands/smoke/core.py +1428 -0
  45. synth_ai/cli/commands/status/__init__.py +3 -0
  46. synth_ai/cli/commands/status/client.py +91 -0
  47. synth_ai/cli/commands/status/config.py +12 -0
  48. synth_ai/cli/commands/status/errors.py +11 -0
  49. synth_ai/cli/commands/status/subcommands/__init__.py +3 -0
  50. synth_ai/cli/commands/status/subcommands/config.py +13 -0
  51. synth_ai/cli/commands/status/subcommands/files.py +34 -0
  52. synth_ai/cli/commands/status/subcommands/jobs.py +51 -0
  53. synth_ai/cli/commands/status/subcommands/models.py +35 -0
  54. synth_ai/cli/commands/status/subcommands/runs.py +34 -0
  55. synth_ai/cli/commands/status/subcommands/session.py +77 -0
  56. synth_ai/cli/commands/status/subcommands/summary.py +39 -0
  57. synth_ai/cli/commands/status/subcommands/utils.py +41 -0
  58. synth_ai/cli/commands/status/utils.py +23 -0
  59. synth_ai/cli/commands/train/__init__.py +53 -0
  60. synth_ai/cli/commands/train/core.py +22 -0
  61. synth_ai/cli/commands/train/errors.py +117 -0
  62. synth_ai/cli/commands/train/judge_schemas.py +201 -0
  63. synth_ai/cli/commands/train/judge_validation.py +305 -0
  64. synth_ai/cli/commands/train/prompt_learning_validation.py +633 -0
  65. synth_ai/cli/commands/train/validation.py +392 -0
  66. synth_ai/cli/demo_apps/__init__.py +10 -0
  67. synth_ai/cli/demo_apps/core/__init__.py +28 -0
  68. synth_ai/cli/demo_apps/core/cli.py +1735 -0
  69. synth_ai/cli/demo_apps/crafter/__init__.py +1 -0
  70. synth_ai/cli/demo_apps/crafter/crafter_fft_4b.toml +55 -0
  71. synth_ai/cli/demo_apps/crafter/grpo_crafter_task_app.py +186 -0
  72. synth_ai/cli/demo_apps/crafter/rl_from_base_qwen4b.toml +74 -0
  73. synth_ai/cli/demo_apps/demo_registry.py +176 -0
  74. synth_ai/cli/demo_apps/demo_task_apps/__init__.py +7 -0
  75. synth_ai/{demos → cli/demo_apps}/demo_task_apps/core.py +117 -51
  76. synth_ai/cli/demo_apps/demo_task_apps/crafter/__init__.py +1 -0
  77. synth_ai/cli/demo_apps/demo_task_apps/crafter/configs/crafter_fft_4b.toml +53 -0
  78. synth_ai/cli/demo_apps/demo_task_apps/crafter/configs/rl_from_base_qwen4b.toml +73 -0
  79. synth_ai/cli/demo_apps/demo_task_apps/crafter/grpo_crafter_task_app.py +185 -0
  80. synth_ai/cli/demo_apps/demo_task_apps/math/_common.py +16 -0
  81. synth_ai/{demos → cli/demo_apps}/demo_task_apps/math/app.py +2 -1
  82. synth_ai/cli/demo_apps/demo_task_apps/math/config.toml +73 -0
  83. synth_ai/{demos → cli/demo_apps}/demo_task_apps/math/deploy_modal.py +3 -6
  84. synth_ai/cli/demo_apps/demo_task_apps/math/modal_task_app.py +738 -0
  85. synth_ai/cli/demo_apps/demo_task_apps/math/task_app_entry.py +39 -0
  86. synth_ai/cli/demo_apps/math/__init__.py +1 -0
  87. synth_ai/cli/demo_apps/math/_common.py +16 -0
  88. synth_ai/cli/demo_apps/math/app.py +38 -0
  89. synth_ai/cli/demo_apps/math/config.toml +75 -0
  90. synth_ai/cli/demo_apps/math/deploy_modal.py +54 -0
  91. synth_ai/cli/demo_apps/math/modal_task_app.py +698 -0
  92. synth_ai/cli/demo_apps/math/task_app_entry.py +53 -0
  93. synth_ai/cli/demo_apps/mipro/main.py +271 -0
  94. synth_ai/cli/demo_apps/mipro/task_app.py +922 -0
  95. synth_ai/cli/demo_apps/mipro/train_cfg.toml +92 -0
  96. synth_ai/cli/demos/__init__.py +12 -0
  97. synth_ai/cli/demos/demo.py +32 -0
  98. synth_ai/cli/demos/rl_demo.py +254 -0
  99. synth_ai/cli/deploy.py +216 -0
  100. synth_ai/cli/infra/__init__.py +14 -0
  101. synth_ai/cli/{balance.py → infra/balance.py} +21 -3
  102. synth_ai/cli/infra/mcp.py +35 -0
  103. synth_ai/cli/infra/modal_app.py +36 -0
  104. synth_ai/cli/infra/setup.py +69 -0
  105. synth_ai/cli/infra/status.py +16 -0
  106. synth_ai/cli/infra/turso.py +77 -0
  107. synth_ai/cli/lib/__init__.py +10 -0
  108. synth_ai/cli/lib/agents.py +76 -0
  109. synth_ai/cli/lib/apps/modal_app.py +101 -0
  110. synth_ai/cli/lib/apps/task_app.py +642 -0
  111. synth_ai/cli/lib/bin.py +39 -0
  112. synth_ai/cli/lib/env.py +375 -0
  113. synth_ai/cli/lib/errors.py +85 -0
  114. synth_ai/cli/lib/modal.py +315 -0
  115. synth_ai/cli/lib/plotting.py +126 -0
  116. synth_ai/cli/lib/prompt_args.py +39 -0
  117. synth_ai/cli/lib/prompts.py +284 -0
  118. synth_ai/cli/lib/sqld.py +122 -0
  119. synth_ai/cli/lib/task_app_discovery.py +884 -0
  120. synth_ai/cli/lib/task_app_env.py +295 -0
  121. synth_ai/cli/lib/train_cfgs.py +300 -0
  122. synth_ai/cli/lib/tunnel_records.py +207 -0
  123. synth_ai/cli/local/__init__.py +14 -0
  124. synth_ai/cli/local/experiment_queue/__init__.py +72 -0
  125. synth_ai/cli/local/experiment_queue/api_schemas.py +221 -0
  126. synth_ai/cli/local/experiment_queue/celery_app.py +208 -0
  127. synth_ai/cli/local/experiment_queue/config.py +128 -0
  128. synth_ai/cli/local/experiment_queue/config_utils.py +272 -0
  129. synth_ai/cli/local/experiment_queue/database.py +175 -0
  130. synth_ai/cli/local/experiment_queue/dispatcher.py +119 -0
  131. synth_ai/cli/local/experiment_queue/models.py +231 -0
  132. synth_ai/cli/local/experiment_queue/progress_info.py +160 -0
  133. synth_ai/cli/local/experiment_queue/results.py +373 -0
  134. synth_ai/cli/local/experiment_queue/schemas.py +131 -0
  135. synth_ai/cli/local/experiment_queue/service.py +344 -0
  136. synth_ai/cli/local/experiment_queue/status.py +372 -0
  137. synth_ai/cli/local/experiment_queue/status_tracker.py +360 -0
  138. synth_ai/cli/local/experiment_queue/tasks.py +1984 -0
  139. synth_ai/cli/local/experiment_queue/trace_storage.py +65 -0
  140. synth_ai/cli/local/experiment_queue/validation.py +157 -0
  141. synth_ai/cli/local/session/__init__.py +92 -0
  142. synth_ai/cli/local/session/client.py +383 -0
  143. synth_ai/cli/local/session/constants.py +63 -0
  144. synth_ai/cli/local/session/exceptions.py +105 -0
  145. synth_ai/cli/local/session/manager.py +139 -0
  146. synth_ai/cli/local/session/models.py +89 -0
  147. synth_ai/cli/local/session/query.py +110 -0
  148. synth_ai/cli/root.py +150 -102
  149. synth_ai/cli/task_apps/__init__.py +37 -0
  150. synth_ai/cli/task_apps/commands.py +3145 -0
  151. synth_ai/cli/task_apps/deploy.py +7 -0
  152. synth_ai/cli/task_apps/list.py +26 -0
  153. synth_ai/cli/task_apps/main.py +36 -0
  154. synth_ai/cli/task_apps/modal_serve.py +11 -0
  155. synth_ai/cli/task_apps/serve.py +11 -0
  156. synth_ai/cli/training/__init__.py +8 -0
  157. synth_ai/cli/training/train.py +5 -0
  158. synth_ai/cli/training/train_cfg.py +34 -0
  159. synth_ai/cli/{watch.py → training/watch.py} +13 -18
  160. synth_ai/cli/turso.py +52 -0
  161. synth_ai/cli/utils/__init__.py +8 -0
  162. synth_ai/cli/utils/experiments.py +235 -0
  163. synth_ai/cli/utils/queue.py +504 -0
  164. synth_ai/cli/{recent.py → utils/recent.py} +13 -7
  165. synth_ai/cli/{traces.py → utils/traces.py} +9 -5
  166. synth_ai/contracts/__init__.py +67 -0
  167. synth_ai/core/__init__.py +100 -0
  168. synth_ai/core/_utils/__init__.py +54 -0
  169. synth_ai/core/_utils/base_url.py +10 -0
  170. synth_ai/core/_utils/http.py +10 -0
  171. synth_ai/core/_utils/prompts.py +14 -0
  172. synth_ai/core/_utils/task_app_state.py +12 -0
  173. synth_ai/core/_utils/user_config.py +10 -0
  174. synth_ai/core/apps/common.py +116 -0
  175. synth_ai/core/auth.py +95 -0
  176. synth_ai/core/cfgs.py +240 -0
  177. synth_ai/core/config/__init__.py +16 -0
  178. synth_ai/core/config/base.py +168 -0
  179. synth_ai/core/config/resolver.py +89 -0
  180. synth_ai/core/env.py +231 -0
  181. synth_ai/core/errors.py +126 -0
  182. synth_ai/core/http.py +230 -0
  183. synth_ai/core/integrations/__init__.py +11 -0
  184. synth_ai/core/integrations/cloudflare.py +1710 -0
  185. synth_ai/core/integrations/mcp/__init__.py +6 -0
  186. synth_ai/core/integrations/mcp/__main__.py +8 -0
  187. synth_ai/core/integrations/mcp/claude.py +36 -0
  188. synth_ai/core/integrations/mcp/main.py +254 -0
  189. synth_ai/core/integrations/mcp/setup.py +100 -0
  190. synth_ai/core/integrations/modal.py +277 -0
  191. synth_ai/core/json.py +72 -0
  192. synth_ai/core/log_filter.py +99 -0
  193. synth_ai/core/logging.py +82 -0
  194. synth_ai/core/paths.py +107 -0
  195. synth_ai/core/pricing.py +109 -0
  196. synth_ai/core/process.py +233 -0
  197. synth_ai/core/ssl.py +25 -0
  198. synth_ai/core/storage/__init__.py +71 -0
  199. synth_ai/core/task_app_state.py +318 -0
  200. synth_ai/core/telemetry.py +282 -0
  201. synth_ai/{tracing_v3 → core/tracing_v3}/__init__.py +5 -1
  202. synth_ai/{tracing_v3 → core/tracing_v3}/abstractions.py +21 -4
  203. synth_ai/core/tracing_v3/config.py +229 -0
  204. synth_ai/core/tracing_v3/constants.py +21 -0
  205. synth_ai/{tracing_v3 → core/tracing_v3}/db_config.py +42 -29
  206. synth_ai/{tracing_v3 → core/tracing_v3}/decorators.py +80 -45
  207. synth_ai/{tracing_v3 → core/tracing_v3}/examples/basic_usage.py +15 -9
  208. synth_ai/{tracing_v3 → core/tracing_v3}/hooks.py +6 -4
  209. synth_ai/{tracing_v3 → core/tracing_v3}/llm_call_record_helpers.py +161 -61
  210. synth_ai/{tracing_v3 → core/tracing_v3}/migration_helper.py +1 -2
  211. synth_ai/{tracing_v3 → core/tracing_v3}/replica_sync.py +12 -7
  212. synth_ai/core/tracing_v3/serialization.py +130 -0
  213. synth_ai/{tracing_v3 → core/tracing_v3}/session_tracer.py +88 -21
  214. synth_ai/{tracing_v3 → core/tracing_v3}/storage/base.py +99 -12
  215. synth_ai/core/tracing_v3/storage/config.py +109 -0
  216. synth_ai/{tracing_v3 → core/tracing_v3}/storage/factory.py +11 -9
  217. synth_ai/{tracing_v3 → core/tracing_v3}/storage/utils.py +15 -11
  218. synth_ai/core/tracing_v3/trace_utils.py +326 -0
  219. synth_ai/core/tracing_v3/turso/__init__.py +12 -0
  220. synth_ai/core/tracing_v3/turso/daemon.py +278 -0
  221. synth_ai/{tracing_v3 → core/tracing_v3}/turso/models.py +7 -3
  222. synth_ai/core/tracing_v3/turso/native_manager.py +1385 -0
  223. synth_ai/{tracing_v3 → core/tracing_v3}/utils.py +5 -4
  224. synth_ai/core/urls.py +18 -0
  225. synth_ai/core/user_config.py +137 -0
  226. synth_ai/core/uvicorn.py +222 -0
  227. synth_ai/data/__init__.py +83 -0
  228. synth_ai/data/enums.py +123 -0
  229. synth_ai/data/rewards.py +152 -0
  230. synth_ai/data/traces.py +35 -0
  231. synth_ai/products/__init__.py +6 -0
  232. synth_ai/products/graph_evolve/__init__.py +46 -0
  233. synth_ai/products/graph_evolve/client.py +226 -0
  234. synth_ai/products/graph_evolve/config.py +591 -0
  235. synth_ai/products/graph_evolve/converters/__init__.py +42 -0
  236. synth_ai/products/graph_evolve/converters/openai_sft.py +484 -0
  237. synth_ai/products/graph_evolve/examples/hotpotqa/config.toml +109 -0
  238. synth_ai/products/graph_evolve/run.py +222 -0
  239. synth_ai/products/graph_gepa/__init__.py +23 -0
  240. synth_ai/products/graph_gepa/converters/__init__.py +19 -0
  241. synth_ai/products/graph_gepa/converters/openai_sft.py +29 -0
  242. synth_ai/sdk/__init__.py +123 -0
  243. synth_ai/sdk/api/__init__.py +1 -0
  244. synth_ai/sdk/api/models/supported.py +514 -0
  245. synth_ai/sdk/api/research_agent/__init__.py +296 -0
  246. synth_ai/sdk/api/train/__init__.py +85 -0
  247. synth_ai/sdk/api/train/builders.py +895 -0
  248. synth_ai/sdk/api/train/cli.py +2199 -0
  249. synth_ai/sdk/api/train/config_finder.py +267 -0
  250. synth_ai/sdk/api/train/configs/__init__.py +65 -0
  251. synth_ai/sdk/api/train/configs/prompt_learning.py +1706 -0
  252. synth_ai/sdk/api/train/configs/rl.py +187 -0
  253. synth_ai/sdk/api/train/configs/sft.py +99 -0
  254. synth_ai/sdk/api/train/configs/shared.py +81 -0
  255. synth_ai/sdk/api/train/context_learning.py +312 -0
  256. synth_ai/sdk/api/train/env_resolver.py +418 -0
  257. synth_ai/sdk/api/train/graph_validators.py +216 -0
  258. synth_ai/sdk/api/train/graphgen.py +984 -0
  259. synth_ai/sdk/api/train/graphgen_models.py +823 -0
  260. synth_ai/sdk/api/train/graphgen_validators.py +109 -0
  261. synth_ai/sdk/api/train/local_api.py +10 -0
  262. synth_ai/sdk/api/train/pollers.py +124 -0
  263. synth_ai/sdk/api/train/progress/__init__.py +97 -0
  264. synth_ai/sdk/api/train/progress/dataclasses.py +569 -0
  265. synth_ai/sdk/api/train/progress/events.py +326 -0
  266. synth_ai/sdk/api/train/progress/results.py +428 -0
  267. synth_ai/sdk/api/train/progress/tracker.py +641 -0
  268. synth_ai/sdk/api/train/prompt_learning.py +469 -0
  269. synth_ai/sdk/api/train/rl.py +441 -0
  270. synth_ai/sdk/api/train/sft.py +396 -0
  271. synth_ai/sdk/api/train/summary.py +522 -0
  272. synth_ai/sdk/api/train/supported_algos.py +147 -0
  273. synth_ai/sdk/api/train/task_app.py +351 -0
  274. synth_ai/sdk/api/train/utils.py +279 -0
  275. synth_ai/sdk/api/train/validators.py +2424 -0
  276. synth_ai/sdk/graphs/__init__.py +15 -0
  277. synth_ai/sdk/graphs/completions.py +570 -0
  278. synth_ai/{inference → sdk/inference}/__init__.py +0 -1
  279. synth_ai/sdk/inference/client.py +128 -0
  280. synth_ai/sdk/jobs/__init__.py +16 -0
  281. synth_ai/sdk/jobs/client.py +371 -0
  282. synth_ai/sdk/judging/__init__.py +14 -0
  283. synth_ai/sdk/judging/base.py +24 -0
  284. synth_ai/sdk/judging/client.py +40 -0
  285. synth_ai/sdk/judging/schemas.py +222 -0
  286. synth_ai/sdk/judging/types.py +42 -0
  287. synth_ai/sdk/learning/__init__.py +99 -0
  288. synth_ai/sdk/learning/algorithms.py +14 -0
  289. synth_ai/{learning → sdk/learning}/client.py +121 -30
  290. synth_ai/sdk/learning/config.py +5 -0
  291. synth_ai/{learning → sdk/learning}/constants.py +0 -2
  292. synth_ai/sdk/learning/context_learning_client.py +531 -0
  293. synth_ai/sdk/learning/context_learning_types.py +292 -0
  294. synth_ai/sdk/learning/ft_client.py +7 -0
  295. synth_ai/{learning → sdk/learning}/health.py +15 -9
  296. synth_ai/{learning → sdk/learning}/jobs.py +44 -47
  297. synth_ai/sdk/learning/prompt_extraction.py +334 -0
  298. synth_ai/sdk/learning/prompt_learning_client.py +455 -0
  299. synth_ai/sdk/learning/prompt_learning_types.py +186 -0
  300. synth_ai/{rl → sdk/learning/rl}/__init__.py +13 -8
  301. synth_ai/{learning/rl_client.py → sdk/learning/rl/client.py} +89 -77
  302. synth_ai/sdk/learning/rl/config.py +31 -0
  303. synth_ai/{rl → sdk/learning/rl}/contracts.py +5 -14
  304. synth_ai/{rl → sdk/learning/rl}/env_keys.py +45 -16
  305. synth_ai/sdk/learning/rl/secrets.py +13 -0
  306. synth_ai/sdk/learning/rl_client.py +5 -0
  307. synth_ai/sdk/learning/sft/__init__.py +29 -0
  308. synth_ai/sdk/learning/sft/client.py +95 -0
  309. synth_ai/sdk/learning/sft/config.py +270 -0
  310. synth_ai/sdk/learning/sft/data.py +698 -0
  311. synth_ai/sdk/learning/sse.py +57 -0
  312. synth_ai/sdk/learning/validators.py +52 -0
  313. synth_ai/sdk/localapi/__init__.py +40 -0
  314. synth_ai/sdk/localapi/apps/__init__.py +28 -0
  315. synth_ai/sdk/localapi/client.py +10 -0
  316. synth_ai/sdk/localapi/contracts.py +10 -0
  317. synth_ai/sdk/localapi/helpers.py +519 -0
  318. synth_ai/sdk/localapi/rollouts.py +87 -0
  319. synth_ai/sdk/localapi/server.py +29 -0
  320. synth_ai/sdk/localapi/template.py +70 -0
  321. synth_ai/sdk/streaming/__init__.py +35 -0
  322. synth_ai/sdk/streaming/config.py +94 -0
  323. synth_ai/sdk/streaming/handlers.py +1997 -0
  324. synth_ai/sdk/streaming/streamer.py +713 -0
  325. synth_ai/sdk/streaming/types.py +112 -0
  326. synth_ai/sdk/task/__init__.py +164 -0
  327. synth_ai/sdk/task/apps/__init__.py +169 -0
  328. synth_ai/sdk/task/auth.py +165 -0
  329. synth_ai/sdk/task/client.py +175 -0
  330. synth_ai/sdk/task/config.py +257 -0
  331. synth_ai/sdk/task/contracts.py +219 -0
  332. synth_ai/sdk/task/datasets.py +108 -0
  333. synth_ai/sdk/task/errors.py +50 -0
  334. synth_ai/sdk/task/health.py +34 -0
  335. synth_ai/sdk/task/in_process.py +1190 -0
  336. synth_ai/sdk/task/in_process_runner.py +314 -0
  337. synth_ai/sdk/task/inference_api.py +299 -0
  338. synth_ai/sdk/task/json.py +111 -0
  339. synth_ai/sdk/task/proxy.py +287 -0
  340. synth_ai/sdk/task/rubrics/__init__.py +55 -0
  341. synth_ai/sdk/task/rubrics/loaders.py +156 -0
  342. synth_ai/sdk/task/rubrics/models.py +57 -0
  343. synth_ai/sdk/task/rubrics/scoring.py +116 -0
  344. synth_ai/sdk/task/rubrics/strict.py +149 -0
  345. synth_ai/sdk/task/rubrics.py +219 -0
  346. synth_ai/sdk/task/server.py +631 -0
  347. synth_ai/sdk/task/trace_correlation_helpers.py +539 -0
  348. synth_ai/sdk/task/tracing_utils.py +95 -0
  349. synth_ai/sdk/task/validators.py +441 -0
  350. synth_ai/sdk/task/vendors.py +59 -0
  351. synth_ai/sdk/training/__init__.py +102 -0
  352. synth_ai/sdk/tunnels/__init__.py +83 -0
  353. synth_ai/sdk/tunnels/cleanup.py +83 -0
  354. synth_ai/sdk/tunnels/ports.py +120 -0
  355. synth_ai/utils/__init__.py +213 -0
  356. synth_ai-0.4.3.dist-info/METADATA +262 -0
  357. synth_ai-0.4.3.dist-info/RECORD +370 -0
  358. {synth_ai-0.2.6.dev1.dist-info → synth_ai-0.4.3.dist-info}/entry_points.txt +0 -1
  359. synth_ai/cli/calc.py +0 -69
  360. synth_ai/cli/demo.py +0 -131
  361. synth_ai/cli/legacy_root_backup.py +0 -470
  362. synth_ai/cli/man.py +0 -106
  363. synth_ai/cli/rl_demo.py +0 -137
  364. synth_ai/cli/status.py +0 -133
  365. synth_ai/config/base_url.py +0 -98
  366. synth_ai/core/experiment.py +0 -15
  367. synth_ai/core/system.py +0 -15
  368. synth_ai/demos/core/__init__.py +0 -1
  369. synth_ai/demos/core/cli.py +0 -685
  370. synth_ai/demos/demo_task_apps/__init__.py +0 -1
  371. synth_ai/demos/demo_task_apps/math/config.toml +0 -44
  372. synth_ai/demos/demo_task_apps/math/deploy_task_app.sh +0 -22
  373. synth_ai/environments/__init__.py +0 -31
  374. synth_ai/environments/environment/__init__.py +0 -1
  375. synth_ai/environments/environment/artifacts/__init__.py +0 -1
  376. synth_ai/environments/environment/artifacts/base.py +0 -52
  377. synth_ai/environments/environment/core.py +0 -67
  378. synth_ai/environments/environment/db/__init__.py +0 -1
  379. synth_ai/environments/environment/db/sqlite.py +0 -45
  380. synth_ai/environments/environment/registry.py +0 -233
  381. synth_ai/environments/environment/resources/sqlite.py +0 -45
  382. synth_ai/environments/environment/results.py +0 -1
  383. synth_ai/environments/environment/rewards/__init__.py +0 -1
  384. synth_ai/environments/environment/rewards/core.py +0 -29
  385. synth_ai/environments/environment/shared_engine.py +0 -26
  386. synth_ai/environments/environment/tools/__init__.py +0 -200
  387. synth_ai/environments/examples/__init__.py +0 -1
  388. synth_ai/environments/examples/bandit/__init__.py +0 -33
  389. synth_ai/environments/examples/bandit/engine.py +0 -294
  390. synth_ai/environments/examples/bandit/environment.py +0 -194
  391. synth_ai/environments/examples/bandit/taskset.py +0 -200
  392. synth_ai/environments/examples/crafter_classic/__init__.py +0 -8
  393. synth_ai/environments/examples/crafter_classic/agent_demos/analyze_semantic_words_markdown.py +0 -250
  394. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_comprehensive_evaluation.py +0 -59
  395. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_evaluation_browser.py +0 -152
  396. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_evaluation_config.toml +0 -24
  397. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_evaluation_framework.py +0 -1194
  398. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/crafter_synth_config.toml +0 -56
  399. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/filter_config_modal.toml +0 -32
  400. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/filter_traces_sft_turso.py +0 -724
  401. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/kick_off_ft_modal.py +0 -384
  402. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_action_results.py +0 -53
  403. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_agent_actions.py +0 -178
  404. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_latest_run.py +0 -222
  405. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_lm_traces.py +0 -183
  406. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_no_rewards.py +0 -210
  407. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_trace_issue.py +0 -206
  408. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/check_db_schema.py +0 -49
  409. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/check_latest_results.py +0 -64
  410. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/debug_agent_responses.py +0 -88
  411. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/quick_trace_check.py +0 -77
  412. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/compare_experiments.py +0 -324
  413. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/filter_traces_sft_turso.py +0 -580
  414. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/kick_off_ft_oai.py +0 -362
  415. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/multi_model_config.toml +0 -49
  416. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/analyze_enhanced_hooks.py +0 -332
  417. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/analyze_hook_events.py +0 -97
  418. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/analyze_hook_results.py +0 -217
  419. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/check_hook_storage.py +0 -87
  420. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/check_seeds.py +0 -88
  421. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/compare_seed_performance.py +0 -195
  422. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/custom_eval_pipelines.py +0 -400
  423. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/plot_hook_frequency.py +0 -195
  424. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/seed_analysis_summary.py +0 -56
  425. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/run_rollouts_for_models_and_compare_v3.py +0 -858
  426. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_quick_evaluation.py +0 -52
  427. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_react_agent.py +0 -874
  428. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_trace_evaluation.py +0 -1412
  429. synth_ai/environments/examples/crafter_classic/agent_demos/example_v3_usage.py +0 -216
  430. synth_ai/environments/examples/crafter_classic/agent_demos/old/compare_traces.py +0 -296
  431. synth_ai/environments/examples/crafter_classic/agent_demos/old/crafter_comprehensive_evaluation.py +0 -58
  432. synth_ai/environments/examples/crafter_classic/agent_demos/old/crafter_env_serialization.py +0 -464
  433. synth_ai/environments/examples/crafter_classic/agent_demos/old/crafter_evaluation_browser.py +0 -152
  434. synth_ai/environments/examples/crafter_classic/agent_demos/old/crafter_quick_evaluation.py +0 -51
  435. synth_ai/environments/examples/crafter_classic/agent_demos/old/crafter_trace_evaluation.py +0 -1412
  436. synth_ai/environments/examples/crafter_classic/agent_demos/old/debug_player_loss.py +0 -112
  437. synth_ai/environments/examples/crafter_classic/agent_demos/old/diagnose_service.py +0 -203
  438. synth_ai/environments/examples/crafter_classic/agent_demos/old/diagnose_slowness.py +0 -305
  439. synth_ai/environments/examples/crafter_classic/agent_demos/old/eval_by_difficulty.py +0 -126
  440. synth_ai/environments/examples/crafter_classic/agent_demos/old/eval_example.py +0 -94
  441. synth_ai/environments/examples/crafter_classic/agent_demos/old/explore_saved_states.py +0 -142
  442. synth_ai/environments/examples/crafter_classic/agent_demos/old/filter_traces_sft.py +0 -26
  443. synth_ai/environments/examples/crafter_classic/agent_demos/old/filter_traces_sft_OLD.py +0 -984
  444. synth_ai/environments/examples/crafter_classic/agent_demos/old/generate_ft_data_gemini.py +0 -724
  445. synth_ai/environments/examples/crafter_classic/agent_demos/old/generate_ft_data_modal.py +0 -386
  446. synth_ai/environments/examples/crafter_classic/agent_demos/old/generate_ft_metadata.py +0 -205
  447. synth_ai/environments/examples/crafter_classic/agent_demos/old/kick_off_ft_gemini.py +0 -150
  448. synth_ai/environments/examples/crafter_classic/agent_demos/old/kick_off_ft_modal.py +0 -283
  449. synth_ai/environments/examples/crafter_classic/agent_demos/old/prepare_vertex_ft.py +0 -280
  450. synth_ai/environments/examples/crafter_classic/agent_demos/old/profile_env_slowness.py +0 -456
  451. synth_ai/environments/examples/crafter_classic/agent_demos/old/replicate_issue.py +0 -166
  452. synth_ai/environments/examples/crafter_classic/agent_demos/old/run_and_eval.py +0 -102
  453. synth_ai/environments/examples/crafter_classic/agent_demos/old/run_comparison.py +0 -128
  454. synth_ai/environments/examples/crafter_classic/agent_demos/old/run_qwen_rollouts.py +0 -655
  455. synth_ai/environments/examples/crafter_classic/agent_demos/old/trace_eval_OLD.py +0 -202
  456. synth_ai/environments/examples/crafter_classic/agent_demos/old/validate_openai_format.py +0 -166
  457. synth_ai/environments/examples/crafter_classic/config_logging.py +0 -111
  458. synth_ai/environments/examples/crafter_classic/debug_translation.py +0 -0
  459. synth_ai/environments/examples/crafter_classic/engine.py +0 -579
  460. synth_ai/environments/examples/crafter_classic/engine_deterministic_patch.py +0 -64
  461. synth_ai/environments/examples/crafter_classic/engine_helpers/action_map.py +0 -6
  462. synth_ai/environments/examples/crafter_classic/engine_helpers/serialization.py +0 -75
  463. synth_ai/environments/examples/crafter_classic/engine_serialization_patch_v3.py +0 -267
  464. synth_ai/environments/examples/crafter_classic/environment.py +0 -404
  465. synth_ai/environments/examples/crafter_classic/taskset.py +0 -233
  466. synth_ai/environments/examples/crafter_classic/trace_hooks_v3.py +0 -228
  467. synth_ai/environments/examples/crafter_classic/world_config_patch_simple.py +0 -299
  468. synth_ai/environments/examples/crafter_custom/__init__.py +0 -4
  469. synth_ai/environments/examples/crafter_custom/agent_demos/__init__.py +0 -1
  470. synth_ai/environments/examples/crafter_custom/agent_demos/trace_eval.py +0 -202
  471. synth_ai/environments/examples/crafter_custom/crafter/__init__.py +0 -7
  472. synth_ai/environments/examples/crafter_custom/crafter/config.py +0 -182
  473. synth_ai/environments/examples/crafter_custom/crafter/constants.py +0 -8
  474. synth_ai/environments/examples/crafter_custom/crafter/engine.py +0 -269
  475. synth_ai/environments/examples/crafter_custom/crafter/env.py +0 -262
  476. synth_ai/environments/examples/crafter_custom/crafter/objects.py +0 -417
  477. synth_ai/environments/examples/crafter_custom/crafter/recorder.py +0 -187
  478. synth_ai/environments/examples/crafter_custom/crafter/worldgen.py +0 -118
  479. synth_ai/environments/examples/crafter_custom/dataset_builder.py +0 -373
  480. synth_ai/environments/examples/crafter_custom/environment.py +0 -312
  481. synth_ai/environments/examples/crafter_custom/old/analyze_diamond_issue.py +0 -159
  482. synth_ai/environments/examples/crafter_custom/old/analyze_diamond_spawning.py +0 -158
  483. synth_ai/environments/examples/crafter_custom/old/compare_worlds.py +0 -71
  484. synth_ai/environments/examples/crafter_custom/old/dataset_stats.py +0 -105
  485. synth_ai/environments/examples/crafter_custom/old/diamond_spawning_summary.py +0 -119
  486. synth_ai/environments/examples/crafter_custom/old/example_dataset_usage.py +0 -52
  487. synth_ai/environments/examples/crafter_custom/run_dataset.py +0 -305
  488. synth_ai/environments/examples/enron/art_helpers/email_search_tools.py +0 -156
  489. synth_ai/environments/examples/enron/art_helpers/local_email_db.py +0 -281
  490. synth_ai/environments/examples/enron/art_helpers/types_enron.py +0 -25
  491. synth_ai/environments/examples/enron/engine.py +0 -295
  492. synth_ai/environments/examples/enron/environment.py +0 -166
  493. synth_ai/environments/examples/enron/taskset.py +0 -112
  494. synth_ai/environments/examples/enron/units/keyword_stats.py +0 -112
  495. synth_ai/environments/examples/minigrid/__init__.py +0 -48
  496. synth_ai/environments/examples/minigrid/agent_demos/minigrid_evaluation_framework.py +0 -1188
  497. synth_ai/environments/examples/minigrid/agent_demos/minigrid_quick_evaluation.py +0 -48
  498. synth_ai/environments/examples/minigrid/agent_demos/minigrid_react_agent.py +0 -562
  499. synth_ai/environments/examples/minigrid/agent_demos/minigrid_trace_evaluation.py +0 -221
  500. synth_ai/environments/examples/minigrid/engine.py +0 -589
  501. synth_ai/environments/examples/minigrid/environment.py +0 -274
  502. synth_ai/environments/examples/minigrid/environment_mapping.py +0 -242
  503. synth_ai/environments/examples/minigrid/puzzle_loader.py +0 -417
  504. synth_ai/environments/examples/minigrid/taskset.py +0 -583
  505. synth_ai/environments/examples/nethack/__init__.py +0 -7
  506. synth_ai/environments/examples/nethack/achievements.py +0 -337
  507. synth_ai/environments/examples/nethack/agent_demos/nethack_evaluation_framework.py +0 -981
  508. synth_ai/environments/examples/nethack/agent_demos/nethack_quick_evaluation.py +0 -74
  509. synth_ai/environments/examples/nethack/agent_demos/nethack_react_agent.py +0 -831
  510. synth_ai/environments/examples/nethack/engine.py +0 -739
  511. synth_ai/environments/examples/nethack/environment.py +0 -256
  512. synth_ai/environments/examples/nethack/helpers/__init__.py +0 -41
  513. synth_ai/environments/examples/nethack/helpers/action_mapping.py +0 -301
  514. synth_ai/environments/examples/nethack/helpers/nle_wrapper.py +0 -402
  515. synth_ai/environments/examples/nethack/helpers/observation_utils.py +0 -433
  516. synth_ai/environments/examples/nethack/helpers/recording_wrapper.py +0 -200
  517. synth_ai/environments/examples/nethack/helpers/trajectory_recorder.py +0 -269
  518. synth_ai/environments/examples/nethack/helpers/visualization/replay_viewer.py +0 -308
  519. synth_ai/environments/examples/nethack/helpers/visualization/visualizer.py +0 -431
  520. synth_ai/environments/examples/nethack/taskset.py +0 -323
  521. synth_ai/environments/examples/red/__init__.py +0 -7
  522. synth_ai/environments/examples/red/agent_demos/__init__.py +0 -1
  523. synth_ai/environments/examples/red/config_logging.py +0 -110
  524. synth_ai/environments/examples/red/engine.py +0 -694
  525. synth_ai/environments/examples/red/engine_helpers/__init__.py +0 -1
  526. synth_ai/environments/examples/red/engine_helpers/memory_map.py +0 -28
  527. synth_ai/environments/examples/red/engine_helpers/reward_components.py +0 -276
  528. synth_ai/environments/examples/red/engine_helpers/reward_library/__init__.py +0 -142
  529. synth_ai/environments/examples/red/engine_helpers/reward_library/adaptive_rewards.py +0 -57
  530. synth_ai/environments/examples/red/engine_helpers/reward_library/battle_rewards.py +0 -284
  531. synth_ai/environments/examples/red/engine_helpers/reward_library/composite_rewards.py +0 -150
  532. synth_ai/environments/examples/red/engine_helpers/reward_library/economy_rewards.py +0 -138
  533. synth_ai/environments/examples/red/engine_helpers/reward_library/efficiency_rewards.py +0 -57
  534. synth_ai/environments/examples/red/engine_helpers/reward_library/exploration_rewards.py +0 -331
  535. synth_ai/environments/examples/red/engine_helpers/reward_library/novelty_rewards.py +0 -121
  536. synth_ai/environments/examples/red/engine_helpers/reward_library/pallet_town_rewards.py +0 -559
  537. synth_ai/environments/examples/red/engine_helpers/reward_library/pokemon_rewards.py +0 -313
  538. synth_ai/environments/examples/red/engine_helpers/reward_library/social_rewards.py +0 -148
  539. synth_ai/environments/examples/red/engine_helpers/reward_library/story_rewards.py +0 -247
  540. synth_ai/environments/examples/red/engine_helpers/screen_analysis.py +0 -368
  541. synth_ai/environments/examples/red/engine_helpers/state_extraction.py +0 -140
  542. synth_ai/environments/examples/red/environment.py +0 -238
  543. synth_ai/environments/examples/red/taskset.py +0 -79
  544. synth_ai/environments/examples/red/units/__init__.py +0 -1
  545. synth_ai/environments/examples/sokoban/__init__.py +0 -1
  546. synth_ai/environments/examples/sokoban/agent_demos/sokoban_full_eval.py +0 -899
  547. synth_ai/environments/examples/sokoban/engine.py +0 -678
  548. synth_ai/environments/examples/sokoban/engine_helpers/__init__.py +0 -1
  549. synth_ai/environments/examples/sokoban/engine_helpers/room_utils.py +0 -657
  550. synth_ai/environments/examples/sokoban/engine_helpers/vendored/__init__.py +0 -18
  551. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/__init__.py +0 -3
  552. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/boxoban_env.py +0 -131
  553. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/render_utils.py +0 -370
  554. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/room_utils.py +0 -332
  555. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env.py +0 -306
  556. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_fixed_targets.py +0 -67
  557. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_pull.py +0 -115
  558. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_two_player.py +0 -123
  559. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_variations.py +0 -394
  560. synth_ai/environments/examples/sokoban/environment.py +0 -229
  561. synth_ai/environments/examples/sokoban/generate_verified_puzzles.py +0 -440
  562. synth_ai/environments/examples/sokoban/puzzle_loader.py +0 -312
  563. synth_ai/environments/examples/sokoban/taskset.py +0 -428
  564. synth_ai/environments/examples/sokoban/units/astar_common.py +0 -95
  565. synth_ai/environments/examples/tictactoe/__init__.py +0 -1
  566. synth_ai/environments/examples/tictactoe/engine.py +0 -368
  567. synth_ai/environments/examples/tictactoe/environment.py +0 -240
  568. synth_ai/environments/examples/tictactoe/taskset.py +0 -215
  569. synth_ai/environments/examples/verilog/__init__.py +0 -10
  570. synth_ai/environments/examples/verilog/engine.py +0 -329
  571. synth_ai/environments/examples/verilog/environment.py +0 -350
  572. synth_ai/environments/examples/verilog/taskset.py +0 -420
  573. synth_ai/environments/examples/wordle/__init__.py +0 -29
  574. synth_ai/environments/examples/wordle/engine.py +0 -398
  575. synth_ai/environments/examples/wordle/environment.py +0 -159
  576. synth_ai/environments/examples/wordle/helpers/generate_instances_wordfreq.py +0 -75
  577. synth_ai/environments/examples/wordle/taskset.py +0 -230
  578. synth_ai/environments/reproducibility/core.py +0 -42
  579. synth_ai/environments/reproducibility/helpers.py +0 -0
  580. synth_ai/environments/reproducibility/tree.py +0 -364
  581. synth_ai/environments/service/app.py +0 -91
  582. synth_ai/environments/service/core_routes.py +0 -1020
  583. synth_ai/environments/service/external_registry.py +0 -56
  584. synth_ai/environments/service/registry.py +0 -9
  585. synth_ai/environments/stateful/__init__.py +0 -1
  586. synth_ai/environments/stateful/core.py +0 -163
  587. synth_ai/environments/stateful/engine.py +0 -21
  588. synth_ai/environments/stateful/state.py +0 -7
  589. synth_ai/environments/tasks/api.py +0 -19
  590. synth_ai/environments/tasks/core.py +0 -80
  591. synth_ai/environments/tasks/filters.py +0 -41
  592. synth_ai/environments/tasks/utils.py +0 -91
  593. synth_ai/environments/v0_observability/history.py +0 -3
  594. synth_ai/environments/v0_observability/log.py +0 -2
  595. synth_ai/evals/base.py +0 -15
  596. synth_ai/experimental/synth_oss.py +0 -446
  597. synth_ai/http.py +0 -102
  598. synth_ai/inference/client.py +0 -20
  599. synth_ai/install_sqld.sh +0 -40
  600. synth_ai/jobs/client.py +0 -246
  601. synth_ai/learning/__init__.py +0 -24
  602. synth_ai/learning/config.py +0 -43
  603. synth_ai/learning/filtering.py +0 -0
  604. synth_ai/learning/ft_client.py +0 -59
  605. synth_ai/learning/offline/dpo.py +0 -0
  606. synth_ai/learning/offline/providers.py +0 -7
  607. synth_ai/learning/offline/sft.py +0 -0
  608. synth_ai/learning/offline/shared.py +0 -0
  609. synth_ai/learning/online/grpo.py +0 -0
  610. synth_ai/learning/online/irft.py +0 -0
  611. synth_ai/learning/prompts/banking77_injection_eval.py +0 -168
  612. synth_ai/learning/prompts/gepa.py +0 -0
  613. synth_ai/learning/prompts/hello_world_in_context_injection_ex.py +0 -213
  614. synth_ai/learning/prompts/mipro.py +0 -289
  615. synth_ai/learning/prompts/random_search.py +0 -246
  616. synth_ai/learning/prompts/run_mipro_banking77.py +0 -172
  617. synth_ai/learning/prompts/run_random_search_banking77.py +0 -324
  618. synth_ai/learning/sse.py +0 -58
  619. synth_ai/learning/validators.py +0 -48
  620. synth_ai/lm/__init__.py +0 -51
  621. synth_ai/lm/caching/constants.py +0 -6
  622. synth_ai/lm/caching/dbs.py +0 -0
  623. synth_ai/lm/caching/ephemeral.py +0 -102
  624. synth_ai/lm/caching/handler.py +0 -137
  625. synth_ai/lm/caching/initialize.py +0 -11
  626. synth_ai/lm/caching/persistent.py +0 -114
  627. synth_ai/lm/config.py +0 -110
  628. synth_ai/lm/constants.py +0 -32
  629. synth_ai/lm/core/__init__.py +0 -8
  630. synth_ai/lm/core/all.py +0 -73
  631. synth_ai/lm/core/exceptions.py +0 -7
  632. synth_ai/lm/core/main.py +0 -319
  633. synth_ai/lm/core/main_v3.py +0 -594
  634. synth_ai/lm/core/synth_models.py +0 -48
  635. synth_ai/lm/core/vendor_clients.py +0 -188
  636. synth_ai/lm/cost/__init__.py +0 -0
  637. synth_ai/lm/cost/monitor.py +0 -1
  638. synth_ai/lm/cost/statefulness.py +0 -1
  639. synth_ai/lm/injection.py +0 -80
  640. synth_ai/lm/overrides.py +0 -206
  641. synth_ai/lm/provider_support/__init__.py +0 -8
  642. synth_ai/lm/provider_support/anthropic.py +0 -972
  643. synth_ai/lm/provider_support/openai.py +0 -1139
  644. synth_ai/lm/provider_support/suppress_logging.py +0 -31
  645. synth_ai/lm/structured_outputs/__init__.py +0 -0
  646. synth_ai/lm/structured_outputs/handler.py +0 -440
  647. synth_ai/lm/structured_outputs/inject.py +0 -297
  648. synth_ai/lm/structured_outputs/rehabilitate.py +0 -185
  649. synth_ai/lm/tools/__init__.py +0 -3
  650. synth_ai/lm/tools/base.py +0 -172
  651. synth_ai/lm/unified_interface.py +0 -202
  652. synth_ai/lm/vendors/__init__.py +0 -0
  653. synth_ai/lm/vendors/base.py +0 -81
  654. synth_ai/lm/vendors/core/__init__.py +0 -0
  655. synth_ai/lm/vendors/core/anthropic_api.py +0 -387
  656. synth_ai/lm/vendors/core/gemini_api.py +0 -292
  657. synth_ai/lm/vendors/core/mistral_api.py +0 -322
  658. synth_ai/lm/vendors/core/openai_api.py +0 -220
  659. synth_ai/lm/vendors/core/synth_dev_api.py +0 -0
  660. synth_ai/lm/vendors/local/__init__.py +0 -0
  661. synth_ai/lm/vendors/local/ollama.py +0 -0
  662. synth_ai/lm/vendors/openai_standard.py +0 -780
  663. synth_ai/lm/vendors/openai_standard_responses.py +0 -256
  664. synth_ai/lm/vendors/retries.py +0 -22
  665. synth_ai/lm/vendors/supported/__init__.py +0 -0
  666. synth_ai/lm/vendors/supported/custom_endpoint.py +0 -417
  667. synth_ai/lm/vendors/supported/deepseek.py +0 -69
  668. synth_ai/lm/vendors/supported/grok.py +0 -75
  669. synth_ai/lm/vendors/supported/groq.py +0 -16
  670. synth_ai/lm/vendors/supported/ollama.py +0 -15
  671. synth_ai/lm/vendors/supported/openrouter.py +0 -74
  672. synth_ai/lm/vendors/supported/together.py +0 -11
  673. synth_ai/lm/vendors/synth_client.py +0 -808
  674. synth_ai/lm/warmup.py +0 -186
  675. synth_ai/rl/secrets.py +0 -19
  676. synth_ai/scripts/verify_rewards.py +0 -100
  677. synth_ai/task/__init__.py +0 -10
  678. synth_ai/task/contracts.py +0 -120
  679. synth_ai/task/health.py +0 -28
  680. synth_ai/task/validators.py +0 -12
  681. synth_ai/tracing/__init__.py +0 -30
  682. synth_ai/tracing_v1/__init__.py +0 -33
  683. synth_ai/tracing_v3/config.py +0 -84
  684. synth_ai/tracing_v3/storage/config.py +0 -62
  685. synth_ai/tracing_v3/turso/__init__.py +0 -25
  686. synth_ai/tracing_v3/turso/daemon.py +0 -144
  687. synth_ai/tracing_v3/turso/manager.py +0 -760
  688. synth_ai/v0/tracing/__init__.py +0 -0
  689. synth_ai/v0/tracing/abstractions.py +0 -224
  690. synth_ai/v0/tracing/base_client.py +0 -91
  691. synth_ai/v0/tracing/client_manager.py +0 -131
  692. synth_ai/v0/tracing/config.py +0 -140
  693. synth_ai/v0/tracing/context.py +0 -146
  694. synth_ai/v0/tracing/decorators.py +0 -680
  695. synth_ai/v0/tracing/events/__init__.py +0 -0
  696. synth_ai/v0/tracing/events/manage.py +0 -147
  697. synth_ai/v0/tracing/events/scope.py +0 -86
  698. synth_ai/v0/tracing/events/store.py +0 -228
  699. synth_ai/v0/tracing/immediate_client.py +0 -151
  700. synth_ai/v0/tracing/local.py +0 -18
  701. synth_ai/v0/tracing/log_client_base.py +0 -73
  702. synth_ai/v0/tracing/retry_queue.py +0 -186
  703. synth_ai/v0/tracing/trackers.py +0 -515
  704. synth_ai/v0/tracing/upload.py +0 -510
  705. synth_ai/v0/tracing/utils.py +0 -9
  706. synth_ai/v0/tracing_v1/__init__.py +0 -16
  707. synth_ai/v0/tracing_v1/abstractions.py +0 -224
  708. synth_ai/v0/tracing_v1/base_client.py +0 -91
  709. synth_ai/v0/tracing_v1/client_manager.py +0 -131
  710. synth_ai/v0/tracing_v1/config.py +0 -140
  711. synth_ai/v0/tracing_v1/context.py +0 -146
  712. synth_ai/v0/tracing_v1/decorators.py +0 -701
  713. synth_ai/v0/tracing_v1/events/__init__.py +0 -0
  714. synth_ai/v0/tracing_v1/events/manage.py +0 -147
  715. synth_ai/v0/tracing_v1/events/scope.py +0 -86
  716. synth_ai/v0/tracing_v1/events/store.py +0 -228
  717. synth_ai/v0/tracing_v1/immediate_client.py +0 -151
  718. synth_ai/v0/tracing_v1/local.py +0 -18
  719. synth_ai/v0/tracing_v1/log_client_base.py +0 -73
  720. synth_ai/v0/tracing_v1/retry_queue.py +0 -186
  721. synth_ai/v0/tracing_v1/trackers.py +0 -515
  722. synth_ai/v0/tracing_v1/upload.py +0 -525
  723. synth_ai/v0/tracing_v1/utils.py +0 -9
  724. synth_ai/zyk/__init__.py +0 -30
  725. synth_ai-0.2.6.dev1.dist-info/METADATA +0 -106
  726. synth_ai-0.2.6.dev1.dist-info/RECORD +0 -416
  727. /synth_ai/{demos → cli/demo_apps}/demo_task_apps/math/__init__.py +0 -0
  728. /synth_ai/{lm/caching → core/apps}/__init__.py +0 -0
  729. /synth_ai/{tracing_v3 → core/tracing_v3}/lm_call_record_abstractions.py +0 -0
  730. /synth_ai/{tracing_v3 → core/tracing_v3}/storage/__init__.py +0 -0
  731. /synth_ai/{tracing_v3 → core/tracing_v3}/storage/exceptions.py +0 -0
  732. /synth_ai/{tracing_v3 → core/tracing_v3}/storage/types.py +0 -0
  733. /synth_ai/{compound/cais.py → py.typed} +0 -0
  734. /synth_ai/{learning → sdk/learning}/core.py +0 -0
  735. /synth_ai/{learning → sdk/learning}/gateway.py +0 -0
  736. {synth_ai-0.2.6.dev1.dist-info → synth_ai-0.4.3.dist-info}/WHEEL +0 -0
  737. {synth_ai-0.2.6.dev1.dist-info → synth_ai-0.4.3.dist-info}/licenses/LICENSE +0 -0
  738. {synth_ai-0.2.6.dev1.dist-info → synth_ai-0.4.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,641 @@
1
+ """Unified GEPA Progress Tracker with configurable display modes.
2
+
3
+ This replaces the various ad-hoc trackers scattered across research/ and cookbooks/.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import json
9
+ import time
10
+ from dataclasses import dataclass, field
11
+ from enum import Enum
12
+ from pathlib import Path
13
+ from typing import Any, TextIO
14
+
15
+ from .dataclasses import (
16
+ BaselineInfo,
17
+ CandidateInfo,
18
+ FrontierUpdate,
19
+ GenerationSummary,
20
+ GEPAProgress,
21
+ )
22
+ from .events import (
23
+ BaselineEvent,
24
+ CandidateEvent,
25
+ CompleteEvent,
26
+ EventCategory,
27
+ EventParser,
28
+ FrontierEvent,
29
+ GenerationEvent,
30
+ ParsedEvent,
31
+ ProgressEvent,
32
+ TerminationEvent,
33
+ UsageEvent,
34
+ )
35
+
36
+
37
+ class DisplayMode(Enum):
38
+ """Display modes for terminal output."""
39
+
40
+ SILENT = "silent" # No output
41
+ MINIMAL = "minimal" # One-liners only
42
+ TQDM = "tqdm" # tqdm progress bars
43
+ RICH = "rich" # Rich console (future)
44
+
45
+
46
+ @dataclass
47
+ class GEPAProgressTracker:
48
+ """Unified GEPA progress tracker with configurable display.
49
+
50
+ Example usage:
51
+
52
+ # Simple terminal streaming (local dev)
53
+ tracker = GEPAProgressTracker(
54
+ display_mode=DisplayMode.TQDM,
55
+ env_name="banking77",
56
+ max_rollouts=500,
57
+ )
58
+
59
+ async for event in job.stream_events():
60
+ tracker.update(event)
61
+
62
+ # Access final state
63
+ print(f"Best: {tracker.best_score:.2%}")
64
+ print(f"Candidates: {len(tracker.candidates)}")
65
+
66
+ # Generate analysis
67
+ tracker.save_all("./results")
68
+ tracker.print_summary()
69
+
70
+ # Headless (no terminal output, just tracking)
71
+ tracker = GEPAProgressTracker(display_mode=DisplayMode.SILENT)
72
+ async for event in job.stream_events():
73
+ tracker.update(event)
74
+ results = tracker.to_analysis_dict()
75
+ """
76
+
77
+ # Configuration
78
+ display_mode: DisplayMode = DisplayMode.TQDM
79
+ env_name: str = "gepa"
80
+ max_rollouts: int = 500
81
+ max_trials: int = 100
82
+ job_id: str | None = None # Optional job ID for log file naming
83
+ log_dir: str | Path | None = None # Directory for log file (defaults to cwd)
84
+ enable_log_file: bool = False # Whether to write events to a log file
85
+
86
+ # Tracking state (initialized in __post_init__)
87
+ candidates: list[CandidateInfo] = field(default_factory=list)
88
+ pareto_history: list[FrontierUpdate] = field(default_factory=list)
89
+ generation_history: list[GenerationSummary] = field(default_factory=list)
90
+ baseline: BaselineInfo | None = None
91
+ progress: GEPAProgress = field(default_factory=GEPAProgress)
92
+ usage_data: dict[str, Any] | None = None
93
+ raw_events: list[dict[str, Any]] = field(default_factory=list)
94
+
95
+ # Internal state
96
+ _start_time: float = field(default_factory=time.time)
97
+ _rollout_pbar: Any = None
98
+ _trial_pbar: Any = None
99
+ _candidate_ids: set[str] = field(default_factory=set)
100
+ _tqdm_available: bool = False
101
+ _tqdm_module: Any = None
102
+ _log_file: TextIO | None = field(default=None, repr=False)
103
+ _log_path: Path | None = field(default=None, repr=False)
104
+
105
+ def __post_init__(self) -> None:
106
+ """Initialize progress bars if using tqdm mode."""
107
+ self._start_time = time.time()
108
+ self._candidate_ids = set()
109
+
110
+ if self.display_mode == DisplayMode.TQDM:
111
+ self._init_tqdm()
112
+
113
+ # Initialize log file if enabled
114
+ if self.enable_log_file:
115
+ self._init_log_file()
116
+
117
+ def _init_log_file(self) -> None:
118
+ """Initialize event log file."""
119
+ log_dir = Path(self.log_dir) if self.log_dir else Path.cwd()
120
+ log_dir.mkdir(parents=True, exist_ok=True)
121
+
122
+ # Generate log filename
123
+ timestamp = time.strftime("%Y%m%d_%H%M%S")
124
+ if self.job_id:
125
+ filename = f"gepa_{self.job_id}_{timestamp}.log"
126
+ else:
127
+ filename = f"gepa_{self.env_name}_{timestamp}.log"
128
+
129
+ self._log_path = log_dir / filename
130
+
131
+ try:
132
+ # Long-lived log file; closed explicitly in close()
133
+ self._log_file = open(self._log_path, "w") # noqa: SIM115
134
+ # Write header
135
+ header = {
136
+ "type": "log_header",
137
+ "timestamp": time.time(),
138
+ "env_name": self.env_name,
139
+ "job_id": self.job_id,
140
+ "max_rollouts": self.max_rollouts,
141
+ "max_trials": self.max_trials,
142
+ }
143
+ self._log_file.write(json.dumps(header) + "\n")
144
+ self._log_file.flush()
145
+
146
+ if self.display_mode != DisplayMode.SILENT:
147
+ self._print(f"Logging events to: {self._log_path}")
148
+ except Exception as e:
149
+ if self.display_mode != DisplayMode.SILENT:
150
+ self._print(f"Warning: Could not create log file: {e}")
151
+ self._log_file = None
152
+ self._log_path = None
153
+
154
+ def _log_event(self, event: dict[str, Any]) -> None:
155
+ """Write event to log file."""
156
+ if self._log_file is not None:
157
+ try:
158
+ # Add timestamp if not present
159
+ log_entry = {
160
+ "logged_at": time.time(),
161
+ "elapsed_seconds": time.time() - self._start_time,
162
+ **event,
163
+ }
164
+ self._log_file.write(json.dumps(log_entry) + "\n")
165
+ self._log_file.flush()
166
+ except Exception:
167
+ pass # Silently ignore log write errors
168
+
169
+ def set_job_id(self, job_id: str) -> None:
170
+ """Set job ID after tracker creation (useful when job ID isn't known until submission).
171
+
172
+ If logging is enabled and no log file exists yet, this will create one.
173
+ If a log file already exists, this updates the job_id in the header.
174
+ """
175
+ self.job_id = job_id
176
+
177
+ # If logging is enabled but log file wasn't created (no job_id at init), create it now
178
+ if self.enable_log_file and self._log_file is None:
179
+ self._init_log_file()
180
+ elif self._log_file is not None:
181
+ # Write job_id update event
182
+ self._log_event({"type": "job_id_set", "job_id": job_id})
183
+
184
+ def _init_tqdm(self) -> None:
185
+ """Initialize tqdm progress bars."""
186
+ try:
187
+ from tqdm import tqdm
188
+
189
+ self._tqdm_available = True
190
+ self._tqdm_module = tqdm
191
+
192
+ self._print_banner()
193
+
194
+ self._rollout_pbar = tqdm(
195
+ total=self.max_rollouts,
196
+ desc="Rollouts",
197
+ unit="rollout",
198
+ bar_format="{l_bar}{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}]",
199
+ )
200
+ self._trial_pbar = tqdm(
201
+ total=self.max_trials,
202
+ desc="Trials ",
203
+ unit="trial",
204
+ bar_format="{l_bar}{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}]",
205
+ )
206
+ except ImportError:
207
+ self._tqdm_available = False
208
+ if self.display_mode == DisplayMode.TQDM:
209
+ print("Warning: tqdm not available, falling back to minimal output")
210
+ self.display_mode = DisplayMode.MINIMAL
211
+
212
+ def _print_banner(self) -> None:
213
+ """Print startup banner."""
214
+ if self.display_mode == DisplayMode.SILENT:
215
+ return
216
+
217
+ banner = f"""
218
+ {'=' * 60}
219
+ GEPA Optimization Progress - {self.env_name}
220
+ {'=' * 60}"""
221
+ print(banner)
222
+
223
+ def _print(self, msg: str) -> None:
224
+ """Print message respecting display mode."""
225
+ if self.display_mode == DisplayMode.SILENT:
226
+ return
227
+
228
+ if self._tqdm_available and self._tqdm_module:
229
+ self._tqdm_module.write(msg)
230
+ else:
231
+ print(msg, flush=True)
232
+
233
+ def _update_progress_bars(self) -> None:
234
+ """Update tqdm progress bars."""
235
+ if not self._tqdm_available:
236
+ return
237
+
238
+ if self._rollout_pbar:
239
+ self._rollout_pbar.n = min(self.progress.rollouts_completed, self.max_rollouts)
240
+ self._rollout_pbar.refresh()
241
+ if self._trial_pbar:
242
+ self._trial_pbar.n = min(self.progress.candidates_evaluated, self.max_trials)
243
+ self._trial_pbar.refresh()
244
+
245
+ def update(self, event: dict[str, Any]) -> None:
246
+ """Update tracker from raw SSE event.
247
+
248
+ Args:
249
+ event: Raw SSE event dict with 'type' and 'data' keys
250
+ """
251
+ # Store raw event
252
+ self.raw_events.append(event)
253
+
254
+ # Log event to file if enabled
255
+ self._log_event(event)
256
+
257
+ # Parse event
258
+ parsed = EventParser.parse(event)
259
+
260
+ # Update elapsed time
261
+ self.progress.elapsed_seconds = time.time() - self._start_time
262
+
263
+ # Dispatch based on category
264
+ if isinstance(parsed, BaselineEvent):
265
+ self._handle_baseline(parsed)
266
+ elif isinstance(parsed, CandidateEvent):
267
+ self._handle_candidate(parsed)
268
+ elif isinstance(parsed, FrontierEvent):
269
+ self._handle_frontier(parsed)
270
+ elif isinstance(parsed, ProgressEvent):
271
+ self._handle_progress(parsed)
272
+ elif isinstance(parsed, GenerationEvent):
273
+ self._handle_generation(parsed)
274
+ elif isinstance(parsed, CompleteEvent):
275
+ self._handle_complete(parsed)
276
+ elif isinstance(parsed, TerminationEvent):
277
+ self._handle_termination(parsed)
278
+ elif isinstance(parsed, UsageEvent):
279
+ self._handle_usage(parsed)
280
+ elif parsed.category == EventCategory.VALIDATION or parsed.category == EventCategory.UNKNOWN and (
281
+ parsed.data.get("baseline_val_accuracy") is not None or
282
+ parsed.data.get("is_baseline") is True or
283
+ "validation" in parsed.event_type.lower()
284
+ ):
285
+ self._handle_validation(parsed)
286
+ # ✅ ADD: Also handle job.event type events that contain validation data
287
+ # (postgrest_emitter wraps events as job.event, so we need to check data for validation indicators)
288
+ elif parsed.category == EventCategory.UNKNOWN and (
289
+ parsed.data.get("baseline_val_accuracy") is not None or
290
+ parsed.data.get("is_baseline") is True or
291
+ "validation" in parsed.event_type.lower()
292
+ ):
293
+ self._handle_validation(parsed)
294
+
295
+ def _handle_baseline(self, event: BaselineEvent) -> None:
296
+ """Handle baseline evaluation event."""
297
+ self.baseline = BaselineInfo(
298
+ accuracy=event.accuracy,
299
+ instance_scores=event.instance_scores or [],
300
+ prompt=event.prompt,
301
+ )
302
+ self.progress.baseline_score = event.accuracy
303
+
304
+ if self.display_mode != DisplayMode.SILENT:
305
+ acc_str = f"{event.accuracy:.2%}" if event.accuracy else "N/A"
306
+ self._print(f"Baseline: {acc_str}")
307
+
308
+ def _handle_candidate(self, event: CandidateEvent) -> None:
309
+ """Handle candidate evaluation event."""
310
+ # Check if this is a baseline candidate
311
+ is_baseline = event.data.get("is_baseline", False) or event.data.get("parent_id") is None
312
+
313
+ # Extract baseline info from baseline candidate events
314
+ if is_baseline and not self.baseline:
315
+ instance_scores = event.data.get("instance_scores", [])
316
+ seeds_evaluated = event.data.get("seeds_evaluated", [])
317
+ prompt = event.data.get("prompt") or event.data.get("prompt_text") or event.data.get("transformation")
318
+
319
+ self.baseline = BaselineInfo(
320
+ accuracy=event.accuracy,
321
+ instance_scores=instance_scores if isinstance(instance_scores, list) else [],
322
+ seeds_evaluated=seeds_evaluated if isinstance(seeds_evaluated, list) else [],
323
+ prompt=prompt if isinstance(prompt, dict) else None,
324
+ )
325
+ if event.accuracy is not None:
326
+ self.progress.baseline_score = event.accuracy
327
+
328
+ if self.display_mode != DisplayMode.SILENT:
329
+ acc_str = f"{event.accuracy:.2%}" if event.accuracy else "N/A"
330
+ self._print(f"Baseline: {acc_str}")
331
+
332
+ # Avoid duplicates
333
+ if event.candidate_id in self._candidate_ids:
334
+ return
335
+ self._candidate_ids.add(event.candidate_id)
336
+
337
+ # Create candidate info
338
+ candidate = CandidateInfo.from_event_data(event.data, event.candidate_id)
339
+ self.candidates.append(candidate)
340
+
341
+ # Update progress
342
+ self.progress.candidates_evaluated = len(self.candidates)
343
+ if event.accuracy is not None and event.accuracy > self.progress.best_score:
344
+ self.progress.best_score = event.accuracy
345
+
346
+ self._update_progress_bars()
347
+
348
+ def _handle_frontier(self, event: FrontierEvent) -> None:
349
+ """Handle pareto frontier update."""
350
+ # Get timestamp_ms from event data if available
351
+ timestamp_ms = event.data.get("timestamp_ms") if event.data else None
352
+
353
+ update = FrontierUpdate(
354
+ timestamp=time.time() - self._start_time,
355
+ added=event.added or [],
356
+ removed=event.removed or [],
357
+ frontier=event.frontier or [],
358
+ frontier_scores=event.frontier_scores or {},
359
+ frontier_size=event.frontier_size,
360
+ optimistic_score=event.best_score,
361
+ generation=event.data.get("generation") if event.data else None,
362
+ baseline_score=event.data.get("baseline_score") if event.data else None,
363
+ timestamp_ms=timestamp_ms,
364
+ )
365
+ self.pareto_history.append(update)
366
+
367
+ if event.best_score is not None and event.best_score > self.progress.best_score:
368
+ self.progress.best_score = event.best_score
369
+
370
+ if self.display_mode != DisplayMode.SILENT:
371
+ lift = self.progress.lift
372
+ lift_str = f" (lift: {lift:+.2%})" if lift is not None else ""
373
+ self._print(
374
+ f"Frontier [{event.frontier_size}]: best={event.best_score:.2%}{lift_str}"
375
+ )
376
+
377
+ def _handle_progress(self, event: ProgressEvent) -> None:
378
+ """Handle progress update event."""
379
+ if event.rollouts_completed > self.progress.rollouts_completed:
380
+ self.progress.rollouts_completed = event.rollouts_completed
381
+
382
+ if event.rollouts_total:
383
+ self.progress.rollouts_total = event.rollouts_total
384
+
385
+ if event.trials_completed > self.progress.candidates_evaluated:
386
+ self.progress.candidates_evaluated = event.trials_completed
387
+
388
+ if event.best_score is not None and event.best_score > self.progress.best_score:
389
+ self.progress.best_score = event.best_score
390
+
391
+ if event.baseline_score is not None and self.progress.baseline_score is None:
392
+ self.progress.baseline_score = event.baseline_score
393
+
394
+ self._update_progress_bars()
395
+
396
+ def _handle_generation(self, event: GenerationEvent) -> None:
397
+ """Handle generation complete event."""
398
+ summary = GenerationSummary(
399
+ generation=event.generation,
400
+ candidates_proposed=event.candidates_proposed,
401
+ candidates_accepted=event.candidates_accepted,
402
+ best_accuracy=event.best_accuracy,
403
+ timestamp=time.time() - self._start_time,
404
+ )
405
+ self.generation_history.append(summary)
406
+ self.progress.generations_completed = event.generation
407
+
408
+ if self.display_mode != DisplayMode.SILENT:
409
+ self._print(f"Generation {event.generation} complete (best: {event.best_accuracy:.2%})")
410
+
411
+ def _handle_complete(self, event: CompleteEvent) -> None:
412
+ """Handle optimization complete event."""
413
+ self.progress.phase = "complete"
414
+
415
+ if event.best_score is not None:
416
+ self.progress.best_score = event.best_score
417
+
418
+ if event.baseline_score is not None:
419
+ self.progress.baseline_score = event.baseline_score
420
+
421
+ if event.finish_reason:
422
+ self.progress.finish_reason = event.finish_reason
423
+
424
+ if self.display_mode != DisplayMode.SILENT:
425
+ self._print("Optimization complete!")
426
+
427
+ def _handle_termination(self, event: TerminationEvent) -> None:
428
+ """Handle termination event."""
429
+ self.progress.finish_reason = event.reason
430
+
431
+ if self.display_mode != DisplayMode.SILENT:
432
+ self._print(f"Termination triggered: {event.reason}")
433
+
434
+ def _handle_usage(self, event: UsageEvent) -> None:
435
+ """Handle usage/cost event."""
436
+ self.usage_data = event.data
437
+
438
+ if self.display_mode != DisplayMode.SILENT:
439
+ self._print(f"Cost: ${event.total_usd:.4f}")
440
+
441
+ def _handle_validation(self, event: ParsedEvent) -> None:
442
+ """Handle validation scored event."""
443
+ data = event.data
444
+ if data.get("is_baseline", False) or data.get("baseline_val_accuracy") is not None:
445
+ # Baseline validation result
446
+ # Try to get baseline_val_accuracy from top level first, then accuracy
447
+ val_accuracy = data.get("baseline_val_accuracy") or data.get("accuracy")
448
+ if val_accuracy is not None:
449
+ # Store in baseline info if available
450
+ if self.baseline:
451
+ self.baseline.val_accuracy = val_accuracy
452
+ # Also store in progress for backwards compatibility
453
+ if self.progress.baseline_score is None:
454
+ self.progress.baseline_score = val_accuracy
455
+
456
+ # === Public API ===
457
+
458
+ @property
459
+ def best_score(self) -> float:
460
+ """Get best score achieved."""
461
+ return self.progress.best_score
462
+
463
+ @property
464
+ def baseline_score(self) -> float | None:
465
+ """Get baseline score."""
466
+ return self.progress.baseline_score
467
+
468
+ @property
469
+ def current_frontier(self) -> list[str]:
470
+ """Get current pareto frontier candidate IDs."""
471
+ if self.pareto_history:
472
+ return self.pareto_history[-1].frontier
473
+ return []
474
+
475
+ def get_candidate(self, candidate_id: str) -> CandidateInfo | None:
476
+ """Get candidate by ID."""
477
+ for c in self.candidates:
478
+ if c.candidate_id == candidate_id:
479
+ return c
480
+ return None
481
+
482
+ def get_pareto_candidates(self) -> list[CandidateInfo]:
483
+ """Get candidates currently in pareto frontier."""
484
+ frontier_ids = set(self.current_frontier)
485
+ return [c for c in self.candidates if c.candidate_id in frontier_ids]
486
+
487
+ def print_summary(self) -> None:
488
+ """Print final summary to terminal."""
489
+ print()
490
+ print("=" * 60)
491
+ print("RESULTS")
492
+ print("=" * 60)
493
+
494
+ print(f"Status: {self.progress.phase}")
495
+ print()
496
+
497
+ print("Scoring Summary:")
498
+ print(f" Baseline: {self.baseline_score:.2%}" if self.baseline_score else " Baseline: N/A")
499
+ print(f" Best: {self.best_score:.2%}")
500
+ if self.progress.lift is not None:
501
+ print(f" Lift: {self.progress.lift:+.2%}")
502
+ print()
503
+
504
+ print("Optimization Stats:")
505
+ print(f" Rollouts: {self.progress.rollouts_completed}")
506
+ print(f" Candidates: {len(self.candidates)}")
507
+ print(f" Generations: {self.progress.generations_completed}")
508
+ print(f" Frontier: {len(self.current_frontier)}")
509
+ print(f" Finish: {self.progress.finish_reason or 'N/A'}")
510
+ print()
511
+
512
+ print(f"Time: {self.progress.elapsed_seconds:.1f}s ({self.progress.elapsed_seconds/60:.1f} min)")
513
+
514
+ if self.usage_data:
515
+ total = self.usage_data.get("total_usd", 0)
516
+ print(f"Cost: ${total:.4f}")
517
+
518
+ print("=" * 60)
519
+
520
+ def to_analysis_dict(self) -> dict[str, Any]:
521
+ """Export all tracking data for analysis.
522
+
523
+ Includes first-class program structure (stages), seed_info, token_usage, etc.
524
+ """
525
+ candidates_list = []
526
+ for c in self.candidates:
527
+ candidate_dict: dict[str, Any] = {
528
+ "candidate_id": c.candidate_id,
529
+ "accuracy": c.accuracy,
530
+ "val_accuracy": c.val_accuracy,
531
+ "train_accuracy": c.train_accuracy,
532
+ "generation": c.generation,
533
+ "parent_id": c.parent_id,
534
+ "is_pareto": c.candidate_id in self.current_frontier,
535
+ "accepted": c.accepted,
536
+ "instance_scores": c.instance_scores,
537
+ "mutation_type": c.mutation_type,
538
+ "mutation_params": c.mutation_params,
539
+ "prompt_summary": c.prompt_summary or c.get_prompt_summary(),
540
+ "timestamp_ms": c.timestamp_ms,
541
+ }
542
+
543
+ # Add stages (first-class program structure)
544
+ if c.stages:
545
+ candidate_dict["stages"] = {
546
+ stage_id: stage.to_dict()
547
+ for stage_id, stage in c.stages.items()
548
+ }
549
+
550
+ # Add seed_scores
551
+ if c.seed_scores:
552
+ candidate_dict["seed_scores"] = c.seed_scores
553
+
554
+ # Add token_usage
555
+ if c.token_usage:
556
+ candidate_dict["token_usage"] = c.token_usage.to_dict()
557
+
558
+ # Add cost
559
+ if c.cost_usd is not None:
560
+ candidate_dict["cost_usd"] = c.cost_usd
561
+
562
+ candidates_list.append(candidate_dict)
563
+
564
+ return {
565
+ "env_name": self.env_name,
566
+ "progress": {
567
+ "phase": self.progress.phase,
568
+ "rollouts_completed": self.progress.rollouts_completed,
569
+ "candidates_evaluated": self.progress.candidates_evaluated,
570
+ "generations_completed": self.progress.generations_completed,
571
+ "best_score": self.progress.best_score,
572
+ "baseline_score": self.progress.baseline_score,
573
+ "lift": self.progress.lift,
574
+ "elapsed_seconds": self.progress.elapsed_seconds,
575
+ "finish_reason": self.progress.finish_reason,
576
+ },
577
+ "baseline": {
578
+ "accuracy": self.baseline.accuracy if self.baseline else None,
579
+ "val_accuracy": self.baseline.val_accuracy if self.baseline else None,
580
+ "instance_scores": self.baseline.instance_scores if self.baseline else [],
581
+ },
582
+ "candidates": candidates_list,
583
+ "pareto_history": [
584
+ {
585
+ "timestamp": u.timestamp,
586
+ "timestamp_ms": u.timestamp_ms,
587
+ "added": u.added,
588
+ "removed": u.removed,
589
+ "frontier": u.frontier,
590
+ "frontier_size": u.frontier_size,
591
+ "frontier_scores": u.frontier_scores,
592
+ "optimistic_score": u.optimistic_score,
593
+ "baseline_score": u.baseline_score,
594
+ "generation": u.generation,
595
+ }
596
+ for u in self.pareto_history
597
+ ],
598
+ "generation_history": [
599
+ {
600
+ "generation": g.generation,
601
+ "candidates_proposed": g.candidates_proposed,
602
+ "candidates_accepted": g.candidates_accepted,
603
+ "best_accuracy": g.best_accuracy,
604
+ "timestamp": g.timestamp,
605
+ }
606
+ for g in self.generation_history
607
+ ],
608
+ "usage": self.usage_data,
609
+ }
610
+
611
+ @property
612
+ def log_path(self) -> Path | None:
613
+ """Get path to the log file if logging is enabled."""
614
+ return self._log_path
615
+
616
+ def close(self) -> None:
617
+ """Close progress bars, log file, and clean up."""
618
+ if self._rollout_pbar:
619
+ self._rollout_pbar.close()
620
+ if self._trial_pbar:
621
+ self._trial_pbar.close()
622
+
623
+ # Close log file
624
+ if self._log_file is not None:
625
+ try:
626
+ # Write footer with final stats
627
+ footer = {
628
+ "type": "log_footer",
629
+ "timestamp": time.time(),
630
+ "elapsed_seconds": time.time() - self._start_time,
631
+ "total_events": len(self.raw_events),
632
+ "best_score": self.best_score,
633
+ "baseline_score": self.baseline_score,
634
+ "candidates_count": len(self.candidates),
635
+ "phase": self.progress.phase,
636
+ }
637
+ self._log_file.write(json.dumps(footer) + "\n")
638
+ self._log_file.close()
639
+ except Exception:
640
+ pass
641
+ self._log_file = None