synth-ai 0.2.9.dev4__py3-none-any.whl → 0.2.9.dev6__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.

Potentially problematic release.


This version of synth-ai might be problematic. Click here for more details.

Files changed (353) hide show
  1. examples/__init__.py +16 -0
  2. examples/crafter_debug_render.py +23 -17
  3. examples/qwen_coder/README.md +102 -0
  4. examples/qwen_coder/_shared.py +113 -0
  5. examples/qwen_coder/configs/coder_lora_30b.toml +61 -0
  6. examples/qwen_coder/configs/coder_lora_4b.toml +57 -0
  7. examples/qwen_coder/configs/coder_lora_small.toml +58 -0
  8. examples/qwen_coder/generate_dataset.py +98 -0
  9. examples/qwen_coder/infer_ft_smoke.py +64 -0
  10. examples/qwen_coder/infer_prod_proxy.py +73 -0
  11. examples/qwen_coder/infer_via_synth.py +87 -0
  12. examples/qwen_coder/scripts/infer_coder.sh +18 -0
  13. examples/qwen_coder/scripts/train_coder_30b.sh +21 -0
  14. examples/qwen_coder/sft_full_17b.py +103 -0
  15. examples/qwen_coder/sft_lora_30b.py +110 -0
  16. examples/qwen_coder/subset_jsonl.py +38 -0
  17. examples/qwen_coder/validate_jsonl.py +59 -0
  18. examples/rl/configs/eval_base_qwen.toml +1 -1
  19. examples/rl/configs/rl_from_base_qwen17.toml +1 -1
  20. examples/rl/download_dataset.py +26 -10
  21. examples/rl/run_eval.py +53 -52
  22. examples/rl/run_rl_and_save.py +29 -12
  23. examples/rl/task_app/math_single_step.py +180 -41
  24. examples/rl/task_app/math_task_app.py +14 -6
  25. examples/sft/README.md +139 -0
  26. examples/sft/configs/crafter_fft_qwen0p6b.toml +44 -0
  27. examples/sft/configs/crafter_lora_qwen0p6b.toml +45 -0
  28. examples/sft/evaluate.py +117 -0
  29. examples/sft/export_dataset.py +117 -0
  30. examples/sft/generate_traces.py +162 -0
  31. examples/swe/__init__.py +12 -0
  32. examples/swe/task_app/README.md +105 -0
  33. examples/swe/task_app/__init__.py +2 -0
  34. examples/swe/task_app/grpo_swe_mini.py +571 -0
  35. examples/swe/task_app/grpo_swe_mini_task_app.py +136 -0
  36. examples/swe/task_app/hosted/README.md +173 -0
  37. examples/swe/task_app/hosted/__init__.py +5 -0
  38. examples/swe/task_app/hosted/branching.py +143 -0
  39. examples/swe/task_app/hosted/environment_routes.py +1289 -0
  40. examples/swe/task_app/hosted/envs/__init__.py +1 -0
  41. examples/swe/task_app/hosted/envs/crafter/__init__.py +6 -0
  42. examples/swe/task_app/hosted/envs/crafter/app.py +1 -0
  43. examples/swe/task_app/hosted/envs/crafter/environment.py +522 -0
  44. examples/swe/task_app/hosted/envs/crafter/policy.py +478 -0
  45. examples/swe/task_app/hosted/envs/crafter/react_agent.py +108 -0
  46. examples/swe/task_app/hosted/envs/crafter/shared.py +305 -0
  47. examples/swe/task_app/hosted/envs/crafter/tools.py +47 -0
  48. examples/swe/task_app/hosted/envs/mini_swe/__init__.py +8 -0
  49. examples/swe/task_app/hosted/envs/mini_swe/environment.py +1164 -0
  50. examples/swe/task_app/hosted/envs/mini_swe/policy.py +355 -0
  51. examples/swe/task_app/hosted/envs/mini_swe/shared.py +83 -0
  52. examples/swe/task_app/hosted/envs/mini_swe/tools.py +96 -0
  53. examples/swe/task_app/hosted/hosted_app.py +204 -0
  54. examples/swe/task_app/hosted/inference/__init__.py +5 -0
  55. examples/swe/task_app/hosted/inference/openai_client.py +618 -0
  56. examples/swe/task_app/hosted/main.py +100 -0
  57. examples/swe/task_app/hosted/policy_routes.py +1079 -0
  58. examples/swe/task_app/hosted/registry.py +195 -0
  59. examples/swe/task_app/hosted/rollout.py +1869 -0
  60. examples/swe/task_app/hosted/storage/__init__.py +5 -0
  61. examples/swe/task_app/hosted/storage/volume.py +211 -0
  62. examples/swe/task_app/hosted/test_agents.py +161 -0
  63. examples/swe/task_app/hosted/test_service.py +137 -0
  64. examples/swe/task_app/hosted/utils.py +62 -0
  65. examples/vlm/README.md +68 -0
  66. examples/vlm/configs/crafter_vlm_gpt4o.toml +44 -0
  67. examples/vlm/crafter_image_only_agent.py +207 -0
  68. examples/vlm/crafter_openai_vlm_agent.py +277 -0
  69. examples/vlm/filter_image_rows.py +63 -0
  70. examples/vlm/run_crafter_vlm_benchmark.py +316 -0
  71. examples/warming_up_to_rl/analyze_trace_db.py +12 -10
  72. examples/warming_up_to_rl/configs/rl_from_base_qwen4b.toml +11 -1
  73. examples/warming_up_to_rl/export_trace_sft.py +218 -36
  74. examples/warming_up_to_rl/groq_test.py +15 -8
  75. examples/warming_up_to_rl/manage_secrets.py +29 -25
  76. examples/warming_up_to_rl/readme.md +9 -2
  77. examples/warming_up_to_rl/run_eval.py +137 -61
  78. examples/warming_up_to_rl/run_fft_and_save.py +131 -60
  79. examples/warming_up_to_rl/run_local_rollout.py +88 -39
  80. examples/warming_up_to_rl/run_local_rollout_modal.py +114 -28
  81. examples/warming_up_to_rl/run_local_rollout_parallel.py +81 -20
  82. examples/warming_up_to_rl/run_local_rollout_traced.py +126 -23
  83. examples/warming_up_to_rl/run_rl_and_save.py +35 -12
  84. examples/warming_up_to_rl/run_rollout_remote.py +44 -19
  85. examples/warming_up_to_rl/task_app/README.md +6 -2
  86. examples/warming_up_to_rl/task_app/grpo_crafter.py +319 -57
  87. examples/warming_up_to_rl/task_app/grpo_crafter_task_app.py +11 -30
  88. examples/warming_up_to_rl/task_app/synth_envs_hosted/__init__.py +1 -1
  89. examples/warming_up_to_rl/task_app/synth_envs_hosted/branching.py +9 -11
  90. examples/warming_up_to_rl/task_app/synth_envs_hosted/environment_routes.py +137 -182
  91. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/__init__.py +1 -1
  92. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/__init__.py +1 -1
  93. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/app.py +1 -1
  94. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/environment.py +150 -57
  95. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/policy.py +105 -69
  96. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/react_agent.py +19 -7
  97. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/shared.py +45 -42
  98. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/tools.py +1 -1
  99. examples/warming_up_to_rl/task_app/synth_envs_hosted/hosted_app.py +47 -45
  100. examples/warming_up_to_rl/task_app/synth_envs_hosted/inference/__init__.py +1 -1
  101. examples/warming_up_to_rl/task_app/synth_envs_hosted/inference/openai_client.py +198 -92
  102. examples/warming_up_to_rl/task_app/synth_envs_hosted/main.py +0 -2
  103. examples/warming_up_to_rl/task_app/synth_envs_hosted/policy_routes.py +361 -263
  104. examples/warming_up_to_rl/task_app/synth_envs_hosted/registry.py +21 -23
  105. examples/warming_up_to_rl/task_app/synth_envs_hosted/rollout.py +394 -274
  106. examples/warming_up_to_rl/task_app/synth_envs_hosted/storage/__init__.py +1 -1
  107. examples/warming_up_to_rl/task_app/synth_envs_hosted/storage/volume.py +56 -62
  108. examples/warming_up_to_rl/task_app/synth_envs_hosted/test_agents.py +1 -0
  109. examples/warming_up_to_rl/task_app/synth_envs_hosted/test_service.py +6 -15
  110. examples/warming_up_to_rl/task_app/synth_envs_hosted/utils.py +4 -3
  111. synth/__init__.py +14 -0
  112. synth_ai/__init__.py +20 -4
  113. synth_ai/api/models/supported.py +376 -0
  114. synth_ai/api/train/builders.py +157 -26
  115. synth_ai/api/train/cli.py +213 -57
  116. synth_ai/api/train/config_finder.py +65 -5
  117. synth_ai/api/train/env_resolver.py +33 -15
  118. synth_ai/api/train/pollers.py +13 -4
  119. synth_ai/api/train/supported_algos.py +139 -0
  120. synth_ai/api/train/task_app.py +5 -3
  121. synth_ai/api/train/utils.py +33 -48
  122. synth_ai/cli/__init__.py +19 -4
  123. synth_ai/cli/_modal_wrapper.py +28 -0
  124. synth_ai/cli/_typer_patch.py +49 -0
  125. synth_ai/cli/balance.py +2 -3
  126. synth_ai/cli/calc.py +1 -1
  127. synth_ai/cli/demo.py +21 -6
  128. synth_ai/cli/recent.py +2 -2
  129. synth_ai/cli/rl_demo.py +77 -17
  130. synth_ai/cli/root.py +116 -39
  131. synth_ai/cli/status.py +2 -2
  132. synth_ai/cli/task_apps.py +1709 -243
  133. synth_ai/cli/traces.py +7 -4
  134. synth_ai/cli/turso.py +73 -0
  135. synth_ai/cli/watch.py +12 -18
  136. synth_ai/core/experiment.py +0 -2
  137. synth_ai/demo_registry.py +68 -31
  138. synth_ai/demos/core/cli.py +516 -194
  139. synth_ai/demos/demo_task_apps/__init__.py +3 -3
  140. synth_ai/demos/demo_task_apps/core.py +64 -28
  141. synth_ai/demos/demo_task_apps/crafter/configs/crafter_fft_4b.toml +2 -3
  142. synth_ai/demos/demo_task_apps/crafter/grpo_crafter_task_app.py +37 -30
  143. synth_ai/demos/demo_task_apps/math/_common.py +1 -2
  144. synth_ai/demos/demo_task_apps/math/app.py +2 -1
  145. synth_ai/demos/demo_task_apps/math/deploy_modal.py +3 -6
  146. synth_ai/demos/demo_task_apps/math/modal_task_app.py +183 -82
  147. synth_ai/demos/demo_task_apps/math/task_app_entry.py +0 -2
  148. synth_ai/environments/examples/bandit/engine.py +12 -4
  149. synth_ai/environments/examples/bandit/taskset.py +4 -4
  150. synth_ai/environments/examples/crafter_classic/environment.py +76 -1
  151. synth_ai/environments/reproducibility/tree.py +5 -6
  152. synth_ai/environments/service/app.py +11 -12
  153. synth_ai/environments/service/core_routes.py +10 -9
  154. synth_ai/environments/stateful/engine.py +1 -1
  155. synth_ai/environments/tasks/core.py +1 -0
  156. synth_ai/environments/tasks/filters.py +5 -6
  157. synth_ai/environments/tasks/utils.py +4 -5
  158. synth_ai/evals/base.py +0 -2
  159. synth_ai/handshake.py +11 -9
  160. synth_ai/http.py +1 -1
  161. synth_ai/http_client.py +43 -11
  162. synth_ai/inference/__init__.py +0 -2
  163. synth_ai/inference/client.py +20 -6
  164. synth_ai/jobs/client.py +103 -78
  165. synth_ai/learning/__init__.py +41 -6
  166. synth_ai/learning/algorithms.py +14 -0
  167. synth_ai/learning/client.py +121 -29
  168. synth_ai/learning/config.py +2 -40
  169. synth_ai/learning/constants.py +0 -2
  170. synth_ai/learning/ft_client.py +4 -56
  171. synth_ai/learning/health.py +13 -7
  172. synth_ai/learning/jobs.py +43 -47
  173. synth_ai/{rl → learning/rl}/__init__.py +14 -5
  174. synth_ai/learning/rl/client.py +267 -0
  175. synth_ai/learning/rl/config.py +31 -0
  176. synth_ai/{rl → learning/rl}/contracts.py +5 -10
  177. synth_ai/{rl → learning/rl}/env_keys.py +45 -16
  178. synth_ai/learning/rl/secrets.py +13 -0
  179. synth_ai/learning/rl_client.py +2 -253
  180. synth_ai/learning/sft/__init__.py +29 -0
  181. synth_ai/learning/sft/client.py +68 -0
  182. synth_ai/learning/sft/config.py +270 -0
  183. synth_ai/learning/sft/data.py +295 -0
  184. synth_ai/learning/sse.py +25 -26
  185. synth_ai/learning/validators.py +25 -24
  186. synth_ai/lm/__init__.py +21 -47
  187. synth_ai/task/__init__.py +26 -27
  188. synth_ai/task/apps/__init__.py +18 -19
  189. synth_ai/task/auth.py +35 -23
  190. synth_ai/task/client.py +15 -13
  191. synth_ai/task/contracts.py +37 -35
  192. synth_ai/task/datasets.py +9 -6
  193. synth_ai/task/errors.py +11 -10
  194. synth_ai/task/health.py +17 -11
  195. synth_ai/task/json.py +58 -24
  196. synth_ai/task/proxy.py +15 -14
  197. synth_ai/task/rubrics.py +22 -15
  198. synth_ai/task/server.py +43 -17
  199. synth_ai/task/tracing_utils.py +12 -7
  200. synth_ai/task/validators.py +0 -1
  201. synth_ai/task/vendors.py +5 -7
  202. synth_ai/tracing_v3/__init__.py +2 -0
  203. synth_ai/tracing_v3/abstractions.py +21 -4
  204. synth_ai/tracing_v3/db_config.py +26 -1
  205. synth_ai/tracing_v3/decorators.py +18 -15
  206. synth_ai/tracing_v3/examples/basic_usage.py +3 -2
  207. synth_ai/tracing_v3/hooks.py +6 -4
  208. synth_ai/tracing_v3/llm_call_record_helpers.py +6 -6
  209. synth_ai/tracing_v3/replica_sync.py +1 -0
  210. synth_ai/tracing_v3/session_tracer.py +63 -16
  211. synth_ai/tracing_v3/storage/base.py +89 -1
  212. synth_ai/tracing_v3/storage/config.py +21 -8
  213. synth_ai/tracing_v3/storage/factory.py +10 -8
  214. synth_ai/tracing_v3/storage/utils.py +4 -2
  215. synth_ai/tracing_v3/turso/daemon.py +7 -2
  216. synth_ai/tracing_v3/turso/models.py +5 -2
  217. synth_ai/tracing_v3/turso/native_manager.py +1173 -0
  218. synth_ai/tracing_v3/utils.py +4 -3
  219. synth_ai/v0/api/__init__.py +8 -0
  220. synth_ai/v0/api/models/__init__.py +8 -0
  221. synth_ai/v0/api/models/supported.py +8 -0
  222. synth_ai/v0/config/__init__.py +15 -0
  223. synth_ai/v0/config/base_url.py +12 -0
  224. synth_ai/v0/lm/__init__.py +51 -0
  225. synth_ai/{lm → v0/lm}/caching/ephemeral.py +3 -5
  226. synth_ai/{lm → v0/lm}/caching/handler.py +4 -4
  227. synth_ai/{lm → v0/lm}/caching/initialize.py +1 -1
  228. synth_ai/{lm → v0/lm}/caching/persistent.py +1 -1
  229. synth_ai/{lm → v0/lm}/config.py +6 -1
  230. synth_ai/{lm → v0/lm}/core/all.py +9 -9
  231. synth_ai/{lm → v0/lm}/core/exceptions.py +0 -2
  232. synth_ai/{lm → v0/lm}/core/main.py +19 -7
  233. synth_ai/{lm → v0/lm}/core/main_v3.py +10 -10
  234. synth_ai/{lm → v0/lm}/core/synth_models.py +2 -15
  235. synth_ai/{lm → v0/lm}/core/vendor_clients.py +6 -4
  236. synth_ai/{lm → v0/lm}/overrides.py +4 -4
  237. synth_ai/{lm → v0/lm}/provider_support/anthropic.py +4 -4
  238. synth_ai/{lm → v0/lm}/provider_support/openai.py +5 -5
  239. synth_ai/{lm → v0/lm}/structured_outputs/handler.py +5 -5
  240. synth_ai/{lm → v0/lm}/structured_outputs/rehabilitate.py +1 -1
  241. synth_ai/{lm → v0/lm}/vendors/core/anthropic_api.py +16 -16
  242. synth_ai/{lm → v0/lm}/vendors/core/gemini_api.py +5 -5
  243. synth_ai/{lm → v0/lm}/vendors/core/mistral_api.py +5 -5
  244. synth_ai/{lm → v0/lm}/vendors/core/openai_api.py +12 -10
  245. synth_ai/{lm → v0/lm}/vendors/openai_standard.py +11 -9
  246. synth_ai/{lm → v0/lm}/vendors/openai_standard_responses.py +8 -5
  247. synth_ai/{lm → v0/lm}/vendors/supported/custom_endpoint.py +4 -6
  248. synth_ai/{lm → v0/lm}/vendors/supported/deepseek.py +2 -2
  249. synth_ai/{lm → v0/lm}/vendors/supported/grok.py +2 -2
  250. synth_ai/{lm → v0/lm}/vendors/supported/groq.py +1 -1
  251. synth_ai/{lm → v0/lm}/vendors/supported/ollama.py +1 -1
  252. synth_ai/{lm → v0/lm}/vendors/supported/openrouter.py +3 -3
  253. synth_ai/{lm → v0/lm}/vendors/supported/together.py +1 -1
  254. synth_ai/{lm → v0/lm}/vendors/synth_client.py +38 -11
  255. synth_ai/v0/tracing/upload.py +32 -135
  256. synth_ai/v0/tracing_v3/__init__.py +10 -0
  257. synth_ai/v0/tracing_v3/abstractions.py +3 -0
  258. synth_ai/v0/tracing_v3/decorators.py +3 -0
  259. synth_ai/v0/tracing_v3/llm_call_record_helpers.py +3 -0
  260. synth_ai/v0/tracing_v3/session_tracer.py +3 -0
  261. synth_ai-0.2.9.dev6.dist-info/METADATA +191 -0
  262. {synth_ai-0.2.9.dev4.dist-info → synth_ai-0.2.9.dev6.dist-info}/RECORD +291 -264
  263. {synth_ai-0.2.9.dev4.dist-info → synth_ai-0.2.9.dev6.dist-info}/top_level.txt +1 -0
  264. examples/common_old/backend.py +0 -21
  265. examples/evals_old/README.md +0 -98
  266. examples/evals_old/__init__.py +0 -6
  267. examples/evals_old/compare_models.py +0 -1037
  268. examples/evals_old/example_log.md +0 -145
  269. examples/evals_old/run_demo.sh +0 -126
  270. examples/evals_old/trace_analysis.py +0 -270
  271. examples/finetuning_old/_backup_synth_qwen/config.toml +0 -29
  272. examples/finetuning_old/_backup_synth_qwen/example_log.md +0 -324
  273. examples/finetuning_old/_backup_synth_qwen/filter_traces.py +0 -60
  274. examples/finetuning_old/_backup_synth_qwen/filter_traces_achievements.py +0 -239
  275. examples/finetuning_old/_backup_synth_qwen/purge_v3_traces.py +0 -109
  276. examples/finetuning_old/_backup_synth_qwen/react_agent_lm.py +0 -1924
  277. examples/finetuning_old/_backup_synth_qwen/readme.md +0 -49
  278. examples/finetuning_old/_backup_synth_qwen/run_crafter_qwen4b.py +0 -114
  279. examples/finetuning_old/_backup_synth_qwen/run_demo.sh +0 -195
  280. examples/finetuning_old/_backup_synth_qwen/sft_kickoff.py +0 -118
  281. examples/finetuning_old/synth_qwen_v1/README.md +0 -68
  282. examples/finetuning_old/synth_qwen_v1/filter_traces.py +0 -60
  283. examples/finetuning_old/synth_qwen_v1/filter_traces_achievements.py +0 -239
  284. examples/finetuning_old/synth_qwen_v1/finetune.py +0 -46
  285. examples/finetuning_old/synth_qwen_v1/hello_ft_model.py +0 -71
  286. examples/finetuning_old/synth_qwen_v1/infer.py +0 -37
  287. examples/finetuning_old/synth_qwen_v1/poll.py +0 -44
  288. examples/finetuning_old/synth_qwen_v1/prepare_data.py +0 -35
  289. examples/finetuning_old/synth_qwen_v1/purge_v3_traces.py +0 -109
  290. examples/finetuning_old/synth_qwen_v1/react_agent_lm.py +0 -1932
  291. examples/finetuning_old/synth_qwen_v1/run_crafter_sft_job.py +0 -207
  292. examples/finetuning_old/synth_qwen_v1/run_ft_job.py +0 -232
  293. examples/finetuning_old/synth_qwen_v1/upload_data.py +0 -34
  294. examples/finetuning_old/synth_qwen_v1/util.py +0 -147
  295. examples/rl_old/task_app.py +0 -962
  296. examples/warming_up_to_rl/old/event_rewards.md +0 -234
  297. examples/warming_up_to_rl/old/notes.md +0 -73
  298. examples/warming_up_to_rl/task_app/synth_envs_hosted/test_stepwise_rewards.py +0 -58
  299. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/filter_traces_sft_turso.py +0 -738
  300. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/filter_traces_sft_turso.py +0 -580
  301. synth_ai/environments/examples/sokoban/units/astar_common.py +0 -95
  302. synth_ai/experimental/synth_oss.py +0 -446
  303. synth_ai/install_sqld.sh +0 -40
  304. synth_ai/learning/filtering.py +0 -0
  305. synth_ai/learning/offline/dpo.py +0 -0
  306. synth_ai/learning/offline/providers.py +0 -7
  307. synth_ai/learning/offline/sft.py +0 -0
  308. synth_ai/learning/offline/shared.py +0 -0
  309. synth_ai/learning/online/grpo.py +0 -0
  310. synth_ai/learning/online/irft.py +0 -0
  311. synth_ai/learning/prompts/banking77_injection_eval.py +0 -168
  312. synth_ai/learning/prompts/gepa.py +0 -0
  313. synth_ai/learning/prompts/hello_world_in_context_injection_ex.py +0 -213
  314. synth_ai/learning/prompts/mipro.py +0 -289
  315. synth_ai/learning/prompts/random_search.py +0 -246
  316. synth_ai/learning/prompts/run_mipro_banking77.py +0 -172
  317. synth_ai/learning/prompts/run_random_search_banking77.py +0 -324
  318. synth_ai/rl/secrets.py +0 -19
  319. synth_ai/scripts/verify_rewards.py +0 -100
  320. synth_ai/tracing/__init__.py +0 -30
  321. synth_ai/tracing_v1/__init__.py +0 -33
  322. synth_ai/tracing_v3/turso/__init__.py +0 -25
  323. synth_ai/tracing_v3/turso/manager.py +0 -774
  324. synth_ai/zyk/__init__.py +0 -30
  325. synth_ai-0.2.9.dev4.dist-info/METADATA +0 -131
  326. /synth_ai/{lm → v0/lm}/caching/__init__.py +0 -0
  327. /synth_ai/{lm → v0/lm}/caching/constants.py +0 -0
  328. /synth_ai/{lm → v0/lm}/caching/dbs.py +0 -0
  329. /synth_ai/{lm → v0/lm}/constants.py +0 -0
  330. /synth_ai/{lm → v0/lm}/core/__init__.py +0 -0
  331. /synth_ai/{lm → v0/lm}/cost/__init__.py +0 -0
  332. /synth_ai/{lm → v0/lm}/cost/monitor.py +0 -0
  333. /synth_ai/{lm → v0/lm}/cost/statefulness.py +0 -0
  334. /synth_ai/{lm → v0/lm}/injection.py +0 -0
  335. /synth_ai/{lm → v0/lm}/provider_support/__init__.py +0 -0
  336. /synth_ai/{lm → v0/lm}/provider_support/suppress_logging.py +0 -0
  337. /synth_ai/{lm → v0/lm}/structured_outputs/__init__.py +0 -0
  338. /synth_ai/{lm → v0/lm}/structured_outputs/inject.py +0 -0
  339. /synth_ai/{lm → v0/lm}/tools/__init__.py +0 -0
  340. /synth_ai/{lm → v0/lm}/tools/base.py +0 -0
  341. /synth_ai/{lm → v0/lm}/unified_interface.py +0 -0
  342. /synth_ai/{lm → v0/lm}/vendors/__init__.py +0 -0
  343. /synth_ai/{lm → v0/lm}/vendors/base.py +0 -0
  344. /synth_ai/{lm → v0/lm}/vendors/core/__init__.py +0 -0
  345. /synth_ai/{lm → v0/lm}/vendors/core/synth_dev_api.py +0 -0
  346. /synth_ai/{lm → v0/lm}/vendors/local/__init__.py +0 -0
  347. /synth_ai/{lm → v0/lm}/vendors/local/ollama.py +0 -0
  348. /synth_ai/{lm → v0/lm}/vendors/retries.py +0 -0
  349. /synth_ai/{lm → v0/lm}/vendors/supported/__init__.py +0 -0
  350. /synth_ai/{lm → v0/lm}/warmup.py +0 -0
  351. {synth_ai-0.2.9.dev4.dist-info → synth_ai-0.2.9.dev6.dist-info}/WHEEL +0 -0
  352. {synth_ai-0.2.9.dev4.dist-info → synth_ai-0.2.9.dev6.dist-info}/entry_points.txt +0 -0
  353. {synth_ai-0.2.9.dev4.dist-info → synth_ai-0.2.9.dev6.dist-info}/licenses/LICENSE +0 -0
@@ -6,12 +6,13 @@ import json
6
6
  import os
7
7
  import sys
8
8
  import time
9
+ import tomllib
9
10
  from pathlib import Path
10
- from typing import Any, Dict, Tuple, List
11
+ from typing import Any
11
12
 
12
- import tomllib
13
- import re
14
13
  import requests
14
+ from dotenv import load_dotenv
15
+ from synth_ai.config.base_url import PROD_BASE_URL_DEFAULT
15
16
 
16
17
 
17
18
  def mask(val: str) -> str:
@@ -20,7 +21,9 @@ def mask(val: str) -> str:
20
21
  return f"{val[:6]}…{val[-4:]}" if len(val) >= 10 else "****"
21
22
 
22
23
 
23
- def post_multipart(base: str, api_key: str, path: str, file_field: str, filepath: Path) -> Dict[str, Any]:
24
+ def post_multipart(
25
+ base: str, api_key: str, path: str, file_field: str, filepath: Path
26
+ ) -> dict[str, Any]:
24
27
  """Upload a file, trying backend-specific endpoints with fallbacks.
25
28
 
26
29
  Priority:
@@ -33,9 +36,9 @@ def post_multipart(base: str, api_key: str, path: str, file_field: str, filepath
33
36
 
34
37
  endpoints = [
35
38
  f"{base.rstrip('/')}/{path.lstrip('/')}", # e.g., /learning/files
36
- f"{base.rstrip('/')}/files", # OpenAI-style
39
+ f"{base.rstrip('/')}/files", # OpenAI-style
37
40
  ]
38
- last_err: Dict[str, Any] | None = None
41
+ last_err: dict[str, Any] | None = None
39
42
  for ep in endpoints:
40
43
  try:
41
44
  r = requests.post(ep, headers=headers, files=files, data=data, timeout=300)
@@ -67,7 +70,7 @@ def post_multipart(base: str, api_key: str, path: str, file_field: str, filepath
67
70
  return last_err or {"error": True, "detail": "upload_failed_all_endpoints"}
68
71
 
69
72
 
70
- def post_json(base: str, api_key: str, path: str, body: Dict[str, Any]) -> Dict[str, Any]:
73
+ def post_json(base: str, api_key: str, path: str, body: dict[str, Any]) -> dict[str, Any]:
71
74
  url = f"{base.rstrip('/')}/{path.lstrip('/')}"
72
75
  headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
73
76
  r = requests.post(url, headers=headers, data=json.dumps(body), timeout=120)
@@ -77,7 +80,7 @@ def post_json(base: str, api_key: str, path: str, body: Dict[str, Any]) -> Dict[
77
80
  return {"status": r.status_code, "text": r.text[:400]}
78
81
 
79
82
 
80
- def get_json(base: str, api_key: str, path: str) -> Dict[str, Any]:
83
+ def get_json(base: str, api_key: str, path: str) -> dict[str, Any]:
81
84
  url = f"{base.rstrip('/')}/{path.lstrip('/')}"
82
85
  headers = {"Authorization": f"Bearer {api_key}"}
83
86
  r = requests.get(url, headers=headers, timeout=30)
@@ -87,17 +90,94 @@ def get_json(base: str, api_key: str, path: str) -> Dict[str, Any]:
87
90
  return {"status": r.status_code, "text": r.text[:400]}
88
91
 
89
92
 
93
+ def _find_fft_configs() -> list[Path]:
94
+ """Find FFT TOML configs in standard locations."""
95
+ candidates: list[Path] = []
96
+
97
+ # Check current directory configs/
98
+ cwd = Path.cwd()
99
+ configs_dir = cwd / "configs"
100
+ if configs_dir.is_dir():
101
+ for f in configs_dir.glob("*.toml"):
102
+ # Look for FFT configs (check if they have [algorithm] method = "supervised_finetune")
103
+ try:
104
+ content = f.read_text()
105
+ if "supervised_finetune" in content or "fft" in content.lower():
106
+ candidates.append(f)
107
+ except Exception:
108
+ pass
109
+
110
+ # Also check for any .toml files in current directory
111
+ for f in cwd.glob("*.toml"):
112
+ if f not in candidates:
113
+ try:
114
+ content = f.read_text()
115
+ if "supervised_finetune" in content or "fft" in content.lower():
116
+ candidates.append(f)
117
+ except Exception:
118
+ pass
119
+
120
+ return sorted(candidates)
121
+
122
+
90
123
  def main() -> None:
124
+ # Load .env file from current directory first if it exists
125
+ default_env = Path.cwd() / ".env"
126
+ if default_env.exists():
127
+ load_dotenv(default_env, override=False)
128
+
91
129
  parser = argparse.ArgumentParser(description="Submit FFT job and save resulting model id")
92
- parser.add_argument("--backend", default=os.getenv("BACKEND_BASE_URL", "http://localhost:8000/api"))
93
- parser.add_argument("--toml", required=True, help="Path to FFT TOML config")
130
+ parser.add_argument(
131
+ "--backend", default=os.getenv("BACKEND_BASE_URL", f"{PROD_BASE_URL_DEFAULT}/api")
132
+ )
133
+ parser.add_argument("--toml", required=False, help="Path to FFT TOML config")
94
134
  parser.add_argument("--data", default="", help="Override dataset JSONL path")
95
135
  parser.add_argument("--poll-seconds", type=int, default=1800)
96
- parser.add_argument("--env-file", default="", help="Optional path to .env file with SYNTH_API_KEY")
136
+ parser.add_argument(
137
+ "--env-file", default="", help="Optional path to .env file with SYNTH_API_KEY"
138
+ )
97
139
  args = parser.parse_args()
98
140
 
99
- config_path = Path(args.toml).expanduser().resolve()
100
- if not config_path.exists():
141
+ # Also load from explicit --env-file if provided
142
+ if args.env_file:
143
+ env_path = Path(args.env_file).expanduser()
144
+ if not env_path.exists():
145
+ print(f"[WARN] Env file not found: {env_path}")
146
+ else:
147
+ load_dotenv(env_path, override=False)
148
+
149
+ # Auto-discover TOML config if not specified
150
+ config_path: Path | None = None
151
+ if args.toml:
152
+ config_path = Path(args.toml).expanduser().resolve()
153
+ else:
154
+ configs = _find_fft_configs()
155
+ if not configs:
156
+ print(
157
+ "No FFT config files found. Please specify --toml or create a config in configs/",
158
+ file=sys.stderr,
159
+ )
160
+ sys.exit(2)
161
+ elif len(configs) == 1:
162
+ config_path = configs[0]
163
+ print(f"Using FFT config: {config_path}")
164
+ else:
165
+ print("\nFound multiple FFT configs:")
166
+ for idx, cfg in enumerate(configs, 1):
167
+ print(f" [{idx}] {cfg}")
168
+ choice = input(f"Select config [1-{len(configs)}]: ").strip()
169
+ try:
170
+ selected_idx = int(choice) - 1
171
+ if 0 <= selected_idx < len(configs):
172
+ config_path = configs[selected_idx]
173
+ else:
174
+ print("Invalid selection", file=sys.stderr)
175
+ sys.exit(2)
176
+ except ValueError:
177
+ print("Invalid input", file=sys.stderr)
178
+ sys.exit(2)
179
+
180
+ if not config_path or not config_path.exists():
101
181
  print(f"Config not found: {config_path}", file=sys.stderr)
102
182
  sys.exit(2)
103
183
  with config_path.open("rb") as fh:
@@ -107,7 +187,9 @@ def main() -> None:
107
187
  compute_cfg = cfg.get("compute", {}) if isinstance(cfg.get("compute"), dict) else {}
108
188
  data_cfg_full = cfg.get("data", {}) if isinstance(cfg.get("data"), dict) else {}
109
189
  topo_cfg = (data_cfg_full or {}).get("topology", {}) if isinstance(data_cfg_full, dict) else {}
110
- validation_local_path = (data_cfg_full or {}).get("validation_path") if isinstance(data_cfg_full, dict) else None
190
+ validation_local_path = (
191
+ (data_cfg_full or {}).get("validation_path") if isinstance(data_cfg_full, dict) else None
192
+ )
111
193
  train_cfg = cfg.get("training", {}) if isinstance(cfg.get("training"), dict) else {}
112
194
  hp_cfg = cfg.get("hyperparameters", {}) if isinstance(cfg.get("hyperparameters"), dict) else {}
113
195
 
@@ -119,7 +201,10 @@ def main() -> None:
119
201
  if isinstance(data_path, str) and data_path.strip():
120
202
  p = Path(data_path).expanduser()
121
203
  if not p.is_absolute():
122
- p = (config_path.parent / p).resolve()
204
+ # Try relative to cwd first, then relative to config directory
205
+ cwd_relative = Path.cwd() / p
206
+ config_relative = config_path.parent / p
207
+ p = cwd_relative.resolve() if cwd_relative.exists() else config_relative.resolve()
123
208
  data_file = p
124
209
  if data_file is None:
125
210
  print("Missing dataset path in --data or [job].data", file=sys.stderr)
@@ -129,38 +214,11 @@ def main() -> None:
129
214
  sys.exit(2)
130
215
 
131
216
  synth_key = (os.getenv("SYNTH_API_KEY") or "").strip()
132
- # Fallback: try to load from .env if not present in environment
133
217
  if not synth_key:
134
- candidate_env: Path | None = None
135
- if isinstance(args.env_file, str) and args.env_file.strip():
136
- candidate_env = Path(args.env_file).expanduser().resolve()
137
- else:
138
- # Prefer .env next to the TOML config
139
- candidate_env = (config_path.parent / ".env").resolve()
140
- if candidate_env and candidate_env.exists():
141
- try:
142
- env_text = candidate_env.read_text(encoding="utf-8", errors="ignore")
143
- # Match lines like: SYNTH_API_KEY=..., or export SYNTH_API_KEY=...
144
- key_val: str | None = None
145
- for line in env_text.splitlines():
146
- m = re.match(r"^\s*(?:export\s+)?SYNTH_API_KEY\s*=\s*(.*)$", line)
147
- if m:
148
- raw = m.group(1).strip()
149
- # Trim surrounding quotes if present
150
- if (raw.startswith('"') and raw.endswith('"')) or (raw.startswith("'") and raw.endswith("'")):
151
- raw = raw[1:-1]
152
- key_val = raw.strip()
153
- break
154
- if key_val:
155
- synth_key = key_val
156
- os.environ["SYNTH_API_KEY"] = synth_key
157
- print(f"[INFO] Loaded SYNTH_API_KEY from {candidate_env}")
158
- except Exception as _e:
159
- # Ignore and fall through to error below
160
- pass
161
- if not synth_key:
162
- print("Missing SYNTH_API_KEY (set in env or provide --env-file pointing to .env)", file=sys.stderr)
163
- sys.exit(2)
218
+ synth_key = input("Please enter your Synth API key:\n> ").strip()
219
+ if not synth_key:
220
+ print("Synth API key is required", file=sys.stderr)
221
+ sys.exit(2)
164
222
 
165
223
  backend = args.backend.rstrip("/")
166
224
  print(f"[INFO] Using backend={backend} key_fp={mask(synth_key)} data={data_file}")
@@ -180,7 +238,10 @@ def main() -> None:
180
238
  err_status = (upf or {}).get("status")
181
239
  err_body = (upf or {}).get("body") or (upf or {}).get("text")
182
240
  err_ep = (upf or {}).get("endpoint")
183
- print(f"Upload failed (status={err_status} endpoint={err_ep}) body={str(err_body)[:200]}", file=sys.stderr)
241
+ print(
242
+ f"Upload failed (status={err_status} endpoint={err_ep}) body={str(err_body)[:200]}",
243
+ file=sys.stderr,
244
+ )
184
245
  sys.exit(4)
185
246
 
186
247
  # Optionally upload validation file
@@ -203,10 +264,12 @@ def main() -> None:
203
264
  err_status = (upv or {}).get("status")
204
265
  err_body = (upv or {}).get("body") or (upv or {}).get("text")
205
266
  err_ep = (upv or {}).get("endpoint")
206
- print(f"[WARN] Validation upload failed (status={err_status} endpoint={err_ep}) body={str(err_body)[:180]} — continuing without validation")
267
+ print(
268
+ f"[WARN] Validation upload failed (status={err_status} endpoint={err_ep}) body={str(err_body)[:180]} — continuing without validation"
269
+ )
207
270
 
208
271
  # 2) Build job payload
209
- hp_block: Dict[str, Any] = {
272
+ hp_block: dict[str, Any] = {
210
273
  "n_epochs": int(hp_cfg.get("n_epochs") or 1),
211
274
  }
212
275
  # Optional extras if present
@@ -227,7 +290,7 @@ def main() -> None:
227
290
  if parallel:
228
291
  hp_block["parallelism"] = parallel
229
292
 
230
- compute_block: Dict[str, Any] = {}
293
+ compute_block: dict[str, Any] = {}
231
294
  for k in ("gpu_type", "gpu_count", "nodes"):
232
295
  if k in compute_cfg:
233
296
  compute_block[k] = compute_cfg[k]
@@ -238,18 +301,24 @@ def main() -> None:
238
301
  "training": {k: v for k, v in train_cfg.items() if k in ("mode", "use_qlora")},
239
302
  }
240
303
  # If TOML includes a [training.validation] block, forward relevant knobs into hyperparameters
241
- validation_cfg = train_cfg.get("validation") if isinstance(train_cfg.get("validation"), dict) else None
304
+ validation_cfg = (
305
+ train_cfg.get("validation") if isinstance(train_cfg.get("validation"), dict) else None
306
+ )
242
307
  if isinstance(validation_cfg, dict):
243
308
  # Enable evaluation and map keys as-is; backend trainer maps metric_for_best_model 'val.loss'→'eval_loss'
244
- hp_block.update({
245
- "evaluation_strategy": validation_cfg.get("evaluation_strategy", "steps"),
246
- "eval_steps": int(validation_cfg.get("eval_steps", 0) or 0),
247
- "save_best_model_at_end": bool(validation_cfg.get("save_best_model_at_end", True)),
248
- "metric_for_best_model": validation_cfg.get("metric_for_best_model", "val.loss"),
249
- "greater_is_better": bool(validation_cfg.get("greater_is_better", False)),
250
- })
309
+ hp_block.update(
310
+ {
311
+ "evaluation_strategy": validation_cfg.get("evaluation_strategy", "steps"),
312
+ "eval_steps": int(validation_cfg.get("eval_steps", 0) or 0),
313
+ "save_best_model_at_end": bool(validation_cfg.get("save_best_model_at_end", True)),
314
+ "metric_for_best_model": validation_cfg.get("metric_for_best_model", "val.loss"),
315
+ "greater_is_better": bool(validation_cfg.get("greater_is_better", False)),
316
+ }
317
+ )
251
318
  # Also surface validation enable flag into effective_config for visibility (optional)
252
- effective.setdefault("training", {})["validation"] = {"enabled": bool(validation_cfg.get("enabled", True))}
319
+ effective.setdefault("training", {})["validation"] = {
320
+ "enabled": bool(validation_cfg.get("enabled", True))
321
+ }
253
322
 
254
323
  body = {
255
324
  "model": model,
@@ -289,7 +358,9 @@ def main() -> None:
289
358
  break
290
359
  # Warn if stuck queued for >10 minutes
291
360
  if status == "queued" and (time.time() - queued_since) > 600:
292
- print("[WARN] Job has remained queued for >10 minutes. Backend may be capacity constrained.")
361
+ print(
362
+ "[WARN] Job has remained queued for >10 minutes. Backend may be capacity constrained."
363
+ )
293
364
  queued_since = time.time()
294
365
  time.sleep(5)
295
366
 
@@ -7,14 +7,12 @@ import argparse
7
7
  import asyncio
8
8
  import json
9
9
  import os
10
+ import sys
10
11
  from pathlib import Path
11
12
  from typing import Any
12
13
 
13
- import sys
14
-
15
14
  import httpx
16
15
  from dotenv import load_dotenv
17
-
18
16
  from synth_ai.task import (
19
17
  RolloutEnvSpec,
20
18
  RolloutPolicySpec,
@@ -46,17 +44,21 @@ def build_rollout_request(
46
44
  )
47
45
  return RolloutRequest(
48
46
  run_id=run_id,
49
- env=RolloutEnvSpec(env_name='crafter', seed=seed, config={}),
50
- policy=RolloutPolicySpec(policy_name='crafter-react', config=policy_config),
47
+ env=RolloutEnvSpec(env_name="crafter", seed=seed, config={}),
48
+ policy=RolloutPolicySpec(policy_name="crafter-react", config=policy_config),
51
49
  ops=ops,
52
50
  record=record_cfg,
53
- on_done='reset',
51
+ on_done="reset",
54
52
  safety=RolloutSafetyConfig(),
55
53
  )
56
54
 
57
55
 
58
56
  def summarise_response(data: Any) -> dict[str, Any]:
59
- metrics = data.metrics.model_dump() if hasattr(data.metrics, "model_dump") else data.get("metrics", {})
57
+ metrics = (
58
+ data.metrics.model_dump()
59
+ if hasattr(data.metrics, "model_dump")
60
+ else data.get("metrics", {})
61
+ )
60
62
  error = None
61
63
  rollout_status = None
62
64
  try:
@@ -86,16 +88,42 @@ async def main() -> None:
86
88
  parser = argparse.ArgumentParser(description=__doc__)
87
89
  parser.add_argument("--base-url", default="http://localhost:8001", help="Task app base URL")
88
90
  parser.add_argument("--api-key", help="Environment API key (or set via --env-file)")
89
- parser.add_argument('--seed', type=int, default=42, help='Env seed to rollout')
90
- parser.add_argument('--run-id', default='local-demo', help='Run identifier')
91
- parser.add_argument('--model', default='gpt-4o-mini', help='Model identifier for the Crafter policy (OpenAI-compatible)')
92
- parser.add_argument('--inference-url', default='https://api.openai.com', help='Inference base URL used by the policy (e.g., https://api.openai.com)')
93
- parser.add_argument('--env-file', type=str, default=None, help='Path to .env file with API keys')
94
- parser.add_argument('--ops', default=None, help='Comma-separated rollout ops (advanced override)')
95
- parser.add_argument('--max-llm-calls', type=int, default=1, help='Number of policy inference calls when --ops not provided')
96
- parser.add_argument('--max-policy-tokens', type=int, default=None, help='Optional per-call token limit forwarded to the policy config')
97
- parser.add_argument('--timeout', type=float, default=600.0, help='HTTP timeout (seconds) for task app requests')
98
- parser.add_argument('--verbose', action='store_true', help='Print resolved configuration and headers')
91
+ parser.add_argument("--seed", type=int, default=42, help="Env seed to rollout")
92
+ parser.add_argument("--run-id", default="local-demo", help="Run identifier")
93
+ parser.add_argument(
94
+ "--model",
95
+ default="gpt-4o-mini",
96
+ help="Model identifier for the Crafter policy (OpenAI-compatible)",
97
+ )
98
+ parser.add_argument(
99
+ "--inference-url",
100
+ default="https://api.openai.com",
101
+ help="Inference base URL used by the policy (e.g., https://api.openai.com)",
102
+ )
103
+ parser.add_argument(
104
+ "--env-file", type=str, default=None, help="Path to .env file with API keys"
105
+ )
106
+ parser.add_argument(
107
+ "--ops", default=None, help="Comma-separated rollout ops (advanced override)"
108
+ )
109
+ parser.add_argument(
110
+ "--max-llm-calls",
111
+ type=int,
112
+ default=1,
113
+ help="Number of policy inference calls when --ops not provided",
114
+ )
115
+ parser.add_argument(
116
+ "--max-policy-tokens",
117
+ type=int,
118
+ default=None,
119
+ help="Optional per-call token limit forwarded to the policy config",
120
+ )
121
+ parser.add_argument(
122
+ "--timeout", type=float, default=600.0, help="HTTP timeout (seconds) for task app requests"
123
+ )
124
+ parser.add_argument(
125
+ "--verbose", action="store_true", help="Print resolved configuration and headers"
126
+ )
99
127
  args = parser.parse_args()
100
128
 
101
129
  if args.env_file:
@@ -117,12 +145,13 @@ async def main() -> None:
117
145
  os.environ["OPENAI_API_KEY"] = synth_key
118
146
 
119
147
  if args.verbose:
148
+
120
149
  def _mask(val: str | None) -> str:
121
150
  if not val:
122
- return '<unset>'
151
+ return "<unset>"
123
152
  return f"{val[:6]}…{val[-4:]} (len={len(val)})"
124
153
 
125
- print('Resolved configuration:')
154
+ print("Resolved configuration:")
126
155
  print(f" Task app base URL : {args.base_url}")
127
156
  print(f" Inference base URL : {args.inference_url}")
128
157
  print(f" Task app API key : {_mask(api_key)}")
@@ -130,21 +159,23 @@ async def main() -> None:
130
159
  print(f" HTTP timeout : {args.timeout:.1f}s")
131
160
 
132
161
  if args.ops:
133
- ops = [op.strip() for op in args.ops.split(',') if op.strip()]
162
+ ops = [op.strip() for op in args.ops.split(",") if op.strip()]
134
163
  if not ops:
135
- raise ValueError('Ops must contain at least one entry')
164
+ raise ValueError("Ops must contain at least one entry")
136
165
  else:
137
166
  llm_calls = max(args.max_llm_calls, 1)
138
167
  if llm_calls > 20:
139
- print('[WARN] --max-llm-calls capped at 20 to avoid excessive episodes; use --ops for manual control.')
168
+ print(
169
+ "[WARN] --max-llm-calls capped at 20 to avoid excessive episodes; use --ops for manual control."
170
+ )
140
171
  llm_calls = 20
141
172
  ops = []
142
173
  for _ in range(llm_calls):
143
- ops.extend(['agent', 'env'])
174
+ ops.extend(["agent", "env"])
144
175
 
145
176
  async with TaskAppClient(args.base_url, api_key=api_key, timeout=args.timeout) as client:
146
177
  try:
147
- print(f'Fetching task_info for seed {args.seed}…')
178
+ print(f"Fetching task_info for seed {args.seed}…")
148
179
  task_info = await client.task_info(seeds=[args.seed])
149
180
  info_payload = task_info[0] if isinstance(task_info, list) else task_info
150
181
  print(json.dumps(info_payload.model_dump(), indent=2)[:600])
@@ -158,29 +189,47 @@ async def main() -> None:
158
189
  extra_headers=extra_headers,
159
190
  )
160
191
  if args.max_policy_tokens is not None:
161
- request.policy.config.update({
162
- 'max_completion_tokens': args.max_policy_tokens,
163
- 'max_tokens': args.max_policy_tokens,
164
- })
192
+ request.policy.config.update(
193
+ {
194
+ "max_completion_tokens": args.max_policy_tokens,
195
+ "max_tokens": args.max_policy_tokens,
196
+ }
197
+ )
165
198
  if args.verbose:
166
- print(f'Ops: {ops}')
167
- print(f'Request headers: {request.policy.config.get("extra_headers", {})}')
168
- print('Requesting rollout…')
199
+ print(f"Ops: {ops}")
200
+ print(f"Request headers: {request.policy.config.get('extra_headers', {})}")
201
+ print("Requesting rollout…")
169
202
  response = await client.rollout(request)
170
203
  summary = summarise_response(response)
171
204
  print(json.dumps(summary, indent=2))
172
- print(f'Ops executed: {ops}')
173
- print('Tip: use --max-llm-calls N for agent/env pairs or --ops for manual control.')
205
+ print(f"Ops executed: {ops}")
206
+ print("Tip: use --max-llm-calls N for agent/env pairs or --ops for manual control.")
174
207
  except httpx.HTTPStatusError as exc:
175
- detail = exc.response.json() if exc.response.headers.get('content-type', '').startswith('application/json') else exc.response.text
176
- print(f'HTTP error {exc.response.status_code}: {detail}', file=sys.stderr)
208
+ detail = (
209
+ exc.response.json()
210
+ if exc.response.headers.get("content-type", "").startswith("application/json")
211
+ else exc.response.text
212
+ )
213
+ print(f"HTTP error {exc.response.status_code}: {detail}", file=sys.stderr)
177
214
  if exc.response.status_code in (401, 503):
178
- print('Hint: ensure the task app was started with ENVIRONMENT_API_KEY set and pass the same key via --api-key.', file=sys.stderr)
215
+ print(
216
+ "Hint: ensure the task app was started with ENVIRONMENT_API_KEY set and pass the same key via --api-key.",
217
+ file=sys.stderr,
218
+ )
179
219
  if exc.response.status_code == 500 and args.model in str(detail):
180
- print('Hint: supply --model/--inference-url (and set OPENAI_API_KEY or GROQ_API_KEY) so the policy can route inference.', file=sys.stderr)
181
- print('Hint: the inference URL should be the base (e.g., https://api.openai.com); the task app appends /v1/chat/completions.', file=sys.stderr)
220
+ print(
221
+ "Hint: supply --model/--inference-url (and set OPENAI_API_KEY or GROQ_API_KEY) so the policy can route inference.",
222
+ file=sys.stderr,
223
+ )
224
+ print(
225
+ "Hint: the inference URL should be the base (e.g., https://api.openai.com); the task app appends /v1/chat/completions.",
226
+ file=sys.stderr,
227
+ )
182
228
  if args.max_policy_tokens is not None:
183
- print(f'Hint: --max-policy-tokens={args.max_policy_tokens} is forwarded to the policy config as max_completion_tokens.', file=sys.stderr)
229
+ print(
230
+ f"Hint: --max-policy-tokens={args.max_policy_tokens} is forwarded to the policy config as max_completion_tokens.",
231
+ file=sys.stderr,
232
+ )
184
233
  raise
185
234
 
186
235