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
@@ -8,10 +8,11 @@ rendered surroundings appeared only as iron/stone due to a mismatched
8
8
  hardcoded mapping.
9
9
  """
10
10
 
11
- from typing import Dict, Any, List, Set
12
- import numpy as np
13
- import re
14
11
  import itertools
12
+ import re
13
+ from typing import Any
14
+
15
+ import numpy as np
15
16
 
16
17
  VIEW_SIZE = 5 # Default view size for the map (match eval_rollout_table)
17
18
 
@@ -58,9 +59,9 @@ ACTION_ALIASES = {
58
59
  "craft_iron_sword": "make_iron_sword",
59
60
  }
60
61
 
61
- VALID_PRIMARY_ACTIONS: Set[str] = set(CRAFTER_ACTIONS.keys())
62
- VALID_ACTION_ALIASES: Set[str] = set(ACTION_ALIASES.keys())
63
- ALL_VALID_ACTION_STRINGS: Set[str] = VALID_PRIMARY_ACTIONS | VALID_ACTION_ALIASES
62
+ VALID_PRIMARY_ACTIONS: set[str] = set(CRAFTER_ACTIONS.keys())
63
+ VALID_ACTION_ALIASES: set[str] = set(ACTION_ALIASES.keys())
64
+ ALL_VALID_ACTION_STRINGS: set[str] = VALID_PRIMARY_ACTIONS | VALID_ACTION_ALIASES
64
65
 
65
66
 
66
67
  def validate_action(action: str) -> bool:
@@ -69,9 +70,9 @@ def validate_action(action: str) -> bool:
69
70
  return normalized in ALL_VALID_ACTION_STRINGS
70
71
 
71
72
 
72
- def parse_actions(action_text: str) -> List[str]:
73
+ def parse_actions(action_text: str) -> list[str]:
73
74
  """Extract actions from response text.
74
-
75
+
75
76
  Tries multiple parsing strategies:
76
77
  1. <action>...</action> tags (original format)
77
78
  2. [action]...[/action] or [action]... format
@@ -79,44 +80,43 @@ def parse_actions(action_text: str) -> List[str]:
79
80
  4. Plain action names if they match valid actions
80
81
  5. Newline-separated actions
81
82
  """
82
- import json
83
-
83
+
84
84
  # First try the original <action> tag format
85
85
  matches = re.findall(r"<action>(.*?)</action>", action_text, re.IGNORECASE)
86
86
  if matches:
87
87
  return [m.strip() for m in matches if validate_action(m.strip())]
88
-
88
+
89
89
  # Try [action] format
90
90
  matches = re.findall(r"\[action\](.*?)(?:\[/action\]|\n|$)", action_text, re.IGNORECASE)
91
91
  if matches:
92
92
  return [m.strip() for m in matches if validate_action(m.strip())]
93
-
93
+
94
94
  # If no tags found, try to parse plain text
95
95
  text = action_text.strip()
96
-
96
+
97
97
  # Check if the entire text is a valid action
98
98
  if validate_action(text):
99
99
  return [text]
100
-
100
+
101
101
  # Try splitting by newlines and checking each line
102
- lines = text.split('\n')
102
+ lines = text.split("\n")
103
103
  actions = []
104
104
  for line in lines:
105
105
  line = line.strip()
106
-
106
+
107
107
  # Remove various prefixes
108
- for prefix in ['ACTION:', 'Action:', 'action:', 'ACTION', '-', '*', '', '**ACTION:**']:
108
+ for prefix in ["ACTION:", "Action:", "action:", "ACTION", "-", "*", "", "**ACTION:**"]:
109
109
  if line.startswith(prefix):
110
- line = line[len(prefix):].strip()
110
+ line = line[len(prefix) :].strip()
111
111
  break
112
-
112
+
113
113
  # Also handle numbered lists
114
- if re.match(r'^\d+\.\s*', line):
115
- line = re.sub(r'^\d+\.\s*', '', line)
116
-
114
+ if re.match(r"^\d+\.\s*", line):
115
+ line = re.sub(r"^\d+\.\s*", "", line)
116
+
117
117
  # Split by common separators to handle multiple actions on one line
118
- parts = re.split(r'[,;]|\s+and\s+|\s+then\s+', line)
119
-
118
+ parts = re.split(r"[,;]|\s+and\s+|\s+then\s+", line)
119
+
120
120
  for part in parts:
121
121
  part = part.strip()
122
122
  # Remove quotes if present
@@ -124,23 +124,23 @@ def parse_actions(action_text: str) -> List[str]:
124
124
  part = part[1:-1]
125
125
  if part.startswith("'") and part.endswith("'"):
126
126
  part = part[1:-1]
127
-
127
+
128
128
  # Check if it's a valid action
129
129
  if part and validate_action(part):
130
130
  actions.append(part)
131
-
131
+
132
132
  return actions
133
133
 
134
134
 
135
- def format_observation(obs_data: Dict[str, Any], step_count: int = 0, max_steps: int = 100) -> str:
135
+ def format_observation(obs_data: dict[str, Any], step_count: int = 0, max_steps: int = 100) -> str:
136
136
  """Format a Crafter observation dictionary into a human-readable string.
137
-
137
+
138
138
  This is critical for preventing massive token counts when observations
139
139
  contain large numpy arrays or deeply nested structures.
140
140
  """
141
141
  if not obs_data:
142
142
  return ""
143
-
143
+
144
144
  # Extract key information
145
145
  health = obs_data.get("health") or obs_data.get("inventory", {}).get("health", 0)
146
146
  inventory_dict = obs_data.get("inventory", {})
@@ -154,24 +154,24 @@ def format_observation(obs_data: Dict[str, Any], step_count: int = 0, max_steps:
154
154
  if obs_data.get("steps") is not None
155
155
  else obs_data.get("num_steps_taken")
156
156
  )
157
- if isinstance(step_from_obs, (int, float)) and step_from_obs >= 0:
157
+ if isinstance(step_from_obs, int | float) and step_from_obs >= 0:
158
158
  step_count = int(step_from_obs)
159
159
 
160
160
  max_steps_from_obs = obs_data.get("max_steps_episode") or obs_data.get("max_steps")
161
- if isinstance(max_steps_from_obs, (int, float)) and max_steps_from_obs > 0:
161
+ if isinstance(max_steps_from_obs, int | float) and max_steps_from_obs > 0:
162
162
  max_steps = int(max_steps_from_obs)
163
-
163
+
164
164
  # Format inventory (skip health as it's shown separately)
165
165
  inv_items = [f"{k}:{v}" for k, v in inventory_dict.items() if v > 0 and k != "health"]
166
166
  inventory_str = ", ".join(inv_items) if inv_items else "empty"
167
-
167
+
168
168
  # Format achievements
169
169
  achieved_list = [k for k, v in achievements.items() if v]
170
170
  achievements_str = ", ".join(achieved_list) if achieved_list else "none"
171
-
171
+
172
172
  # Format semantic map view (simplified version)
173
173
  map_view = _format_semantic_map_view(obs_data, VIEW_SIZE)
174
-
174
+
175
175
  return (
176
176
  f"=== CRAFTER GAME STATE ===\n"
177
177
  f"Step: {step_count}/{max_steps}\n"
@@ -184,6 +184,7 @@ def format_observation(obs_data: Dict[str, Any], step_count: int = 0, max_steps:
184
184
  f"Choose your next actions.\n"
185
185
  )
186
186
 
187
+
187
188
  def _try_build_dynamic_mapping():
188
189
  """Attempt to build id->name mapping from a real Crafter env.
189
190
 
@@ -232,7 +233,7 @@ def _try_build_dynamic_mapping():
232
233
  # Build dynamic mapping if possible; otherwise fall back to a basic map
233
234
  _ID_TO_NAME = _try_build_dynamic_mapping()
234
235
  _FALLBACK_ID_TO_NAME = {
235
- 0: "none", # None from materials
236
+ 0: "none", # None from materials
236
237
  1: "water",
237
238
  2: "grass",
238
239
  3: "stone",
@@ -254,7 +255,7 @@ _FALLBACK_ID_TO_NAME = {
254
255
  }
255
256
 
256
257
 
257
- def _format_semantic_map_view(obs_data: Dict[str, Any], view_size: int = VIEW_SIZE) -> str:
258
+ def _format_semantic_map_view(obs_data: dict[str, Any], view_size: int = VIEW_SIZE) -> str:
258
259
  """Format the semantic map into a text representation using dynamic IDs.
259
260
 
260
261
  Shows a local view around the player with nearby objects.
@@ -279,9 +280,9 @@ def _format_semantic_map_view(obs_data: Dict[str, Any], view_size: int = VIEW_SI
279
280
  use_list = isinstance(_ID_TO_NAME, list) and len(_ID_TO_NAME) > 0
280
281
 
281
282
  # Build matrix centered at player, then transpose for human-friendly view
282
- matrix: List[List[str]] = []
283
+ matrix: list[list[str]] = []
283
284
  for dy in range(-half, half + 1):
284
- row_tokens: List[str] = []
285
+ row_tokens: list[str] = []
285
286
  for dx in range(-half, half + 1):
286
287
  x, y = px + dx, py + dy
287
288
  if not (0 <= x < sem_arr.shape[0] and 0 <= y < sem_arr.shape[1]):
@@ -297,6 +298,8 @@ def _format_semantic_map_view(obs_data: Dict[str, Any], view_size: int = VIEW_SI
297
298
  row_tokens.append(name)
298
299
  matrix.append(row_tokens)
299
300
 
300
- transposed = list(zip(*matrix))
301
- grid_rows: List[str] = [" ".join(row) for row in transposed]
302
- return "\nLocal Map View (" + str(view_size) + "x" + str(view_size) + "):\n" + "\n".join(grid_rows)
301
+ transposed = list(zip(*matrix, strict=False))
302
+ grid_rows: list[str] = [" ".join(row) for row in transposed]
303
+ return (
304
+ "\nLocal Map View (" + str(view_size) + "x" + str(view_size) + "):\n" + "\n".join(grid_rows)
305
+ )
@@ -44,4 +44,4 @@ TOOLS_SCHEMA = [
44
44
  },
45
45
  },
46
46
  }
47
- ]
47
+ ]
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import contextlib
3
4
  import os
4
- from typing import Optional
5
5
 
6
6
  from fastapi import FastAPI
7
7
  from fastapi.middleware.cors import CORSMiddleware
@@ -15,16 +15,14 @@ class TaskApp:
15
15
 
16
16
  def __init__(
17
17
  self,
18
- service_base_url: Optional[str] = None,
19
- vllm_base_url: Optional[str] = None,
20
- default_model: Optional[str] = None,
18
+ service_base_url: str | None = None,
19
+ vllm_base_url: str | None = None,
20
+ default_model: str | None = None,
21
21
  ) -> None:
22
22
  self.service_base_url = service_base_url or os.getenv(
23
23
  "SERVICE_BASE_URL", "http://localhost:8000"
24
24
  )
25
- self.vllm_base_url = vllm_base_url or os.getenv(
26
- "VLLM_BASE_URL", "http://localhost:8001"
27
- )
25
+ self.vllm_base_url = vllm_base_url or os.getenv("VLLM_BASE_URL", "http://localhost:8001")
28
26
  self.default_model = default_model or os.getenv("DEFAULT_MODEL")
29
27
 
30
28
 
@@ -69,55 +67,51 @@ def create_app(allowed_environments: list[str] = None) -> FastAPI:
69
67
  @app.middleware("http")
70
68
  async def validate_environment(request, call_next):
71
69
  # Check if this is an environment-related request
72
- if request.url.path.startswith("/env/") or request.url.path.startswith(
73
- "/rollout"
74
- ):
75
- # Extract environment name from request body for POST requests
76
- if request.method == "POST":
77
- # We need to read the body to check env_name
78
- body = await request.body()
79
- try:
80
- import json
81
-
82
- data = json.loads(body) if body else {}
83
- env_name = data.get("env_name", "").lower()
84
-
85
- # Check if environment is allowed
86
- if env_name and env_name not in [
87
- e.lower() for e in allowed_environments
88
- ]:
89
- from fastapi import HTTPException
90
-
91
- raise HTTPException(
92
- status_code=403,
93
- detail=f"Environment '{env_name}' not allowed. This service only handles: {allowed_environments}",
94
- )
95
- except json.JSONDecodeError:
96
- pass # Invalid JSON, let the endpoint handle it
97
-
98
- # Recreate request with the body we consumed
99
- request._body = body
70
+ path = request.url.path
71
+ if (
72
+ path.startswith("/env/") or path.startswith("/rollout")
73
+ ) and request.method == "POST":
74
+ # We need to read the body to check env_name
75
+ body = await request.body()
76
+ try:
77
+ import json
78
+
79
+ data = json.loads(body) if body else {}
80
+ env_name = data.get("env_name", "").lower()
81
+
82
+ # Check if environment is allowed
83
+ if env_name and env_name not in [e.lower() for e in allowed_environments]:
84
+ from fastapi import HTTPException
85
+
86
+ raise HTTPException(
87
+ status_code=403,
88
+ detail=f"Environment '{env_name}' not allowed. This service only handles: {allowed_environments}",
89
+ )
90
+ except json.JSONDecodeError:
91
+ pass # Invalid JSON, let the endpoint handle it
92
+
93
+ # Recreate request with the body we consumed
94
+ request._body = body
100
95
 
101
96
  response = await call_next(request)
102
97
  return response
103
98
 
104
99
  # Mount routers
100
+ from .branching import router as branching_router
105
101
  from .environment_routes import router as env_router
106
102
  from .rollout import router as rollout_router
107
- from .branching import router as branching_router
108
103
 
109
104
  app.include_router(env_router, prefix="/env", tags=["environment"])
110
105
 
111
106
  # Policy routes are optional; skip if optional envs are missing in this build
112
107
  try:
113
108
  from .policy_routes import router as policy_router
109
+
114
110
  app.include_router(policy_router, prefix="/policy", tags=["policy"])
115
111
  except Exception as _e:
116
112
  # Log lightweight message; policy endpoints will be unavailable
117
- try:
113
+ with contextlib.suppress(Exception):
118
114
  print(f"[hosted_app] Skipping policy routes: {_e}", flush=True)
119
- except Exception:
120
- pass
121
115
 
122
116
  app.include_router(rollout_router, tags=["rollout"])
123
117
  app.include_router(branching_router, tags=["branching"])
@@ -153,10 +147,10 @@ def create_app(allowed_environments: list[str] = None) -> FastAPI:
153
147
  - If X-API-Key header is provided and mismatches, returns 401.
154
148
  - Otherwise returns 200 with basic info.
155
149
  """
156
- import os as _os
157
150
 
158
151
  # Check if any environment API keys are configured
159
152
  from synth_ai.task.auth import allowed_environment_api_keys
153
+
160
154
  allowed_keys = allowed_environment_api_keys()
161
155
  if not allowed_keys:
162
156
  # Server-side misconfiguration; rollout would fail with 503
@@ -167,28 +161,34 @@ def create_app(allowed_environments: list[str] = None) -> FastAPI:
167
161
  "detail": "Auth not configured: missing ENVIRONMENT_API_KEY in task service environment",
168
162
  },
169
163
  )
170
-
164
+
171
165
  # Authorize using all header variants without typed Header params (avoid 422s)
172
166
  from synth_ai.task.auth import is_api_key_header_authorized
167
+
173
168
  authorized = is_api_key_header_authorized(request)
174
169
  if not authorized:
175
170
  # Soft-pass 200 with authorized=False to avoid failing CLI preflight
176
171
  primary_key = list(allowed_keys)[0] if allowed_keys else None
177
- prefix = (primary_key[: max(1, len(primary_key) // 2)] if primary_key else None)
172
+ prefix = primary_key[: max(1, len(primary_key) // 2)] if primary_key else None
178
173
  content = {"status": "healthy", "authorized": False}
179
174
  if prefix:
180
175
  content["expected_api_key_prefix"] = prefix
181
176
  return JSONResponse(status_code=200, content=content)
182
- return {"status": "healthy", "authorized": True, "service": {"base_url": task_app.service_base_url}}
177
+ return {
178
+ "status": "healthy",
179
+ "authorized": True,
180
+ "service": {"base_url": task_app.service_base_url},
181
+ }
183
182
 
184
183
  # Log and surface 422 validation errors with header presence
185
184
  from fastapi.exceptions import RequestValidationError
185
+
186
186
  @app.exception_handler(RequestValidationError)
187
187
  async def _on_validation_error(request: Request, exc: RequestValidationError):
188
188
  try:
189
189
  hdr = request.headers
190
190
  snapshot = {
191
- "path": str(getattr(request, "url").path),
191
+ "path": str(request.url.path),
192
192
  "have_x_api_key": bool(hdr.get("x-api-key")),
193
193
  "have_x_api_keys": bool(hdr.get("x-api-keys")),
194
194
  "have_authorization": bool(hdr.get("authorization")),
@@ -197,6 +197,8 @@ def create_app(allowed_environments: list[str] = None) -> FastAPI:
197
197
  print("[422] validation", snapshot, flush=True)
198
198
  except Exception:
199
199
  pass
200
- return JSONResponse(status_code=422, content={"status": "invalid", "detail": exc.errors()[:5]})
200
+ return JSONResponse(
201
+ status_code=422, content={"status": "invalid", "detail": exc.errors()[:5]}
202
+ )
201
203
 
202
204
  return app
@@ -2,4 +2,4 @@
2
2
 
3
3
  from .openai_client import OpenAIClient, create_inference_client
4
4
 
5
- __all__ = ["OpenAIClient", "create_inference_client"]
5
+ __all__ = ["OpenAIClient", "create_inference_client"]