synth-ai 0.2.9.dev7__py3-none-any.whl → 0.2.9.dev9__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 (327) hide show
  1. examples/__init__.py +16 -0
  2. examples/crafter_debug_render.py +8 -11
  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/run_eval.py +36 -37
  19. examples/rl/run_rl_and_save.py +5 -5
  20. examples/rl/task_app/math_single_step.py +65 -43
  21. examples/rl/task_app/math_task_app.py +3 -3
  22. examples/sft/README.md +139 -0
  23. examples/sft/configs/crafter_fft_qwen0p6b.toml +44 -0
  24. examples/sft/configs/crafter_lora_qwen0p6b.toml +45 -0
  25. examples/sft/evaluate.py +117 -0
  26. examples/sft/export_dataset.py +117 -0
  27. examples/sft/generate_traces.py +162 -0
  28. examples/swe/__init__.py +12 -0
  29. examples/swe/task_app/README.md +105 -0
  30. examples/swe/task_app/__init__.py +2 -0
  31. examples/swe/task_app/grpo_swe_mini.py +571 -0
  32. examples/swe/task_app/grpo_swe_mini_task_app.py +136 -0
  33. examples/swe/task_app/hosted/README.md +173 -0
  34. examples/swe/task_app/hosted/__init__.py +5 -0
  35. examples/swe/task_app/hosted/branching.py +143 -0
  36. examples/swe/task_app/hosted/environment_routes.py +1289 -0
  37. examples/swe/task_app/hosted/envs/__init__.py +1 -0
  38. examples/swe/task_app/hosted/envs/crafter/__init__.py +6 -0
  39. examples/swe/task_app/hosted/envs/crafter/app.py +1 -0
  40. examples/swe/task_app/hosted/envs/crafter/environment.py +522 -0
  41. examples/swe/task_app/hosted/envs/crafter/policy.py +478 -0
  42. examples/swe/task_app/hosted/envs/crafter/react_agent.py +108 -0
  43. examples/swe/task_app/hosted/envs/crafter/shared.py +305 -0
  44. examples/swe/task_app/hosted/envs/crafter/tools.py +47 -0
  45. examples/swe/task_app/hosted/envs/mini_swe/__init__.py +8 -0
  46. examples/swe/task_app/hosted/envs/mini_swe/environment.py +1164 -0
  47. examples/swe/task_app/hosted/envs/mini_swe/policy.py +355 -0
  48. examples/swe/task_app/hosted/envs/mini_swe/shared.py +83 -0
  49. examples/swe/task_app/hosted/envs/mini_swe/tools.py +96 -0
  50. examples/swe/task_app/hosted/hosted_app.py +204 -0
  51. examples/swe/task_app/hosted/inference/__init__.py +5 -0
  52. examples/swe/task_app/hosted/inference/openai_client.py +618 -0
  53. examples/swe/task_app/hosted/main.py +100 -0
  54. examples/swe/task_app/hosted/policy_routes.py +1079 -0
  55. examples/swe/task_app/hosted/registry.py +195 -0
  56. examples/swe/task_app/hosted/rollout.py +1869 -0
  57. examples/swe/task_app/hosted/storage/__init__.py +5 -0
  58. examples/swe/task_app/hosted/storage/volume.py +211 -0
  59. examples/swe/task_app/hosted/test_agents.py +161 -0
  60. examples/swe/task_app/hosted/test_service.py +137 -0
  61. examples/swe/task_app/hosted/utils.py +62 -0
  62. examples/vlm/README.md +68 -0
  63. examples/vlm/configs/crafter_vlm_gpt4o.toml +44 -0
  64. examples/vlm/crafter_image_only_agent.py +207 -0
  65. examples/vlm/crafter_openai_vlm_agent.py +277 -0
  66. examples/vlm/filter_image_rows.py +63 -0
  67. examples/vlm/run_crafter_vlm_benchmark.py +316 -0
  68. examples/warming_up_to_rl/analyze_trace_db.py +5 -5
  69. examples/warming_up_to_rl/configs/rl_from_base_qwen4b.toml +11 -1
  70. examples/warming_up_to_rl/export_trace_sft.py +78 -21
  71. examples/warming_up_to_rl/groq_test.py +4 -4
  72. examples/warming_up_to_rl/manage_secrets.py +13 -18
  73. examples/warming_up_to_rl/run_eval.py +42 -44
  74. examples/warming_up_to_rl/run_fft_and_save.py +11 -16
  75. examples/warming_up_to_rl/run_local_rollout.py +1 -3
  76. examples/warming_up_to_rl/run_local_rollout_modal.py +2 -4
  77. examples/warming_up_to_rl/run_local_rollout_parallel.py +1 -4
  78. examples/warming_up_to_rl/run_local_rollout_traced.py +3 -5
  79. examples/warming_up_to_rl/run_rl_and_save.py +5 -6
  80. examples/warming_up_to_rl/run_rollout_remote.py +8 -10
  81. examples/warming_up_to_rl/task_app/README.md +6 -2
  82. examples/warming_up_to_rl/task_app/grpo_crafter.py +234 -35
  83. examples/warming_up_to_rl/task_app/grpo_crafter_task_app.py +2 -3
  84. examples/warming_up_to_rl/task_app/synth_envs_hosted/__init__.py +1 -1
  85. examples/warming_up_to_rl/task_app/synth_envs_hosted/branching.py +9 -11
  86. examples/warming_up_to_rl/task_app/synth_envs_hosted/environment_routes.py +131 -114
  87. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/environment.py +101 -41
  88. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/policy.py +73 -51
  89. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/react_agent.py +14 -6
  90. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/shared.py +16 -16
  91. examples/warming_up_to_rl/task_app/synth_envs_hosted/hosted_app.py +32 -34
  92. examples/warming_up_to_rl/task_app/synth_envs_hosted/inference/openai_client.py +94 -31
  93. examples/warming_up_to_rl/task_app/synth_envs_hosted/main.py +0 -2
  94. examples/warming_up_to_rl/task_app/synth_envs_hosted/policy_routes.py +303 -203
  95. examples/warming_up_to_rl/task_app/synth_envs_hosted/registry.py +21 -23
  96. examples/warming_up_to_rl/task_app/synth_envs_hosted/rollout.py +328 -225
  97. examples/warming_up_to_rl/task_app/synth_envs_hosted/storage/volume.py +13 -13
  98. examples/warming_up_to_rl/task_app/synth_envs_hosted/test_agents.py +1 -0
  99. examples/warming_up_to_rl/task_app/synth_envs_hosted/test_service.py +1 -0
  100. examples/warming_up_to_rl/task_app/synth_envs_hosted/utils.py +4 -3
  101. synth/__init__.py +14 -0
  102. synth_ai/__init__.py +26 -4
  103. synth_ai/api/models/supported.py +376 -0
  104. synth_ai/api/train/builders.py +128 -21
  105. synth_ai/api/train/cli.py +80 -64
  106. synth_ai/api/train/config_finder.py +7 -2
  107. synth_ai/api/train/env_resolver.py +1 -1
  108. synth_ai/api/train/pollers.py +2 -1
  109. synth_ai/api/train/supported_algos.py +139 -0
  110. synth_ai/api/train/task_app.py +1 -2
  111. synth_ai/api/train/utils.py +13 -44
  112. synth_ai/cli/__init__.py +8 -0
  113. synth_ai/cli/_modal_wrapper.py +28 -0
  114. synth_ai/cli/_typer_patch.py +49 -0
  115. synth_ai/cli/balance.py +1 -2
  116. synth_ai/cli/calc.py +1 -1
  117. synth_ai/cli/demo.py +2 -1
  118. synth_ai/cli/recent.py +2 -2
  119. synth_ai/cli/rl_demo.py +2 -1
  120. synth_ai/cli/root.py +11 -13
  121. synth_ai/cli/status.py +2 -2
  122. synth_ai/cli/task_apps.py +529 -179
  123. synth_ai/cli/traces.py +6 -4
  124. synth_ai/cli/watch.py +12 -18
  125. synth_ai/demo_registry.py +1 -1
  126. synth_ai/demos/core/cli.py +36 -43
  127. synth_ai/demos/demo_task_apps/__init__.py +3 -3
  128. synth_ai/demos/demo_task_apps/core.py +17 -25
  129. synth_ai/demos/demo_task_apps/crafter/grpo_crafter_task_app.py +3 -4
  130. synth_ai/demos/demo_task_apps/math/app.py +2 -1
  131. synth_ai/demos/demo_task_apps/math/deploy_modal.py +3 -4
  132. synth_ai/demos/demo_task_apps/math/modal_task_app.py +16 -18
  133. synth_ai/demos/demo_task_apps/math/task_app_entry.py +0 -1
  134. synth_ai/environments/examples/crafter_classic/environment.py +76 -1
  135. synth_ai/environments/reproducibility/tree.py +2 -5
  136. synth_ai/environments/service/app.py +11 -12
  137. synth_ai/environments/service/core_routes.py +4 -7
  138. synth_ai/environments/stateful/engine.py +1 -1
  139. synth_ai/environments/tasks/core.py +1 -0
  140. synth_ai/environments/tasks/filters.py +5 -6
  141. synth_ai/environments/tasks/utils.py +4 -5
  142. synth_ai/handshake.py +9 -9
  143. synth_ai/http.py +1 -1
  144. synth_ai/http_client.py +18 -10
  145. synth_ai/inference/client.py +15 -5
  146. synth_ai/jobs/client.py +78 -83
  147. synth_ai/learning/__init__.py +41 -6
  148. synth_ai/learning/algorithms.py +14 -0
  149. synth_ai/learning/client.py +91 -24
  150. synth_ai/learning/config.py +2 -38
  151. synth_ai/learning/ft_client.py +4 -59
  152. synth_ai/learning/health.py +5 -6
  153. synth_ai/learning/jobs.py +31 -47
  154. synth_ai/{rl → learning/rl}/__init__.py +14 -4
  155. synth_ai/learning/rl/client.py +267 -0
  156. synth_ai/learning/rl/config.py +31 -0
  157. synth_ai/{rl → learning/rl}/contracts.py +5 -8
  158. synth_ai/{rl → learning/rl}/env_keys.py +39 -15
  159. synth_ai/learning/rl/secrets.py +13 -0
  160. synth_ai/learning/rl_client.py +2 -281
  161. synth_ai/learning/sft/__init__.py +29 -0
  162. synth_ai/learning/sft/client.py +68 -0
  163. synth_ai/learning/sft/config.py +270 -0
  164. synth_ai/learning/sft/data.py +295 -0
  165. synth_ai/learning/sse.py +25 -24
  166. synth_ai/learning/validators.py +25 -28
  167. synth_ai/lm/__init__.py +21 -47
  168. synth_ai/main.py +6 -0
  169. synth_ai/task/__init__.py +25 -27
  170. synth_ai/task/apps/__init__.py +7 -8
  171. synth_ai/task/auth.py +8 -8
  172. synth_ai/task/client.py +14 -14
  173. synth_ai/task/contracts.py +36 -35
  174. synth_ai/task/datasets.py +6 -5
  175. synth_ai/task/errors.py +10 -10
  176. synth_ai/task/health.py +17 -9
  177. synth_ai/task/json.py +58 -23
  178. synth_ai/task/proxy.py +13 -9
  179. synth_ai/task/rubrics.py +16 -15
  180. synth_ai/task/server.py +12 -12
  181. synth_ai/task/tracing_utils.py +4 -4
  182. synth_ai/task/vendors.py +5 -6
  183. synth_ai/tracing_v3/__init__.py +2 -0
  184. synth_ai/tracing_v3/abstractions.py +21 -4
  185. synth_ai/tracing_v3/decorators.py +18 -16
  186. synth_ai/tracing_v3/hooks.py +5 -5
  187. synth_ai/tracing_v3/llm_call_record_helpers.py +6 -6
  188. synth_ai/tracing_v3/session_tracer.py +40 -14
  189. synth_ai/tracing_v3/storage/base.py +85 -0
  190. synth_ai/tracing_v3/storage/config.py +21 -8
  191. synth_ai/tracing_v3/storage/factory.py +10 -7
  192. synth_ai/tracing_v3/storage/utils.py +4 -2
  193. synth_ai/tracing_v3/turso/daemon.py +7 -2
  194. synth_ai/tracing_v3/turso/models.py +2 -2
  195. synth_ai/tracing_v3/turso/native_manager.py +1173 -0
  196. synth_ai/tracing_v3/utils.py +4 -4
  197. synth_ai/v0/api/__init__.py +8 -0
  198. synth_ai/v0/api/models/__init__.py +8 -0
  199. synth_ai/v0/api/models/supported.py +8 -0
  200. synth_ai/v0/config/__init__.py +15 -0
  201. synth_ai/v0/config/base_url.py +12 -0
  202. synth_ai/v0/lm/__init__.py +51 -0
  203. synth_ai/{lm → v0/lm}/caching/ephemeral.py +2 -2
  204. synth_ai/{lm → v0/lm}/caching/handler.py +4 -4
  205. synth_ai/{lm → v0/lm}/caching/initialize.py +1 -1
  206. synth_ai/{lm → v0/lm}/caching/persistent.py +1 -1
  207. synth_ai/{lm → v0/lm}/config.py +6 -1
  208. synth_ai/{lm → v0/lm}/core/all.py +9 -9
  209. synth_ai/{lm → v0/lm}/core/main.py +6 -6
  210. synth_ai/{lm → v0/lm}/core/main_v3.py +10 -10
  211. synth_ai/{lm → v0/lm}/core/synth_models.py +2 -14
  212. synth_ai/{lm → v0/lm}/core/vendor_clients.py +2 -2
  213. synth_ai/{lm → v0/lm}/overrides.py +2 -2
  214. synth_ai/{lm → v0/lm}/provider_support/anthropic.py +4 -4
  215. synth_ai/{lm → v0/lm}/provider_support/openai.py +5 -5
  216. synth_ai/{lm → v0/lm}/structured_outputs/handler.py +5 -5
  217. synth_ai/{lm → v0/lm}/structured_outputs/rehabilitate.py +1 -1
  218. synth_ai/{lm → v0/lm}/vendors/core/anthropic_api.py +9 -9
  219. synth_ai/{lm → v0/lm}/vendors/core/gemini_api.py +5 -5
  220. synth_ai/{lm → v0/lm}/vendors/core/mistral_api.py +5 -5
  221. synth_ai/{lm → v0/lm}/vendors/core/openai_api.py +10 -10
  222. synth_ai/{lm → v0/lm}/vendors/openai_standard.py +8 -8
  223. synth_ai/{lm → v0/lm}/vendors/openai_standard_responses.py +2 -2
  224. synth_ai/{lm → v0/lm}/vendors/supported/custom_endpoint.py +3 -3
  225. synth_ai/{lm → v0/lm}/vendors/supported/deepseek.py +2 -2
  226. synth_ai/{lm → v0/lm}/vendors/supported/grok.py +2 -2
  227. synth_ai/{lm → v0/lm}/vendors/supported/groq.py +1 -1
  228. synth_ai/{lm → v0/lm}/vendors/supported/ollama.py +1 -1
  229. synth_ai/{lm → v0/lm}/vendors/supported/openrouter.py +3 -3
  230. synth_ai/{lm → v0/lm}/vendors/supported/together.py +1 -1
  231. synth_ai/{lm → v0/lm}/vendors/synth_client.py +1 -1
  232. synth_ai/v0/tracing_v3/__init__.py +10 -0
  233. synth_ai/v0/tracing_v3/abstractions.py +3 -0
  234. synth_ai/v0/tracing_v3/decorators.py +3 -0
  235. synth_ai/v0/tracing_v3/llm_call_record_helpers.py +3 -0
  236. synth_ai/v0/tracing_v3/session_tracer.py +3 -0
  237. synth_ai-0.2.9.dev9.dist-info/METADATA +191 -0
  238. {synth_ai-0.2.9.dev7.dist-info → synth_ai-0.2.9.dev9.dist-info}/RECORD +268 -238
  239. {synth_ai-0.2.9.dev7.dist-info → synth_ai-0.2.9.dev9.dist-info}/top_level.txt +1 -0
  240. examples/common_old/backend.py +0 -20
  241. examples/evals_old/README.md +0 -98
  242. examples/evals_old/__init__.py +0 -6
  243. examples/evals_old/compare_models.py +0 -1038
  244. examples/evals_old/example_log.md +0 -145
  245. examples/evals_old/run_demo.sh +0 -126
  246. examples/evals_old/trace_analysis.py +0 -270
  247. examples/finetuning_old/_backup_synth_qwen/config.toml +0 -29
  248. examples/finetuning_old/_backup_synth_qwen/example_log.md +0 -324
  249. examples/finetuning_old/_backup_synth_qwen/filter_traces.py +0 -60
  250. examples/finetuning_old/_backup_synth_qwen/filter_traces_achievements.py +0 -243
  251. examples/finetuning_old/_backup_synth_qwen/purge_v3_traces.py +0 -109
  252. examples/finetuning_old/_backup_synth_qwen/react_agent_lm.py +0 -1924
  253. examples/finetuning_old/_backup_synth_qwen/readme.md +0 -49
  254. examples/finetuning_old/_backup_synth_qwen/run_crafter_qwen4b.py +0 -114
  255. examples/finetuning_old/_backup_synth_qwen/run_demo.sh +0 -195
  256. examples/finetuning_old/_backup_synth_qwen/sft_kickoff.py +0 -119
  257. examples/finetuning_old/synth_qwen_v1/README.md +0 -68
  258. examples/finetuning_old/synth_qwen_v1/filter_traces.py +0 -60
  259. examples/finetuning_old/synth_qwen_v1/filter_traces_achievements.py +0 -243
  260. examples/finetuning_old/synth_qwen_v1/finetune.py +0 -46
  261. examples/finetuning_old/synth_qwen_v1/hello_ft_model.py +0 -71
  262. examples/finetuning_old/synth_qwen_v1/infer.py +0 -36
  263. examples/finetuning_old/synth_qwen_v1/poll.py +0 -46
  264. examples/finetuning_old/synth_qwen_v1/prepare_data.py +0 -35
  265. examples/finetuning_old/synth_qwen_v1/purge_v3_traces.py +0 -109
  266. examples/finetuning_old/synth_qwen_v1/react_agent_lm.py +0 -1933
  267. examples/finetuning_old/synth_qwen_v1/run_crafter_sft_job.py +0 -210
  268. examples/finetuning_old/synth_qwen_v1/run_ft_job.py +0 -237
  269. examples/finetuning_old/synth_qwen_v1/upload_data.py +0 -34
  270. examples/finetuning_old/synth_qwen_v1/util.py +0 -152
  271. examples/rl_old/task_app.py +0 -1131
  272. examples/warming_up_to_rl/old/event_rewards.md +0 -234
  273. examples/warming_up_to_rl/old/notes.md +0 -73
  274. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/filter_traces_sft_turso.py +0 -738
  275. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/filter_traces_sft_turso.py +0 -580
  276. synth_ai/experimental/synth_oss.py +0 -445
  277. synth_ai/learning/filtering.py +0 -0
  278. synth_ai/learning/offline/dpo.py +0 -0
  279. synth_ai/learning/offline/providers.py +0 -7
  280. synth_ai/learning/offline/sft.py +0 -0
  281. synth_ai/learning/offline/shared.py +0 -0
  282. synth_ai/learning/online/grpo.py +0 -0
  283. synth_ai/learning/online/irft.py +0 -0
  284. synth_ai/learning/prompts/banking77_injection_eval.py +0 -168
  285. synth_ai/learning/prompts/gepa.py +0 -0
  286. synth_ai/learning/prompts/hello_world_in_context_injection_ex.py +0 -211
  287. synth_ai/learning/prompts/mipro.py +0 -289
  288. synth_ai/learning/prompts/random_search.py +0 -249
  289. synth_ai/learning/prompts/run_mipro_banking77.py +0 -172
  290. synth_ai/learning/prompts/run_random_search_banking77.py +0 -329
  291. synth_ai/rl/secrets.py +0 -19
  292. synth_ai/scripts/verify_rewards.py +0 -100
  293. synth_ai/tracing/__init__.py +0 -30
  294. synth_ai/tracing_v1/__init__.py +0 -33
  295. synth_ai/tracing_v3/turso/__init__.py +0 -25
  296. synth_ai/tracing_v3/turso/manager.py +0 -838
  297. synth_ai/zyk/__init__.py +0 -30
  298. synth_ai-0.2.9.dev7.dist-info/METADATA +0 -131
  299. /synth_ai/{lm → v0/lm}/caching/__init__.py +0 -0
  300. /synth_ai/{lm → v0/lm}/caching/constants.py +0 -0
  301. /synth_ai/{lm → v0/lm}/caching/dbs.py +0 -0
  302. /synth_ai/{lm → v0/lm}/constants.py +0 -0
  303. /synth_ai/{lm → v0/lm}/core/__init__.py +0 -0
  304. /synth_ai/{lm → v0/lm}/core/exceptions.py +0 -0
  305. /synth_ai/{lm → v0/lm}/cost/__init__.py +0 -0
  306. /synth_ai/{lm → v0/lm}/cost/monitor.py +0 -0
  307. /synth_ai/{lm → v0/lm}/cost/statefulness.py +0 -0
  308. /synth_ai/{lm → v0/lm}/injection.py +0 -0
  309. /synth_ai/{lm → v0/lm}/provider_support/__init__.py +0 -0
  310. /synth_ai/{lm → v0/lm}/provider_support/suppress_logging.py +0 -0
  311. /synth_ai/{lm → v0/lm}/structured_outputs/__init__.py +0 -0
  312. /synth_ai/{lm → v0/lm}/structured_outputs/inject.py +0 -0
  313. /synth_ai/{lm → v0/lm}/tools/__init__.py +0 -0
  314. /synth_ai/{lm → v0/lm}/tools/base.py +0 -0
  315. /synth_ai/{lm → v0/lm}/unified_interface.py +0 -0
  316. /synth_ai/{lm → v0/lm}/vendors/__init__.py +0 -0
  317. /synth_ai/{lm → v0/lm}/vendors/base.py +0 -0
  318. /synth_ai/{lm → v0/lm}/vendors/core/__init__.py +0 -0
  319. /synth_ai/{lm → v0/lm}/vendors/core/synth_dev_api.py +0 -0
  320. /synth_ai/{lm → v0/lm}/vendors/local/__init__.py +0 -0
  321. /synth_ai/{lm → v0/lm}/vendors/local/ollama.py +0 -0
  322. /synth_ai/{lm → v0/lm}/vendors/retries.py +0 -0
  323. /synth_ai/{lm → v0/lm}/vendors/supported/__init__.py +0 -0
  324. /synth_ai/{lm → v0/lm}/warmup.py +0 -0
  325. {synth_ai-0.2.9.dev7.dist-info → synth_ai-0.2.9.dev9.dist-info}/WHEEL +0 -0
  326. {synth_ai-0.2.9.dev7.dist-info → synth_ai-0.2.9.dev9.dist-info}/entry_points.txt +0 -0
  327. {synth_ai-0.2.9.dev7.dist-info → synth_ai-0.2.9.dev9.dist-info}/licenses/LICENSE +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,7 +70,7 @@ 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:
@@ -79,7 +80,6 @@ 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)
@@ -132,7 +132,7 @@ def parse_actions(action_text: str) -> List[str]:
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
@@ -154,11 +154,11 @@ 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)
@@ -255,7 +255,7 @@ _FALLBACK_ID_TO_NAME = {
255
255
  }
256
256
 
257
257
 
258
- 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:
259
259
  """Format the semantic map into a text representation using dynamic IDs.
260
260
 
261
261
  Shows a local view around the player with nearby objects.
@@ -280,9 +280,9 @@ def _format_semantic_map_view(obs_data: Dict[str, Any], view_size: int = VIEW_SI
280
280
  use_list = isinstance(_ID_TO_NAME, list) and len(_ID_TO_NAME) > 0
281
281
 
282
282
  # Build matrix centered at player, then transpose for human-friendly view
283
- matrix: List[List[str]] = []
283
+ matrix: list[list[str]] = []
284
284
  for dy in range(-half, half + 1):
285
- row_tokens: List[str] = []
285
+ row_tokens: list[str] = []
286
286
  for dx in range(-half, half + 1):
287
287
  x, y = px + dx, py + dy
288
288
  if not (0 <= x < sem_arr.shape[0] and 0 <= y < sem_arr.shape[1]):
@@ -298,8 +298,8 @@ def _format_semantic_map_view(obs_data: Dict[str, Any], view_size: int = VIEW_SI
298
298
  row_tokens.append(name)
299
299
  matrix.append(row_tokens)
300
300
 
301
- transposed = list(zip(*matrix))
302
- grid_rows: List[str] = [" ".join(row) for row in transposed]
301
+ transposed = list(zip(*matrix, strict=False))
302
+ grid_rows: list[str] = [" ".join(row) for row in transposed]
303
303
  return (
304
304
  "\nLocal Map View (" + str(view_size) + "x" + str(view_size) + "):\n" + "\n".join(grid_rows)
305
305
  )
@@ -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,9 +15,9 @@ 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"
@@ -67,38 +67,39 @@ def create_app(allowed_environments: list[str] = None) -> FastAPI:
67
67
  @app.middleware("http")
68
68
  async def validate_environment(request, call_next):
69
69
  # Check if this is an environment-related request
70
- if request.url.path.startswith("/env/") or request.url.path.startswith("/rollout"):
71
- # Extract environment name from request body for POST requests
72
- if request.method == "POST":
73
- # We need to read the body to check env_name
74
- body = await request.body()
75
- try:
76
- import json
77
-
78
- data = json.loads(body) if body else {}
79
- env_name = data.get("env_name", "").lower()
80
-
81
- # Check if environment is allowed
82
- if env_name and env_name not in [e.lower() for e in allowed_environments]:
83
- from fastapi import HTTPException
84
-
85
- raise HTTPException(
86
- status_code=403,
87
- detail=f"Environment '{env_name}' not allowed. This service only handles: {allowed_environments}",
88
- )
89
- except json.JSONDecodeError:
90
- pass # Invalid JSON, let the endpoint handle it
91
-
92
- # Recreate request with the body we consumed
93
- 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
94
95
 
95
96
  response = await call_next(request)
96
97
  return response
97
98
 
98
99
  # Mount routers
100
+ from .branching import router as branching_router
99
101
  from .environment_routes import router as env_router
100
102
  from .rollout import router as rollout_router
101
- from .branching import router as branching_router
102
103
 
103
104
  app.include_router(env_router, prefix="/env", tags=["environment"])
104
105
 
@@ -109,10 +110,8 @@ def create_app(allowed_environments: list[str] = None) -> FastAPI:
109
110
  app.include_router(policy_router, prefix="/policy", tags=["policy"])
110
111
  except Exception as _e:
111
112
  # Log lightweight message; policy endpoints will be unavailable
112
- try:
113
+ with contextlib.suppress(Exception):
113
114
  print(f"[hosted_app] Skipping policy routes: {_e}", flush=True)
114
- except Exception:
115
- pass
116
115
 
117
116
  app.include_router(rollout_router, tags=["rollout"])
118
117
  app.include_router(branching_router, tags=["branching"])
@@ -148,7 +147,6 @@ def create_app(allowed_environments: list[str] = None) -> FastAPI:
148
147
  - If X-API-Key header is provided and mismatches, returns 401.
149
148
  - Otherwise returns 200 with basic info.
150
149
  """
151
- import os as _os
152
150
 
153
151
  # Check if any environment API keys are configured
154
152
  from synth_ai.task.auth import allowed_environment_api_keys
@@ -190,7 +188,7 @@ def create_app(allowed_environments: list[str] = None) -> FastAPI:
190
188
  try:
191
189
  hdr = request.headers
192
190
  snapshot = {
193
- "path": str(getattr(request, "url").path),
191
+ "path": str(request.url.path),
194
192
  "have_x_api_key": bool(hdr.get("x-api-key")),
195
193
  "have_x_api_keys": bool(hdr.get("x-api-keys")),
196
194
  "have_authorization": bool(hdr.get("authorization")),
@@ -1,8 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import asyncio
4
+ import contextlib
4
5
  import logging
5
- from typing import Any, Dict, Optional
6
+ from typing import Any
6
7
 
7
8
  import httpx
8
9
 
@@ -15,7 +16,7 @@ class OpenAIClient:
15
16
  def __init__(
16
17
  self,
17
18
  base_url: str,
18
- api_key: Optional[str] = None,
19
+ api_key: str | None = None,
19
20
  timeout_s: float = 120.0,
20
21
  ) -> None:
21
22
  self.base_url = base_url.rstrip("/")
@@ -27,8 +28,8 @@ class OpenAIClient:
27
28
  self.headers["Authorization"] = f"Bearer {api_key}"
28
29
 
29
30
  def _fix_model_parameters(
30
- self, request: Dict[str, Any], target_url: Optional[str] = None
31
- ) -> Dict[str, Any]:
31
+ self, request: dict[str, Any], target_url: str | None = None
32
+ ) -> dict[str, Any]:
32
33
  """
33
34
  Fix parameter compatibility for newer OpenAI models.
34
35
 
@@ -91,9 +92,25 @@ class OpenAIClient:
91
92
  try:
92
93
  tools = fixed_request.get("tools")
93
94
  if isinstance(tools, list) and tools:
95
+ # Choose the first provided function name from tools schema (e.g., run_command)
96
+ func_name = None
97
+ for t in tools:
98
+ try:
99
+ cand = None
100
+ if isinstance(t, dict):
101
+ f = t.get("function")
102
+ if isinstance(f, dict):
103
+ cand = f.get("name")
104
+ if isinstance(cand, str) and cand:
105
+ func_name = cand
106
+ break
107
+ except Exception:
108
+ continue
109
+ if not func_name:
110
+ func_name = "run_command"
94
111
  fixed_request["tool_choice"] = {
95
112
  "type": "function",
96
- "function": {"name": "interact_many"},
113
+ "function": {"name": func_name},
97
114
  }
98
115
  fixed_request["parallel_tool_calls"] = False
99
116
  except Exception:
@@ -103,11 +120,11 @@ class OpenAIClient:
103
120
 
104
121
  async def generate(
105
122
  self,
106
- request: Dict[str, Any],
107
- base_url: Optional[str] = None,
108
- timeout_s: Optional[float] = None,
109
- extra_headers: Optional[Dict[str, str]] = None,
110
- ) -> Dict[str, Any]:
123
+ request: dict[str, Any],
124
+ base_url: str | None = None,
125
+ timeout_s: float | None = None,
126
+ extra_headers: dict[str, str] | None = None,
127
+ ) -> dict[str, Any]:
111
128
  """
112
129
  Send a chat completion request to the inference server.
113
130
 
@@ -135,18 +152,15 @@ class OpenAIClient:
135
152
  logger.info(f"Inference POST target: {url}")
136
153
  if extra_headers:
137
154
  logger.info(f"Extra headers: {extra_headers}")
138
- try:
139
- keys_preview = sorted(list(processed_request.keys()))
155
+ with contextlib.suppress(Exception):
156
+ keys_preview = sorted(processed_request.keys())
140
157
  logger.info(f"Request keys: {keys_preview}")
141
- except Exception:
142
- pass
143
158
 
144
159
  # Final hard-guard for OpenAI: ensure unsupported field is not present
145
160
  try:
146
- if "openai" in url.lower():
147
- if "stop_after_tool_calls" in processed_request:
148
- processed_request.pop("stop_after_tool_calls", None)
149
- logger.info("Removed stop_after_tool_calls for OpenAI request")
161
+ if "openai" in url.lower() and "stop_after_tool_calls" in processed_request:
162
+ processed_request.pop("stop_after_tool_calls", None)
163
+ logger.info("Removed stop_after_tool_calls for OpenAI request")
150
164
  # Groq-specific requirement: when using JSON mode, one of the messages must contain the word 'json'
151
165
  low_url = url.lower()
152
166
  if ("groq.com" in low_url or "/openai" in low_url) and isinstance(
@@ -284,7 +298,7 @@ class OpenAIClient:
284
298
  processed_request.pop(k, None)
285
299
  # Force structured tool choice
286
300
  if processed_request.get("tool_choice") == "required":
287
- func_name = "interact_many"
301
+ func_name = "run_command"
288
302
  try:
289
303
  tools_arr = processed_request.get("tools") or []
290
304
  if isinstance(tools_arr, list) and tools_arr:
@@ -367,9 +381,9 @@ class OpenAIClient:
367
381
 
368
382
  async def check_health(
369
383
  self,
370
- base_url: Optional[str] = None,
371
- timeout_s: Optional[float] = None,
372
- ) -> Dict[str, Any]:
384
+ base_url: str | None = None,
385
+ timeout_s: float | None = None,
386
+ ) -> dict[str, Any]:
373
387
  """
374
388
  Check if the inference service is healthy.
375
389
 
@@ -403,13 +417,13 @@ class OpenAIClient:
403
417
 
404
418
  async def generate_with_retries(
405
419
  self,
406
- request: Dict[str, Any],
407
- base_url: Optional[str] = None,
408
- timeout_s: Optional[float] = None,
420
+ request: dict[str, Any],
421
+ base_url: str | None = None,
422
+ timeout_s: float | None = None,
409
423
  max_retries: int = 4,
410
424
  backoff_factor: float = 2.0,
411
- extra_headers: Optional[Dict[str, str]] = None,
412
- ) -> Dict[str, Any]:
425
+ extra_headers: dict[str, str] | None = None,
426
+ ) -> dict[str, Any]:
413
427
  """
414
428
  Generate with exponential backoff retries for transient errors.
415
429
 
@@ -482,7 +496,7 @@ class OpenAIClient:
482
496
  ) from e
483
497
  except Exception:
484
498
  # If we can't parse the response, don't retry 400 errors
485
- try:
499
+ with contextlib.suppress(Exception):
486
500
  logger.error(
487
501
  {
488
502
  "non_overload_400_unparsed": True,
@@ -490,8 +504,6 @@ class OpenAIClient:
490
504
  "payload": processed_request,
491
505
  }
492
506
  )
493
- except Exception:
494
- pass
495
507
  raise RuntimeError(
496
508
  f"Inference 400 response (unparsed): {e.response.text if e.response is not None else 'Bad Request'}"
497
509
  ) from e
@@ -528,7 +540,7 @@ class OpenAIClient:
528
540
 
529
541
  def create_inference_client(
530
542
  task_app: Any,
531
- api_key: Optional[str] = None,
543
+ api_key: str | None = None,
532
544
  ) -> OpenAIClient:
533
545
  """
534
546
  Create an inference client using TaskApp configuration.
@@ -549,6 +561,57 @@ def create_inference_client(
549
561
  except Exception:
550
562
  api_key = None
551
563
 
564
+ import json as _json
565
+ import os as _os
566
+ import time as _time
567
+
568
+ if _os.getenv("SYNTH_FAKE_INFERENCE", "").strip():
569
+
570
+ class _DummyClient:
571
+ async def generate_with_retries(
572
+ self,
573
+ request: dict[str, Any],
574
+ base_url: str | None = None,
575
+ max_retries: int = 0,
576
+ backoff_factor: float = 1.0,
577
+ extra_headers: dict[str, str] | None = None,
578
+ ) -> dict[str, Any]:
579
+ tool_call = {
580
+ "id": "call_dummy",
581
+ "type": "function",
582
+ "function": {
583
+ "name": "interact_many",
584
+ "arguments": _json.dumps({"actions": ["move_right"]}),
585
+ },
586
+ }
587
+ return {
588
+ "id": f"cmpl-{int(_time.time())}",
589
+ "object": "chat.completion",
590
+ "created": int(_time.time()),
591
+ "model": request.get("model") or "dummy-model",
592
+ "choices": [
593
+ {
594
+ "index": 0,
595
+ "message": {
596
+ "role": "assistant",
597
+ "content": "",
598
+ "tool_calls": [tool_call],
599
+ },
600
+ "finish_reason": "tool_calls",
601
+ }
602
+ ],
603
+ "usage": {"prompt_tokens": 10, "completion_tokens": 5, "total_tokens": 15},
604
+ }
605
+
606
+ async def check_health(
607
+ self,
608
+ base_url: str | None = None,
609
+ timeout_s: float | None = None,
610
+ ) -> dict[str, Any]:
611
+ return {"status": "ok", "dummy": True}
612
+
613
+ return _DummyClient()
614
+
552
615
  return OpenAIClient(
553
616
  base_url=task_app.vllm_base_url,
554
617
  api_key=api_key,
@@ -12,7 +12,6 @@ For Modal deployment:
12
12
  from __future__ import annotations
13
13
 
14
14
  import os
15
- from typing import Optional
16
15
 
17
16
  import modal
18
17
 
@@ -26,7 +25,6 @@ except ImportError:
26
25
 
27
26
  from synth_envs_hosted.hosted_app import create_app
28
27
 
29
-
30
28
  # Local development mode
31
29
  if __name__ == "__main__":
32
30
  import uvicorn