synth-ai 0.2.9.dev5__py3-none-any.whl → 0.2.10__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 (349) hide show
  1. examples/__init__.py +16 -0
  2. examples/crafter_debug_render.py +23 -17
  3. examples/dev/qwen3_32b_qlora_4xh100.toml +40 -0
  4. examples/multi_step/crafter_rl_lora.md +29 -0
  5. examples/qwen_coder/README.md +102 -0
  6. examples/qwen_coder/_shared.py +113 -0
  7. examples/qwen_coder/configs/coder_lora_30b.toml +61 -0
  8. examples/qwen_coder/configs/coder_lora_4b.toml +57 -0
  9. examples/qwen_coder/configs/coder_lora_small.toml +58 -0
  10. examples/qwen_coder/generate_dataset.py +98 -0
  11. examples/qwen_coder/infer_ft_smoke.py +65 -0
  12. examples/qwen_coder/infer_prod_proxy.py +73 -0
  13. examples/qwen_coder/infer_via_synth.py +87 -0
  14. examples/qwen_coder/scripts/infer_coder.sh +19 -0
  15. examples/qwen_coder/scripts/train_coder_30b.sh +22 -0
  16. examples/qwen_coder/sft_full_17b.py +103 -0
  17. examples/qwen_coder/sft_lora_30b.py +110 -0
  18. examples/qwen_coder/subset_jsonl.py +39 -0
  19. examples/qwen_coder/todos.md +38 -0
  20. examples/qwen_coder/validate_jsonl.py +60 -0
  21. examples/rl/configs/eval_base_qwen.toml +1 -1
  22. examples/rl/configs/rl_from_base_qwen17.toml +1 -1
  23. examples/rl/download_dataset.py +26 -10
  24. examples/rl/run_eval.py +53 -52
  25. examples/rl/run_rl_and_save.py +29 -12
  26. examples/rl/task_app/math_single_step.py +180 -41
  27. examples/rl/task_app/math_task_app.py +14 -6
  28. examples/sft/README.md +139 -0
  29. examples/sft/configs/crafter_fft_qwen0p6b.toml +44 -0
  30. examples/sft/configs/crafter_lora_qwen0p6b.toml +45 -0
  31. examples/sft/evaluate.py +117 -0
  32. examples/sft/export_dataset.py +117 -0
  33. examples/sft/generate_traces.py +162 -0
  34. examples/swe/__init__.py +12 -0
  35. examples/swe/task_app/README.md +105 -0
  36. examples/swe/task_app/__init__.py +2 -0
  37. examples/swe/task_app/grpo_swe_mini.py +571 -0
  38. examples/swe/task_app/grpo_swe_mini_task_app.py +136 -0
  39. examples/swe/task_app/hosted/README.md +173 -0
  40. examples/swe/task_app/hosted/__init__.py +5 -0
  41. examples/swe/task_app/hosted/branching.py +143 -0
  42. examples/swe/task_app/hosted/environment_routes.py +1289 -0
  43. examples/swe/task_app/hosted/envs/__init__.py +1 -0
  44. examples/swe/task_app/hosted/envs/crafter/__init__.py +6 -0
  45. examples/swe/task_app/hosted/envs/crafter/app.py +1 -0
  46. examples/swe/task_app/hosted/envs/crafter/environment.py +522 -0
  47. examples/swe/task_app/hosted/envs/crafter/policy.py +478 -0
  48. examples/swe/task_app/hosted/envs/crafter/react_agent.py +108 -0
  49. examples/swe/task_app/hosted/envs/crafter/shared.py +305 -0
  50. examples/swe/task_app/hosted/envs/crafter/tools.py +47 -0
  51. examples/swe/task_app/hosted/envs/mini_swe/__init__.py +8 -0
  52. examples/swe/task_app/hosted/envs/mini_swe/environment.py +1164 -0
  53. examples/swe/task_app/hosted/envs/mini_swe/policy.py +355 -0
  54. examples/swe/task_app/hosted/envs/mini_swe/shared.py +83 -0
  55. examples/swe/task_app/hosted/envs/mini_swe/tools.py +96 -0
  56. examples/swe/task_app/hosted/hosted_app.py +204 -0
  57. examples/swe/task_app/hosted/inference/__init__.py +5 -0
  58. examples/swe/task_app/hosted/inference/openai_client.py +618 -0
  59. examples/swe/task_app/hosted/main.py +100 -0
  60. examples/swe/task_app/hosted/policy_routes.py +1079 -0
  61. examples/swe/task_app/hosted/registry.py +195 -0
  62. examples/swe/task_app/hosted/rollout.py +1869 -0
  63. examples/swe/task_app/hosted/storage/__init__.py +5 -0
  64. examples/swe/task_app/hosted/storage/volume.py +211 -0
  65. examples/swe/task_app/hosted/test_agents.py +161 -0
  66. examples/swe/task_app/hosted/test_service.py +137 -0
  67. examples/swe/task_app/hosted/utils.py +62 -0
  68. examples/vlm/PROPOSAL.md +53 -0
  69. examples/vlm/README.md +68 -0
  70. examples/vlm/configs/crafter_vlm_gpt4o.toml +44 -0
  71. examples/vlm/crafter_image_only_agent.py +207 -0
  72. examples/vlm/crafter_openai_vlm_agent.py +277 -0
  73. examples/vlm/filter_image_rows.py +63 -0
  74. examples/vlm/run_crafter_vlm_benchmark.py +316 -0
  75. examples/warming_up_to_rl/analyze_trace_db.py +12 -10
  76. examples/warming_up_to_rl/configs/rl_from_base_qwen4b.toml +11 -1
  77. examples/warming_up_to_rl/export_trace_sft.py +218 -36
  78. examples/warming_up_to_rl/groq_test.py +15 -8
  79. examples/warming_up_to_rl/manage_secrets.py +29 -25
  80. examples/warming_up_to_rl/readme.md +9 -2
  81. examples/warming_up_to_rl/run_eval.py +137 -61
  82. examples/warming_up_to_rl/run_fft_and_save.py +131 -60
  83. examples/warming_up_to_rl/run_local_rollout.py +88 -39
  84. examples/warming_up_to_rl/run_local_rollout_modal.py +114 -28
  85. examples/warming_up_to_rl/run_local_rollout_parallel.py +81 -20
  86. examples/warming_up_to_rl/run_local_rollout_traced.py +126 -23
  87. examples/warming_up_to_rl/run_rl_and_save.py +35 -12
  88. examples/warming_up_to_rl/run_rollout_remote.py +44 -19
  89. examples/warming_up_to_rl/task_app/README.md +6 -2
  90. examples/warming_up_to_rl/task_app/grpo_crafter.py +319 -57
  91. examples/warming_up_to_rl/task_app/grpo_crafter_task_app.py +11 -30
  92. examples/warming_up_to_rl/task_app/synth_envs_hosted/__init__.py +1 -1
  93. examples/warming_up_to_rl/task_app/synth_envs_hosted/branching.py +9 -11
  94. examples/warming_up_to_rl/task_app/synth_envs_hosted/environment_routes.py +137 -182
  95. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/__init__.py +1 -1
  96. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/__init__.py +1 -1
  97. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/app.py +1 -1
  98. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/environment.py +150 -57
  99. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/policy.py +105 -69
  100. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/react_agent.py +19 -7
  101. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/shared.py +45 -42
  102. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/tools.py +1 -1
  103. examples/warming_up_to_rl/task_app/synth_envs_hosted/hosted_app.py +47 -45
  104. examples/warming_up_to_rl/task_app/synth_envs_hosted/inference/__init__.py +1 -1
  105. examples/warming_up_to_rl/task_app/synth_envs_hosted/inference/openai_client.py +198 -92
  106. examples/warming_up_to_rl/task_app/synth_envs_hosted/main.py +0 -2
  107. examples/warming_up_to_rl/task_app/synth_envs_hosted/policy_routes.py +361 -263
  108. examples/warming_up_to_rl/task_app/synth_envs_hosted/registry.py +21 -23
  109. examples/warming_up_to_rl/task_app/synth_envs_hosted/rollout.py +394 -274
  110. examples/warming_up_to_rl/task_app/synth_envs_hosted/storage/__init__.py +1 -1
  111. examples/warming_up_to_rl/task_app/synth_envs_hosted/storage/volume.py +56 -62
  112. examples/warming_up_to_rl/task_app/synth_envs_hosted/test_agents.py +1 -0
  113. examples/warming_up_to_rl/task_app/synth_envs_hosted/test_service.py +6 -15
  114. examples/warming_up_to_rl/task_app/synth_envs_hosted/utils.py +4 -3
  115. synth_ai/__init__.py +1 -0
  116. synth_ai/api/models/supported.py +376 -0
  117. synth_ai/api/train/builders.py +157 -26
  118. synth_ai/api/train/cli.py +213 -57
  119. synth_ai/api/train/config_finder.py +65 -5
  120. synth_ai/api/train/env_resolver.py +33 -15
  121. synth_ai/api/train/pollers.py +13 -4
  122. synth_ai/api/train/supported_algos.py +139 -0
  123. synth_ai/api/train/task_app.py +5 -3
  124. synth_ai/api/train/utils.py +33 -48
  125. synth_ai/cli/__init__.py +19 -4
  126. synth_ai/cli/_modal_wrapper.py +28 -0
  127. synth_ai/cli/_typer_patch.py +49 -0
  128. synth_ai/cli/balance.py +2 -3
  129. synth_ai/cli/calc.py +1 -1
  130. synth_ai/cli/demo.py +21 -6
  131. synth_ai/cli/recent.py +2 -2
  132. synth_ai/cli/rl_demo.py +77 -17
  133. synth_ai/cli/root.py +116 -39
  134. synth_ai/cli/status.py +2 -2
  135. synth_ai/cli/task_apps.py +1699 -259
  136. synth_ai/cli/traces.py +7 -4
  137. synth_ai/cli/turso.py +73 -0
  138. synth_ai/cli/watch.py +12 -18
  139. synth_ai/core/experiment.py +0 -2
  140. synth_ai/demo_registry.py +68 -31
  141. synth_ai/demos/core/cli.py +516 -194
  142. synth_ai/demos/demo_task_apps/__init__.py +3 -3
  143. synth_ai/demos/demo_task_apps/core.py +64 -28
  144. synth_ai/demos/demo_task_apps/crafter/configs/crafter_fft_4b.toml +2 -3
  145. synth_ai/demos/demo_task_apps/crafter/grpo_crafter_task_app.py +37 -30
  146. synth_ai/demos/demo_task_apps/math/_common.py +1 -2
  147. synth_ai/demos/demo_task_apps/math/app.py +2 -1
  148. synth_ai/demos/demo_task_apps/math/deploy_modal.py +3 -6
  149. synth_ai/demos/demo_task_apps/math/modal_task_app.py +183 -82
  150. synth_ai/demos/demo_task_apps/math/task_app_entry.py +0 -2
  151. synth_ai/environments/examples/bandit/engine.py +12 -4
  152. synth_ai/environments/examples/bandit/taskset.py +4 -4
  153. synth_ai/environments/examples/crafter_classic/environment.py +76 -1
  154. synth_ai/environments/reproducibility/tree.py +5 -6
  155. synth_ai/environments/service/app.py +11 -12
  156. synth_ai/environments/service/core_routes.py +10 -9
  157. synth_ai/environments/stateful/engine.py +1 -1
  158. synth_ai/environments/tasks/core.py +1 -0
  159. synth_ai/environments/tasks/filters.py +5 -6
  160. synth_ai/environments/tasks/utils.py +4 -5
  161. synth_ai/evals/base.py +0 -2
  162. synth_ai/handshake.py +11 -9
  163. synth_ai/http.py +1 -1
  164. synth_ai/http_client.py +43 -11
  165. synth_ai/inference/__init__.py +0 -2
  166. synth_ai/inference/client.py +20 -6
  167. synth_ai/jobs/client.py +103 -78
  168. synth_ai/learning/__init__.py +41 -6
  169. synth_ai/learning/algorithms.py +14 -0
  170. synth_ai/learning/client.py +121 -29
  171. synth_ai/learning/config.py +2 -40
  172. synth_ai/learning/constants.py +0 -2
  173. synth_ai/learning/ft_client.py +4 -56
  174. synth_ai/learning/health.py +13 -7
  175. synth_ai/learning/jobs.py +43 -47
  176. synth_ai/{rl → learning/rl}/__init__.py +14 -5
  177. synth_ai/learning/rl/client.py +267 -0
  178. synth_ai/learning/rl/config.py +31 -0
  179. synth_ai/{rl → learning/rl}/contracts.py +5 -10
  180. synth_ai/{rl → learning/rl}/env_keys.py +45 -16
  181. synth_ai/learning/rl/secrets.py +13 -0
  182. synth_ai/learning/rl_client.py +2 -253
  183. synth_ai/learning/sft/__init__.py +29 -0
  184. synth_ai/learning/sft/client.py +68 -0
  185. synth_ai/learning/sft/config.py +270 -0
  186. synth_ai/learning/sft/data.py +295 -0
  187. synth_ai/learning/sse.py +25 -26
  188. synth_ai/learning/validators.py +25 -24
  189. synth_ai/lm/__init__.py +21 -47
  190. synth_ai/task/__init__.py +26 -27
  191. synth_ai/task/apps/__init__.py +18 -19
  192. synth_ai/task/auth.py +35 -23
  193. synth_ai/task/client.py +15 -13
  194. synth_ai/task/contracts.py +37 -35
  195. synth_ai/task/datasets.py +9 -6
  196. synth_ai/task/errors.py +11 -10
  197. synth_ai/task/health.py +17 -11
  198. synth_ai/task/json.py +58 -24
  199. synth_ai/task/proxy.py +15 -14
  200. synth_ai/task/rubrics.py +22 -15
  201. synth_ai/task/server.py +43 -17
  202. synth_ai/task/tracing_utils.py +12 -7
  203. synth_ai/task/validators.py +0 -1
  204. synth_ai/task/vendors.py +5 -7
  205. synth_ai/tracing_v3/__init__.py +2 -0
  206. synth_ai/tracing_v3/abstractions.py +21 -4
  207. synth_ai/tracing_v3/db_config.py +26 -1
  208. synth_ai/tracing_v3/decorators.py +18 -15
  209. synth_ai/tracing_v3/examples/basic_usage.py +3 -2
  210. synth_ai/tracing_v3/hooks.py +6 -4
  211. synth_ai/tracing_v3/llm_call_record_helpers.py +6 -6
  212. synth_ai/tracing_v3/replica_sync.py +1 -0
  213. synth_ai/tracing_v3/session_tracer.py +63 -16
  214. synth_ai/tracing_v3/storage/base.py +89 -1
  215. synth_ai/tracing_v3/storage/config.py +21 -8
  216. synth_ai/tracing_v3/storage/factory.py +10 -8
  217. synth_ai/tracing_v3/storage/utils.py +4 -2
  218. synth_ai/tracing_v3/turso/daemon.py +7 -2
  219. synth_ai/tracing_v3/turso/models.py +5 -2
  220. synth_ai/tracing_v3/turso/native_manager.py +1173 -0
  221. synth_ai/tracing_v3/utils.py +4 -3
  222. synth_ai/v0/api/__init__.py +8 -0
  223. synth_ai/v0/api/models/__init__.py +8 -0
  224. synth_ai/v0/api/models/supported.py +8 -0
  225. synth_ai/v0/config/__init__.py +15 -0
  226. synth_ai/v0/config/base_url.py +12 -0
  227. synth_ai/v0/lm/__init__.py +51 -0
  228. synth_ai/{lm → v0/lm}/caching/ephemeral.py +3 -5
  229. synth_ai/{lm → v0/lm}/caching/handler.py +4 -4
  230. synth_ai/{lm → v0/lm}/caching/initialize.py +1 -1
  231. synth_ai/{lm → v0/lm}/caching/persistent.py +1 -1
  232. synth_ai/{lm → v0/lm}/config.py +6 -1
  233. synth_ai/{lm → v0/lm}/core/all.py +9 -9
  234. synth_ai/{lm → v0/lm}/core/exceptions.py +0 -2
  235. synth_ai/{lm → v0/lm}/core/main.py +19 -7
  236. synth_ai/{lm → v0/lm}/core/main_v3.py +10 -10
  237. synth_ai/{lm → v0/lm}/core/synth_models.py +2 -15
  238. synth_ai/{lm → v0/lm}/core/vendor_clients.py +6 -4
  239. synth_ai/{lm → v0/lm}/overrides.py +4 -4
  240. synth_ai/{lm → v0/lm}/provider_support/anthropic.py +4 -4
  241. synth_ai/{lm → v0/lm}/provider_support/openai.py +5 -5
  242. synth_ai/{lm → v0/lm}/structured_outputs/handler.py +5 -5
  243. synth_ai/{lm → v0/lm}/structured_outputs/rehabilitate.py +1 -1
  244. synth_ai/{lm → v0/lm}/vendors/core/anthropic_api.py +16 -16
  245. synth_ai/{lm → v0/lm}/vendors/core/gemini_api.py +5 -5
  246. synth_ai/{lm → v0/lm}/vendors/core/mistral_api.py +5 -5
  247. synth_ai/{lm → v0/lm}/vendors/core/openai_api.py +12 -10
  248. synth_ai/{lm → v0/lm}/vendors/openai_standard.py +11 -9
  249. synth_ai/{lm → v0/lm}/vendors/openai_standard_responses.py +8 -5
  250. synth_ai/{lm → v0/lm}/vendors/supported/custom_endpoint.py +4 -6
  251. synth_ai/{lm → v0/lm}/vendors/supported/deepseek.py +2 -2
  252. synth_ai/{lm → v0/lm}/vendors/supported/grok.py +2 -2
  253. synth_ai/{lm → v0/lm}/vendors/supported/groq.py +1 -1
  254. synth_ai/{lm → v0/lm}/vendors/supported/ollama.py +1 -1
  255. synth_ai/{lm → v0/lm}/vendors/supported/openrouter.py +3 -3
  256. synth_ai/{lm → v0/lm}/vendors/supported/together.py +1 -1
  257. synth_ai/{lm → v0/lm}/vendors/synth_client.py +38 -11
  258. synth_ai/v0/tracing/upload.py +32 -135
  259. synth_ai/v0/tracing_v3/__init__.py +10 -0
  260. synth_ai/v0/tracing_v3/abstractions.py +3 -0
  261. synth_ai/v0/tracing_v3/decorators.py +3 -0
  262. synth_ai/v0/tracing_v3/llm_call_record_helpers.py +3 -0
  263. synth_ai/v0/tracing_v3/session_tracer.py +3 -0
  264. {synth_ai-0.2.9.dev5.dist-info → synth_ai-0.2.10.dist-info}/METADATA +10 -7
  265. {synth_ai-0.2.9.dev5.dist-info → synth_ai-0.2.10.dist-info}/RECORD +294 -258
  266. examples/common_old/backend.py +0 -21
  267. examples/evals_old/README.md +0 -98
  268. examples/evals_old/__init__.py +0 -6
  269. examples/evals_old/compare_models.py +0 -1037
  270. examples/evals_old/example_log.md +0 -145
  271. examples/evals_old/run_demo.sh +0 -126
  272. examples/evals_old/trace_analysis.py +0 -270
  273. examples/finetuning_old/_backup_synth_qwen/config.toml +0 -29
  274. examples/finetuning_old/_backup_synth_qwen/example_log.md +0 -324
  275. examples/finetuning_old/_backup_synth_qwen/filter_traces.py +0 -60
  276. examples/finetuning_old/_backup_synth_qwen/filter_traces_achievements.py +0 -239
  277. examples/finetuning_old/_backup_synth_qwen/purge_v3_traces.py +0 -109
  278. examples/finetuning_old/_backup_synth_qwen/react_agent_lm.py +0 -1924
  279. examples/finetuning_old/_backup_synth_qwen/readme.md +0 -49
  280. examples/finetuning_old/_backup_synth_qwen/run_crafter_qwen4b.py +0 -114
  281. examples/finetuning_old/_backup_synth_qwen/run_demo.sh +0 -195
  282. examples/finetuning_old/_backup_synth_qwen/sft_kickoff.py +0 -118
  283. examples/finetuning_old/synth_qwen_v1/README.md +0 -68
  284. examples/finetuning_old/synth_qwen_v1/filter_traces.py +0 -60
  285. examples/finetuning_old/synth_qwen_v1/filter_traces_achievements.py +0 -239
  286. examples/finetuning_old/synth_qwen_v1/finetune.py +0 -46
  287. examples/finetuning_old/synth_qwen_v1/hello_ft_model.py +0 -71
  288. examples/finetuning_old/synth_qwen_v1/infer.py +0 -37
  289. examples/finetuning_old/synth_qwen_v1/poll.py +0 -44
  290. examples/finetuning_old/synth_qwen_v1/prepare_data.py +0 -35
  291. examples/finetuning_old/synth_qwen_v1/purge_v3_traces.py +0 -109
  292. examples/finetuning_old/synth_qwen_v1/react_agent_lm.py +0 -1932
  293. examples/finetuning_old/synth_qwen_v1/run_crafter_sft_job.py +0 -207
  294. examples/finetuning_old/synth_qwen_v1/run_ft_job.py +0 -232
  295. examples/finetuning_old/synth_qwen_v1/upload_data.py +0 -34
  296. examples/finetuning_old/synth_qwen_v1/util.py +0 -147
  297. examples/rl_old/task_app.py +0 -962
  298. synth_ai/experimental/synth_oss.py +0 -446
  299. synth_ai/install_sqld.sh +0 -40
  300. synth_ai/learning/filtering.py +0 -0
  301. synth_ai/learning/offline/dpo.py +0 -0
  302. synth_ai/learning/offline/providers.py +0 -7
  303. synth_ai/learning/offline/sft.py +0 -0
  304. synth_ai/learning/offline/shared.py +0 -0
  305. synth_ai/learning/online/grpo.py +0 -0
  306. synth_ai/learning/online/irft.py +0 -0
  307. synth_ai/learning/prompts/banking77_injection_eval.py +0 -168
  308. synth_ai/learning/prompts/gepa.py +0 -0
  309. synth_ai/learning/prompts/hello_world_in_context_injection_ex.py +0 -213
  310. synth_ai/learning/prompts/mipro.py +0 -289
  311. synth_ai/learning/prompts/random_search.py +0 -246
  312. synth_ai/learning/prompts/run_mipro_banking77.py +0 -172
  313. synth_ai/learning/prompts/run_random_search_banking77.py +0 -324
  314. synth_ai/rl/secrets.py +0 -19
  315. synth_ai/scripts/verify_rewards.py +0 -100
  316. synth_ai/tracing/__init__.py +0 -30
  317. synth_ai/tracing_v1/__init__.py +0 -33
  318. synth_ai/tracing_v3/turso/__init__.py +0 -25
  319. synth_ai/tracing_v3/turso/manager.py +0 -774
  320. synth_ai/zyk/__init__.py +0 -30
  321. /synth_ai/{lm → v0/lm}/caching/__init__.py +0 -0
  322. /synth_ai/{lm → v0/lm}/caching/constants.py +0 -0
  323. /synth_ai/{lm → v0/lm}/caching/dbs.py +0 -0
  324. /synth_ai/{lm → v0/lm}/constants.py +0 -0
  325. /synth_ai/{lm → v0/lm}/core/__init__.py +0 -0
  326. /synth_ai/{lm → v0/lm}/cost/__init__.py +0 -0
  327. /synth_ai/{lm → v0/lm}/cost/monitor.py +0 -0
  328. /synth_ai/{lm → v0/lm}/cost/statefulness.py +0 -0
  329. /synth_ai/{lm → v0/lm}/injection.py +0 -0
  330. /synth_ai/{lm → v0/lm}/provider_support/__init__.py +0 -0
  331. /synth_ai/{lm → v0/lm}/provider_support/suppress_logging.py +0 -0
  332. /synth_ai/{lm → v0/lm}/structured_outputs/__init__.py +0 -0
  333. /synth_ai/{lm → v0/lm}/structured_outputs/inject.py +0 -0
  334. /synth_ai/{lm → v0/lm}/tools/__init__.py +0 -0
  335. /synth_ai/{lm → v0/lm}/tools/base.py +0 -0
  336. /synth_ai/{lm → v0/lm}/unified_interface.py +0 -0
  337. /synth_ai/{lm → v0/lm}/vendors/__init__.py +0 -0
  338. /synth_ai/{lm → v0/lm}/vendors/base.py +0 -0
  339. /synth_ai/{lm → v0/lm}/vendors/core/__init__.py +0 -0
  340. /synth_ai/{lm → v0/lm}/vendors/core/synth_dev_api.py +0 -0
  341. /synth_ai/{lm → v0/lm}/vendors/local/__init__.py +0 -0
  342. /synth_ai/{lm → v0/lm}/vendors/local/ollama.py +0 -0
  343. /synth_ai/{lm → v0/lm}/vendors/retries.py +0 -0
  344. /synth_ai/{lm → v0/lm}/vendors/supported/__init__.py +0 -0
  345. /synth_ai/{lm → v0/lm}/warmup.py +0 -0
  346. {synth_ai-0.2.9.dev5.dist-info → synth_ai-0.2.10.dist-info}/WHEEL +0 -0
  347. {synth_ai-0.2.9.dev5.dist-info → synth_ai-0.2.10.dist-info}/entry_points.txt +0 -0
  348. {synth_ai-0.2.9.dev5.dist-info → synth_ai-0.2.10.dist-info}/licenses/LICENSE +0 -0
  349. {synth_ai-0.2.9.dev5.dist-info → synth_ai-0.2.10.dist-info}/top_level.txt +0 -0
@@ -14,8 +14,10 @@ big “backend.production” code-base.
14
14
  from __future__ import annotations
15
15
 
16
16
  import gzip
17
+ import hashlib
17
18
  import json
18
19
  import logging
20
+ import os
19
21
  import pickle
20
22
  import sqlite3
21
23
  from collections.abc import Iterable
@@ -32,11 +34,6 @@ log = logging.getLogger(__name__)
32
34
  # --------------------------------------------------------------------------- #
33
35
  # lightweight metadata record #
34
36
  # --------------------------------------------------------------------------- #
35
- import hashlib
36
- import logging
37
- import os
38
-
39
- log = logging.getLogger(__name__)
40
37
 
41
38
  # Default directory for storing snapshots relative to some base path
42
39
  # This could be configured via environment variables or settings later.
@@ -256,7 +253,9 @@ class TrajectoryTreeStore:
256
253
  def reconstruct_actions(self, snap_id: str) -> tuple[Any, ...]:
257
254
  """Return the sequence of *actions* from the root → `snap_id`."""
258
255
  actions = []
259
- for child, parent in zip(self.path_to_root(snap_id)[:-1], self.path_to_root(snap_id)[1:], strict=False):
256
+ for child, parent in zip(
257
+ self.path_to_root(snap_id)[:-1], self.path_to_root(snap_id)[1:], strict=False
258
+ ):
260
259
  actions.append(self.graph.edges[parent, child]["action"])
261
260
  return tuple(reversed(actions))
262
261
 
@@ -1,6 +1,17 @@
1
+ import logging
1
2
  import os # Added to ensure os is available before use
2
3
  import sys
3
4
 
5
+ import synth_ai.environments.examples.crafter_classic.environment as cc
6
+ import synth_ai.environments.examples.crafter_custom.environment as ccustom
7
+ from fastapi import FastAPI
8
+ from synth_ai.environments.service.core_routes import api_router
9
+ from synth_ai.environments.service.external_registry import (
10
+ ExternalRegistryConfig,
11
+ load_external_environments,
12
+ )
13
+ from synth_ai.environments.service.registry import list_supported_env_types, register_environment
14
+
4
15
  # Ensure repository root is on PYTHONPATH for dev installs
5
16
  # Current file path: <repo>/synth_ai/environments/service/app.py
6
17
  # We want sys.path to include <repo>, NOT <repo>/synth_ai to avoid shadowing stdlib 'http'
@@ -16,15 +27,6 @@ if _repo_root not in sys.path:
16
27
  sys.path.insert(0, _repo_root)
17
28
 
18
29
  print(f"SYS.PATH IN APP.PY: {sys.path}")
19
- import logging
20
-
21
- from fastapi import FastAPI
22
- from synth_ai.environments.service.core_routes import api_router
23
- from synth_ai.environments.service.external_registry import (
24
- ExternalRegistryConfig,
25
- load_external_environments,
26
- )
27
- from synth_ai.environments.service.registry import list_supported_env_types, register_environment
28
30
 
29
31
  # Configure logging with more detail
30
32
  logging.basicConfig(
@@ -38,11 +40,8 @@ logger = logging.getLogger(__name__)
38
40
  logging.getLogger("uvicorn.access").setLevel(logging.INFO)
39
41
 
40
42
  # Register built-in environments at import time
41
- import synth_ai.environments.examples.crafter_classic.environment as cc
42
43
 
43
44
  register_environment("CrafterClassic", cc.CrafterClassicEnvironment)
44
- import synth_ai.environments.examples.crafter_custom.environment as ccustom
45
-
46
45
  register_environment("CrafterCustom", ccustom.CrafterCustomEnvironment)
47
46
 
48
47
  # Register Wordle example environment
@@ -97,15 +97,12 @@ def create_task_instance_for_environment(
97
97
  task.initial_engine_snapshot["seed"] = config["seed"]
98
98
 
99
99
  # For CrafterClassic, also handle difficulty
100
- if env_name == "CrafterClassic" and config:
101
- if "difficulty" in config:
102
- task.initial_engine_snapshot["difficulty"] = config["difficulty"]
100
+ if env_name == "CrafterClassic" and config and "difficulty" in config:
101
+ task.initial_engine_snapshot["difficulty"] = config["difficulty"]
103
102
 
104
103
  # For MiniGrid, handle environment selection
105
- if env_name == "MiniGrid" and config:
106
- # Check if a specific environment is requested
107
- if "env_name" in config:
108
- task.initial_engine_snapshot["env_name"] = config["env_name"]
104
+ if env_name == "MiniGrid" and config and "env_name" in config:
105
+ task.initial_engine_snapshot["env_name"] = config["env_name"]
109
106
 
110
107
  return task
111
108
 
@@ -951,7 +948,9 @@ async def register_environment_api(request: RegisterEnvironmentRequest) -> dict[
951
948
  ) from e
952
949
  except Exception as e:
953
950
  logger.error(f"Failed to register environment {request.name}: {e}")
954
- raise HTTPException(status_code=500, detail=f"Failed to register environment: {str(e)}") from e
951
+ raise HTTPException(
952
+ status_code=500, detail=f"Failed to register environment: {str(e)}"
953
+ ) from e
955
954
 
956
955
 
957
956
  @api_router.delete("/registry/environments/{env_name}")
@@ -984,7 +983,9 @@ async def unregister_environment_api(env_name: str) -> dict[str, Any]:
984
983
 
985
984
  except Exception as e:
986
985
  logger.error(f"Failed to unregister environment {env_name}: {e}")
987
- raise HTTPException(status_code=500, detail=f"Failed to unregister environment: {str(e)}") from e
986
+ raise HTTPException(
987
+ status_code=500, detail=f"Failed to unregister environment: {str(e)}"
988
+ ) from e
988
989
 
989
990
 
990
991
  @api_router.get("/registry/environments")
@@ -14,7 +14,7 @@ class StatefulEngine(Engine):
14
14
  pass
15
15
 
16
16
  @classmethod
17
- async def deserialize(self, engine_snapshot: StatefulEngineSnapshot):
17
+ async def deserialize(cls, engine_snapshot: StatefulEngineSnapshot):
18
18
  pass
19
19
 
20
20
  async def _step_engine(self):
@@ -4,6 +4,7 @@ from dataclasses import dataclass, field
4
4
  from typing import Any, Optional
5
5
  from uuid import UUID
6
6
 
7
+ from synth_ai.environments.stateful.engine import StatefulEngineSnapshot
7
8
  from synth_ai.environments.v0_observability.history import SynthGlobalTrajectory
8
9
 
9
10
 
@@ -29,13 +29,12 @@ class RangeFilter(TaskInstanceMetadataFilter):
29
29
  # If the attribute doesn't exist on the metadata, it can't be in range.
30
30
  return False
31
31
 
32
- if not isinstance(instance_value, (int, float)):
32
+ if not isinstance(instance_value, int | float):
33
33
  # If the attribute is not a number, it can't be in a numerical range.
34
34
  # Or, we could raise an error, depending on desired strictness.
35
35
  return False
36
36
 
37
- if self.min_val is not None and instance_value < self.min_val:
38
- return False
39
- if self.max_val is not None and instance_value > self.max_val:
40
- return False
41
- return True
37
+ return not (
38
+ (self.min_val is not None and instance_value < self.min_val)
39
+ or (self.max_val is not None and instance_value > self.max_val)
40
+ )
@@ -54,11 +54,10 @@ class RangeFilter(TaskInstanceMetadataFilter):
54
54
 
55
55
  def __call__(self, instance: TaskInstance) -> bool:
56
56
  value = getattr(instance.metadata, self.key, None)
57
- if self.min_value is not None and (value is None or value < self.min_value):
58
- return False
59
- if self.max_value is not None and (value is None or value > self.max_value):
60
- return False
61
- return True
57
+ return not (
58
+ (self.min_value is not None and (value is None or value < self.min_value))
59
+ or (self.max_value is not None and (value is None or value > self.max_value))
60
+ )
62
61
 
63
62
 
64
63
  def make_taskset(
synth_ai/evals/base.py CHANGED
@@ -1,5 +1,3 @@
1
-
2
-
3
1
  class Judgement:
4
2
  def __init__(
5
3
  self, criteria: str, score: float, reasoning: str = "", evidence: list[str] = None
synth_ai/handshake.py CHANGED
@@ -1,8 +1,10 @@
1
1
  from __future__ import annotations
2
+
3
+ import contextlib
2
4
  import os
3
5
  import time
4
6
  import webbrowser
5
- from typing import Any, Dict, Tuple
7
+ from typing import Any
6
8
  from urllib.parse import urljoin, urlsplit, urlunsplit
7
9
 
8
10
  import requests
@@ -43,7 +45,7 @@ def _split_origin(origin: str) -> tuple[str, str]:
43
45
  return bare, path
44
46
 
45
47
 
46
- def _ensure_verification_uri(data: Dict[str, Any], base_with_path: str) -> None:
48
+ def _ensure_verification_uri(data: dict[str, Any], base_with_path: str) -> None:
47
49
  uri = data.get("verification_uri")
48
50
  if not isinstance(uri, str) or not uri:
49
51
  return
@@ -52,7 +54,7 @@ def _ensure_verification_uri(data: Dict[str, Any], base_with_path: str) -> None:
52
54
  data["verification_uri"] = urljoin(base_with_path.rstrip("/") + "/", uri.lstrip("/"))
53
55
 
54
56
 
55
- def start_handshake_session(origin: str | None = None) -> Tuple[str, str, int, int]:
57
+ def start_handshake_session(origin: str | None = None) -> tuple[str, str, int, int]:
56
58
  base = (origin or _origin()).rstrip("/")
57
59
  api_origin, _ = _split_origin(base)
58
60
  url = urljoin(api_origin.rstrip("/") + "/", "api/sdk/handshake/init")
@@ -72,7 +74,9 @@ def start_handshake_session(origin: str | None = None) -> Tuple[str, str, int, i
72
74
  )
73
75
 
74
76
 
75
- def poll_handshake_token(device_code: str, origin: str | None = None, *, timeout_s: int | None = None) -> Dict[str, Any]:
77
+ def poll_handshake_token(
78
+ device_code: str, origin: str | None = None, *, timeout_s: int | None = None
79
+ ) -> dict[str, Any]:
76
80
  base = (origin or _origin()).rstrip("/")
77
81
  api_origin, _ = _split_origin(base)
78
82
  url = urljoin(api_origin.rstrip("/") + "/", "api/sdk/handshake/token")
@@ -82,7 +86,7 @@ def poll_handshake_token(device_code: str, origin: str | None = None, *, timeout
82
86
  raise HandshakeError("handshake timed out")
83
87
  try:
84
88
  r = requests.post(url, json={"device_code": device_code}, timeout=10)
85
- except Exception as e:
89
+ except Exception:
86
90
  time.sleep(2)
87
91
  continue
88
92
  if r.status_code == 200:
@@ -98,10 +102,8 @@ def poll_handshake_token(device_code: str, origin: str | None = None, *, timeout
98
102
  time.sleep(2)
99
103
 
100
104
 
101
- def run_handshake(origin: str | None = None) -> Dict[str, Any]:
105
+ def run_handshake(origin: str | None = None) -> dict[str, Any]:
102
106
  device_code, verification_uri, expires_in, interval = start_handshake_session(origin)
103
- try:
107
+ with contextlib.suppress(Exception):
104
108
  webbrowser.open(verification_uri)
105
- except Exception:
106
- pass
107
109
  return poll_handshake_token(device_code, origin, timeout_s=expires_in)
synth_ai/http.py CHANGED
@@ -18,7 +18,7 @@ except Exception:
18
18
  _client_path = _here.parent / "http_client.py"
19
19
  _spec = _ilu.spec_from_file_location("http_client", str(_client_path))
20
20
  if not _spec or not _spec.loader:
21
- raise ImportError("Could not load http_client module")
21
+ raise ImportError("Could not load http_client module") from None
22
22
  _mod = _ilu.module_from_spec(_spec)
23
23
  _spec.loader.exec_module(_mod)
24
24
  _sys.modules["synth_ai.http_client"] = _mod
synth_ai/http_client.py CHANGED
@@ -1,8 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import asyncio
4
+ import os
4
5
  from dataclasses import dataclass
5
- from typing import Any, Dict, Optional
6
+ from typing import Any
6
7
 
7
8
  import aiohttp
8
9
 
@@ -27,11 +28,18 @@ class AsyncHttpClient:
27
28
  self._base_url = base_url.rstrip("/")
28
29
  self._api_key = api_key
29
30
  self._timeout = aiohttp.ClientTimeout(total=timeout)
30
- self._session: Optional[aiohttp.ClientSession] = None
31
+ self._session: aiohttp.ClientSession | None = None
31
32
 
32
- async def __aenter__(self) -> "AsyncHttpClient":
33
+ async def __aenter__(self) -> AsyncHttpClient:
33
34
  if self._session is None:
34
35
  headers = {"authorization": f"Bearer {self._api_key}"}
36
+ # Optional dev overrides for user/org context
37
+ user_id = os.getenv("SYNTH_USER_ID") or os.getenv("X_USER_ID") or os.getenv("USER_ID")
38
+ if user_id:
39
+ headers["X-User-ID"] = user_id
40
+ org_id = os.getenv("SYNTH_ORG_ID") or os.getenv("X_ORG_ID") or os.getenv("ORG_ID")
41
+ if org_id:
42
+ headers["X-Org-ID"] = org_id
35
43
  self._session = aiohttp.ClientSession(headers=headers, timeout=self._timeout)
36
44
  return self
37
45
 
@@ -48,30 +56,50 @@ class AsyncHttpClient:
48
56
  path = path[4:] # Remove leading /api
49
57
  return f"{self._base_url}/{path.lstrip('/')}"
50
58
 
51
- async def get(self, path: str, *, params: Optional[Dict[str, Any]] = None, headers: Optional[Dict[str, str]] = None) -> Any:
59
+ async def get(
60
+ self,
61
+ path: str,
62
+ *,
63
+ params: dict[str, Any] | None = None,
64
+ headers: dict[str, str] | None = None,
65
+ ) -> Any:
52
66
  url = self._abs(path)
53
67
  assert self._session is not None, "AsyncHttpClient must be used as an async context manager"
54
68
  async with self._session.get(url, params=params, headers=headers) as resp:
55
69
  return await self._handle_response(resp, url)
56
70
 
57
- async def post_json(self, path: str, *, json: Dict[str, Any], headers: Optional[Dict[str, str]] = None) -> Any:
71
+ async def post_json(
72
+ self, path: str, *, json: dict[str, Any], headers: dict[str, str] | None = None
73
+ ) -> Any:
58
74
  url = self._abs(path)
59
75
  assert self._session is not None, "AsyncHttpClient must be used as an async context manager"
60
76
  async with self._session.post(url, json=json, headers=headers) as resp:
61
77
  return await self._handle_response(resp, url)
62
78
 
63
- async def post_multipart(self, path: str, *, data: Dict[str, Any], files: Dict[str, tuple[str, bytes, str | None]], headers: Optional[Dict[str, str]] = None) -> Any:
79
+ async def post_multipart(
80
+ self,
81
+ path: str,
82
+ *,
83
+ data: dict[str, Any],
84
+ files: dict[str, tuple[str, bytes, str | None]],
85
+ headers: dict[str, str] | None = None,
86
+ ) -> Any:
64
87
  url = self._abs(path)
65
88
  assert self._session is not None, "AsyncHttpClient must be used as an async context manager"
66
89
  form = aiohttp.FormData()
67
90
  for k, v in data.items():
68
91
  form.add_field(k, str(v))
69
92
  for field, (filename, content, content_type) in files.items():
70
- form.add_field(field, content, filename=filename, content_type=content_type or "application/octet-stream")
93
+ form.add_field(
94
+ field,
95
+ content,
96
+ filename=filename,
97
+ content_type=content_type or "application/octet-stream",
98
+ )
71
99
  async with self._session.post(url, data=form, headers=headers) as resp:
72
100
  return await self._handle_response(resp, url)
73
101
 
74
- async def delete(self, path: str, *, headers: Optional[Dict[str, str]] = None) -> Any:
102
+ async def delete(self, path: str, *, headers: dict[str, str] | None = None) -> Any:
75
103
  url = self._abs(path)
76
104
  assert self._session is not None, "AsyncHttpClient must be used as an async context manager"
77
105
  async with self._session.delete(url, headers=headers) as resp:
@@ -95,10 +123,14 @@ class AsyncHttpClient:
95
123
  detail = await resp.json()
96
124
  except Exception:
97
125
  detail = None
98
- raise HTTPError(status=resp.status, url=url, message="request_failed", body_snippet=body_snippet, detail=detail)
126
+ raise HTTPError(
127
+ status=resp.status,
128
+ url=url,
129
+ message="request_failed",
130
+ body_snippet=body_snippet,
131
+ detail=detail,
132
+ )
99
133
 
100
134
 
101
135
  async def sleep(seconds: float) -> None:
102
136
  await asyncio.sleep(seconds)
103
-
104
-
@@ -3,5 +3,3 @@ from .client import InferenceClient
3
3
  __all__ = [
4
4
  "InferenceClient",
5
5
  ]
6
-
7
-
@@ -1,6 +1,11 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import Any, Dict
3
+ from typing import Any
4
+
5
+ from synth_ai.api.models.supported import (
6
+ UnsupportedModelError,
7
+ normalize_model_identifier,
8
+ )
4
9
 
5
10
  from ..http import AsyncHttpClient
6
11
 
@@ -11,10 +16,19 @@ class InferenceClient:
11
16
  self._api_key = api_key
12
17
  self._timeout = timeout
13
18
 
14
- async def create_chat_completion(self, *, model: str, messages: list[dict], **kwargs: Any) -> Dict[str, Any]:
15
- body: Dict[str, Any] = {"model": model, "messages": messages}
19
+ async def create_chat_completion(
20
+ self, *, model: str, messages: list[dict], **kwargs: Any
21
+ ) -> dict[str, Any]:
22
+ try:
23
+ normalized_model = normalize_model_identifier(model)
24
+ except UnsupportedModelError as exc:
25
+ raise ValueError(str(exc)) from exc
26
+
27
+ body: dict[str, Any] = {"model": normalized_model, "messages": messages}
16
28
  body.update(kwargs)
29
+ # Backend now expects an explicit thinking_budget; provide a sensible default if omitted
30
+ if "thinking_budget" not in body:
31
+ body["thinking_budget"] = 256
17
32
  async with AsyncHttpClient(self._base_url, self._api_key, timeout=self._timeout) as http:
18
- return await http.post_json("/v1/chat/completions", json=body)
19
-
20
-
33
+ # Route through backend inference proxy to Modal
34
+ return await http.post_json("/api/inference/v1/chat/completions", json=body)