synth-ai 0.2.9.dev5__py3-none-any.whl → 0.2.9.dev6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (351) hide show
  1. examples/__init__.py +16 -0
  2. examples/crafter_debug_render.py +23 -17
  3. examples/qwen_coder/README.md +102 -0
  4. examples/qwen_coder/_shared.py +113 -0
  5. examples/qwen_coder/configs/coder_lora_30b.toml +61 -0
  6. examples/qwen_coder/configs/coder_lora_4b.toml +57 -0
  7. examples/qwen_coder/configs/coder_lora_small.toml +58 -0
  8. examples/qwen_coder/generate_dataset.py +98 -0
  9. examples/qwen_coder/infer_ft_smoke.py +64 -0
  10. examples/qwen_coder/infer_prod_proxy.py +73 -0
  11. examples/qwen_coder/infer_via_synth.py +87 -0
  12. examples/qwen_coder/scripts/infer_coder.sh +18 -0
  13. examples/qwen_coder/scripts/train_coder_30b.sh +21 -0
  14. examples/qwen_coder/sft_full_17b.py +103 -0
  15. examples/qwen_coder/sft_lora_30b.py +110 -0
  16. examples/qwen_coder/subset_jsonl.py +38 -0
  17. examples/qwen_coder/validate_jsonl.py +59 -0
  18. examples/rl/configs/eval_base_qwen.toml +1 -1
  19. examples/rl/configs/rl_from_base_qwen17.toml +1 -1
  20. examples/rl/download_dataset.py +26 -10
  21. examples/rl/run_eval.py +53 -52
  22. examples/rl/run_rl_and_save.py +29 -12
  23. examples/rl/task_app/math_single_step.py +180 -41
  24. examples/rl/task_app/math_task_app.py +14 -6
  25. examples/sft/README.md +139 -0
  26. examples/sft/configs/crafter_fft_qwen0p6b.toml +44 -0
  27. examples/sft/configs/crafter_lora_qwen0p6b.toml +45 -0
  28. examples/sft/evaluate.py +117 -0
  29. examples/sft/export_dataset.py +117 -0
  30. examples/sft/generate_traces.py +162 -0
  31. examples/swe/__init__.py +12 -0
  32. examples/swe/task_app/README.md +105 -0
  33. examples/swe/task_app/__init__.py +2 -0
  34. examples/swe/task_app/grpo_swe_mini.py +571 -0
  35. examples/swe/task_app/grpo_swe_mini_task_app.py +136 -0
  36. examples/swe/task_app/hosted/README.md +173 -0
  37. examples/swe/task_app/hosted/__init__.py +5 -0
  38. examples/swe/task_app/hosted/branching.py +143 -0
  39. examples/swe/task_app/hosted/environment_routes.py +1289 -0
  40. examples/swe/task_app/hosted/envs/__init__.py +1 -0
  41. examples/swe/task_app/hosted/envs/crafter/__init__.py +6 -0
  42. examples/swe/task_app/hosted/envs/crafter/app.py +1 -0
  43. examples/swe/task_app/hosted/envs/crafter/environment.py +522 -0
  44. examples/swe/task_app/hosted/envs/crafter/policy.py +478 -0
  45. examples/swe/task_app/hosted/envs/crafter/react_agent.py +108 -0
  46. examples/swe/task_app/hosted/envs/crafter/shared.py +305 -0
  47. examples/swe/task_app/hosted/envs/crafter/tools.py +47 -0
  48. examples/swe/task_app/hosted/envs/mini_swe/__init__.py +8 -0
  49. examples/swe/task_app/hosted/envs/mini_swe/environment.py +1164 -0
  50. examples/swe/task_app/hosted/envs/mini_swe/policy.py +355 -0
  51. examples/swe/task_app/hosted/envs/mini_swe/shared.py +83 -0
  52. examples/swe/task_app/hosted/envs/mini_swe/tools.py +96 -0
  53. examples/swe/task_app/hosted/hosted_app.py +204 -0
  54. examples/swe/task_app/hosted/inference/__init__.py +5 -0
  55. examples/swe/task_app/hosted/inference/openai_client.py +618 -0
  56. examples/swe/task_app/hosted/main.py +100 -0
  57. examples/swe/task_app/hosted/policy_routes.py +1079 -0
  58. examples/swe/task_app/hosted/registry.py +195 -0
  59. examples/swe/task_app/hosted/rollout.py +1869 -0
  60. examples/swe/task_app/hosted/storage/__init__.py +5 -0
  61. examples/swe/task_app/hosted/storage/volume.py +211 -0
  62. examples/swe/task_app/hosted/test_agents.py +161 -0
  63. examples/swe/task_app/hosted/test_service.py +137 -0
  64. examples/swe/task_app/hosted/utils.py +62 -0
  65. examples/vlm/README.md +68 -0
  66. examples/vlm/configs/crafter_vlm_gpt4o.toml +44 -0
  67. examples/vlm/crafter_image_only_agent.py +207 -0
  68. examples/vlm/crafter_openai_vlm_agent.py +277 -0
  69. examples/vlm/filter_image_rows.py +63 -0
  70. examples/vlm/run_crafter_vlm_benchmark.py +316 -0
  71. examples/warming_up_to_rl/analyze_trace_db.py +12 -10
  72. examples/warming_up_to_rl/configs/rl_from_base_qwen4b.toml +11 -1
  73. examples/warming_up_to_rl/export_trace_sft.py +218 -36
  74. examples/warming_up_to_rl/groq_test.py +15 -8
  75. examples/warming_up_to_rl/manage_secrets.py +29 -25
  76. examples/warming_up_to_rl/readme.md +9 -2
  77. examples/warming_up_to_rl/run_eval.py +137 -61
  78. examples/warming_up_to_rl/run_fft_and_save.py +131 -60
  79. examples/warming_up_to_rl/run_local_rollout.py +88 -39
  80. examples/warming_up_to_rl/run_local_rollout_modal.py +114 -28
  81. examples/warming_up_to_rl/run_local_rollout_parallel.py +81 -20
  82. examples/warming_up_to_rl/run_local_rollout_traced.py +126 -23
  83. examples/warming_up_to_rl/run_rl_and_save.py +35 -12
  84. examples/warming_up_to_rl/run_rollout_remote.py +44 -19
  85. examples/warming_up_to_rl/task_app/README.md +6 -2
  86. examples/warming_up_to_rl/task_app/grpo_crafter.py +319 -57
  87. examples/warming_up_to_rl/task_app/grpo_crafter_task_app.py +11 -30
  88. examples/warming_up_to_rl/task_app/synth_envs_hosted/__init__.py +1 -1
  89. examples/warming_up_to_rl/task_app/synth_envs_hosted/branching.py +9 -11
  90. examples/warming_up_to_rl/task_app/synth_envs_hosted/environment_routes.py +137 -182
  91. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/__init__.py +1 -1
  92. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/__init__.py +1 -1
  93. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/app.py +1 -1
  94. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/environment.py +150 -57
  95. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/policy.py +105 -69
  96. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/react_agent.py +19 -7
  97. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/shared.py +45 -42
  98. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/tools.py +1 -1
  99. examples/warming_up_to_rl/task_app/synth_envs_hosted/hosted_app.py +47 -45
  100. examples/warming_up_to_rl/task_app/synth_envs_hosted/inference/__init__.py +1 -1
  101. examples/warming_up_to_rl/task_app/synth_envs_hosted/inference/openai_client.py +198 -92
  102. examples/warming_up_to_rl/task_app/synth_envs_hosted/main.py +0 -2
  103. examples/warming_up_to_rl/task_app/synth_envs_hosted/policy_routes.py +361 -263
  104. examples/warming_up_to_rl/task_app/synth_envs_hosted/registry.py +21 -23
  105. examples/warming_up_to_rl/task_app/synth_envs_hosted/rollout.py +394 -274
  106. examples/warming_up_to_rl/task_app/synth_envs_hosted/storage/__init__.py +1 -1
  107. examples/warming_up_to_rl/task_app/synth_envs_hosted/storage/volume.py +56 -62
  108. examples/warming_up_to_rl/task_app/synth_envs_hosted/test_agents.py +1 -0
  109. examples/warming_up_to_rl/task_app/synth_envs_hosted/test_service.py +6 -15
  110. examples/warming_up_to_rl/task_app/synth_envs_hosted/utils.py +4 -3
  111. synth/__init__.py +14 -0
  112. synth_ai/__init__.py +20 -4
  113. synth_ai/api/models/supported.py +376 -0
  114. synth_ai/api/train/builders.py +157 -26
  115. synth_ai/api/train/cli.py +213 -57
  116. synth_ai/api/train/config_finder.py +65 -5
  117. synth_ai/api/train/env_resolver.py +33 -15
  118. synth_ai/api/train/pollers.py +13 -4
  119. synth_ai/api/train/supported_algos.py +139 -0
  120. synth_ai/api/train/task_app.py +5 -3
  121. synth_ai/api/train/utils.py +33 -48
  122. synth_ai/cli/__init__.py +19 -4
  123. synth_ai/cli/_modal_wrapper.py +28 -0
  124. synth_ai/cli/_typer_patch.py +49 -0
  125. synth_ai/cli/balance.py +2 -3
  126. synth_ai/cli/calc.py +1 -1
  127. synth_ai/cli/demo.py +21 -6
  128. synth_ai/cli/recent.py +2 -2
  129. synth_ai/cli/rl_demo.py +77 -17
  130. synth_ai/cli/root.py +116 -39
  131. synth_ai/cli/status.py +2 -2
  132. synth_ai/cli/task_apps.py +1699 -259
  133. synth_ai/cli/traces.py +7 -4
  134. synth_ai/cli/turso.py +73 -0
  135. synth_ai/cli/watch.py +12 -18
  136. synth_ai/core/experiment.py +0 -2
  137. synth_ai/demo_registry.py +68 -31
  138. synth_ai/demos/core/cli.py +516 -194
  139. synth_ai/demos/demo_task_apps/__init__.py +3 -3
  140. synth_ai/demos/demo_task_apps/core.py +64 -28
  141. synth_ai/demos/demo_task_apps/crafter/configs/crafter_fft_4b.toml +2 -3
  142. synth_ai/demos/demo_task_apps/crafter/grpo_crafter_task_app.py +37 -30
  143. synth_ai/demos/demo_task_apps/math/_common.py +1 -2
  144. synth_ai/demos/demo_task_apps/math/app.py +2 -1
  145. synth_ai/demos/demo_task_apps/math/deploy_modal.py +3 -6
  146. synth_ai/demos/demo_task_apps/math/modal_task_app.py +183 -82
  147. synth_ai/demos/demo_task_apps/math/task_app_entry.py +0 -2
  148. synth_ai/environments/examples/bandit/engine.py +12 -4
  149. synth_ai/environments/examples/bandit/taskset.py +4 -4
  150. synth_ai/environments/examples/crafter_classic/environment.py +76 -1
  151. synth_ai/environments/reproducibility/tree.py +5 -6
  152. synth_ai/environments/service/app.py +11 -12
  153. synth_ai/environments/service/core_routes.py +10 -9
  154. synth_ai/environments/stateful/engine.py +1 -1
  155. synth_ai/environments/tasks/core.py +1 -0
  156. synth_ai/environments/tasks/filters.py +5 -6
  157. synth_ai/environments/tasks/utils.py +4 -5
  158. synth_ai/evals/base.py +0 -2
  159. synth_ai/handshake.py +11 -9
  160. synth_ai/http.py +1 -1
  161. synth_ai/http_client.py +43 -11
  162. synth_ai/inference/__init__.py +0 -2
  163. synth_ai/inference/client.py +20 -6
  164. synth_ai/jobs/client.py +103 -78
  165. synth_ai/learning/__init__.py +41 -6
  166. synth_ai/learning/algorithms.py +14 -0
  167. synth_ai/learning/client.py +121 -29
  168. synth_ai/learning/config.py +2 -40
  169. synth_ai/learning/constants.py +0 -2
  170. synth_ai/learning/ft_client.py +4 -56
  171. synth_ai/learning/health.py +13 -7
  172. synth_ai/learning/jobs.py +43 -47
  173. synth_ai/{rl → learning/rl}/__init__.py +14 -5
  174. synth_ai/learning/rl/client.py +267 -0
  175. synth_ai/learning/rl/config.py +31 -0
  176. synth_ai/{rl → learning/rl}/contracts.py +5 -10
  177. synth_ai/{rl → learning/rl}/env_keys.py +45 -16
  178. synth_ai/learning/rl/secrets.py +13 -0
  179. synth_ai/learning/rl_client.py +2 -253
  180. synth_ai/learning/sft/__init__.py +29 -0
  181. synth_ai/learning/sft/client.py +68 -0
  182. synth_ai/learning/sft/config.py +270 -0
  183. synth_ai/learning/sft/data.py +295 -0
  184. synth_ai/learning/sse.py +25 -26
  185. synth_ai/learning/validators.py +25 -24
  186. synth_ai/lm/__init__.py +21 -47
  187. synth_ai/task/__init__.py +26 -27
  188. synth_ai/task/apps/__init__.py +18 -19
  189. synth_ai/task/auth.py +35 -23
  190. synth_ai/task/client.py +15 -13
  191. synth_ai/task/contracts.py +37 -35
  192. synth_ai/task/datasets.py +9 -6
  193. synth_ai/task/errors.py +11 -10
  194. synth_ai/task/health.py +17 -11
  195. synth_ai/task/json.py +58 -24
  196. synth_ai/task/proxy.py +15 -14
  197. synth_ai/task/rubrics.py +22 -15
  198. synth_ai/task/server.py +43 -17
  199. synth_ai/task/tracing_utils.py +12 -7
  200. synth_ai/task/validators.py +0 -1
  201. synth_ai/task/vendors.py +5 -7
  202. synth_ai/tracing_v3/__init__.py +2 -0
  203. synth_ai/tracing_v3/abstractions.py +21 -4
  204. synth_ai/tracing_v3/db_config.py +26 -1
  205. synth_ai/tracing_v3/decorators.py +18 -15
  206. synth_ai/tracing_v3/examples/basic_usage.py +3 -2
  207. synth_ai/tracing_v3/hooks.py +6 -4
  208. synth_ai/tracing_v3/llm_call_record_helpers.py +6 -6
  209. synth_ai/tracing_v3/replica_sync.py +1 -0
  210. synth_ai/tracing_v3/session_tracer.py +63 -16
  211. synth_ai/tracing_v3/storage/base.py +89 -1
  212. synth_ai/tracing_v3/storage/config.py +21 -8
  213. synth_ai/tracing_v3/storage/factory.py +10 -8
  214. synth_ai/tracing_v3/storage/utils.py +4 -2
  215. synth_ai/tracing_v3/turso/daemon.py +7 -2
  216. synth_ai/tracing_v3/turso/models.py +5 -2
  217. synth_ai/tracing_v3/turso/native_manager.py +1173 -0
  218. synth_ai/tracing_v3/utils.py +4 -3
  219. synth_ai/v0/api/__init__.py +8 -0
  220. synth_ai/v0/api/models/__init__.py +8 -0
  221. synth_ai/v0/api/models/supported.py +8 -0
  222. synth_ai/v0/config/__init__.py +15 -0
  223. synth_ai/v0/config/base_url.py +12 -0
  224. synth_ai/v0/lm/__init__.py +51 -0
  225. synth_ai/{lm → v0/lm}/caching/ephemeral.py +3 -5
  226. synth_ai/{lm → v0/lm}/caching/handler.py +4 -4
  227. synth_ai/{lm → v0/lm}/caching/initialize.py +1 -1
  228. synth_ai/{lm → v0/lm}/caching/persistent.py +1 -1
  229. synth_ai/{lm → v0/lm}/config.py +6 -1
  230. synth_ai/{lm → v0/lm}/core/all.py +9 -9
  231. synth_ai/{lm → v0/lm}/core/exceptions.py +0 -2
  232. synth_ai/{lm → v0/lm}/core/main.py +19 -7
  233. synth_ai/{lm → v0/lm}/core/main_v3.py +10 -10
  234. synth_ai/{lm → v0/lm}/core/synth_models.py +2 -15
  235. synth_ai/{lm → v0/lm}/core/vendor_clients.py +6 -4
  236. synth_ai/{lm → v0/lm}/overrides.py +4 -4
  237. synth_ai/{lm → v0/lm}/provider_support/anthropic.py +4 -4
  238. synth_ai/{lm → v0/lm}/provider_support/openai.py +5 -5
  239. synth_ai/{lm → v0/lm}/structured_outputs/handler.py +5 -5
  240. synth_ai/{lm → v0/lm}/structured_outputs/rehabilitate.py +1 -1
  241. synth_ai/{lm → v0/lm}/vendors/core/anthropic_api.py +16 -16
  242. synth_ai/{lm → v0/lm}/vendors/core/gemini_api.py +5 -5
  243. synth_ai/{lm → v0/lm}/vendors/core/mistral_api.py +5 -5
  244. synth_ai/{lm → v0/lm}/vendors/core/openai_api.py +12 -10
  245. synth_ai/{lm → v0/lm}/vendors/openai_standard.py +11 -9
  246. synth_ai/{lm → v0/lm}/vendors/openai_standard_responses.py +8 -5
  247. synth_ai/{lm → v0/lm}/vendors/supported/custom_endpoint.py +4 -6
  248. synth_ai/{lm → v0/lm}/vendors/supported/deepseek.py +2 -2
  249. synth_ai/{lm → v0/lm}/vendors/supported/grok.py +2 -2
  250. synth_ai/{lm → v0/lm}/vendors/supported/groq.py +1 -1
  251. synth_ai/{lm → v0/lm}/vendors/supported/ollama.py +1 -1
  252. synth_ai/{lm → v0/lm}/vendors/supported/openrouter.py +3 -3
  253. synth_ai/{lm → v0/lm}/vendors/supported/together.py +1 -1
  254. synth_ai/{lm → v0/lm}/vendors/synth_client.py +38 -11
  255. synth_ai/v0/tracing/upload.py +32 -135
  256. synth_ai/v0/tracing_v3/__init__.py +10 -0
  257. synth_ai/v0/tracing_v3/abstractions.py +3 -0
  258. synth_ai/v0/tracing_v3/decorators.py +3 -0
  259. synth_ai/v0/tracing_v3/llm_call_record_helpers.py +3 -0
  260. synth_ai/v0/tracing_v3/session_tracer.py +3 -0
  261. synth_ai-0.2.9.dev6.dist-info/METADATA +191 -0
  262. {synth_ai-0.2.9.dev5.dist-info → synth_ai-0.2.9.dev6.dist-info}/RECORD +291 -262
  263. {synth_ai-0.2.9.dev5.dist-info → synth_ai-0.2.9.dev6.dist-info}/top_level.txt +1 -0
  264. examples/common_old/backend.py +0 -21
  265. examples/evals_old/README.md +0 -98
  266. examples/evals_old/__init__.py +0 -6
  267. examples/evals_old/compare_models.py +0 -1037
  268. examples/evals_old/example_log.md +0 -145
  269. examples/evals_old/run_demo.sh +0 -126
  270. examples/evals_old/trace_analysis.py +0 -270
  271. examples/finetuning_old/_backup_synth_qwen/config.toml +0 -29
  272. examples/finetuning_old/_backup_synth_qwen/example_log.md +0 -324
  273. examples/finetuning_old/_backup_synth_qwen/filter_traces.py +0 -60
  274. examples/finetuning_old/_backup_synth_qwen/filter_traces_achievements.py +0 -239
  275. examples/finetuning_old/_backup_synth_qwen/purge_v3_traces.py +0 -109
  276. examples/finetuning_old/_backup_synth_qwen/react_agent_lm.py +0 -1924
  277. examples/finetuning_old/_backup_synth_qwen/readme.md +0 -49
  278. examples/finetuning_old/_backup_synth_qwen/run_crafter_qwen4b.py +0 -114
  279. examples/finetuning_old/_backup_synth_qwen/run_demo.sh +0 -195
  280. examples/finetuning_old/_backup_synth_qwen/sft_kickoff.py +0 -118
  281. examples/finetuning_old/synth_qwen_v1/README.md +0 -68
  282. examples/finetuning_old/synth_qwen_v1/filter_traces.py +0 -60
  283. examples/finetuning_old/synth_qwen_v1/filter_traces_achievements.py +0 -239
  284. examples/finetuning_old/synth_qwen_v1/finetune.py +0 -46
  285. examples/finetuning_old/synth_qwen_v1/hello_ft_model.py +0 -71
  286. examples/finetuning_old/synth_qwen_v1/infer.py +0 -37
  287. examples/finetuning_old/synth_qwen_v1/poll.py +0 -44
  288. examples/finetuning_old/synth_qwen_v1/prepare_data.py +0 -35
  289. examples/finetuning_old/synth_qwen_v1/purge_v3_traces.py +0 -109
  290. examples/finetuning_old/synth_qwen_v1/react_agent_lm.py +0 -1932
  291. examples/finetuning_old/synth_qwen_v1/run_crafter_sft_job.py +0 -207
  292. examples/finetuning_old/synth_qwen_v1/run_ft_job.py +0 -232
  293. examples/finetuning_old/synth_qwen_v1/upload_data.py +0 -34
  294. examples/finetuning_old/synth_qwen_v1/util.py +0 -147
  295. examples/rl_old/task_app.py +0 -962
  296. examples/warming_up_to_rl/old/event_rewards.md +0 -234
  297. examples/warming_up_to_rl/old/notes.md +0 -73
  298. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/filter_traces_sft_turso.py +0 -738
  299. synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/filter_traces_sft_turso.py +0 -580
  300. synth_ai/experimental/synth_oss.py +0 -446
  301. synth_ai/install_sqld.sh +0 -40
  302. synth_ai/learning/filtering.py +0 -0
  303. synth_ai/learning/offline/dpo.py +0 -0
  304. synth_ai/learning/offline/providers.py +0 -7
  305. synth_ai/learning/offline/sft.py +0 -0
  306. synth_ai/learning/offline/shared.py +0 -0
  307. synth_ai/learning/online/grpo.py +0 -0
  308. synth_ai/learning/online/irft.py +0 -0
  309. synth_ai/learning/prompts/banking77_injection_eval.py +0 -168
  310. synth_ai/learning/prompts/gepa.py +0 -0
  311. synth_ai/learning/prompts/hello_world_in_context_injection_ex.py +0 -213
  312. synth_ai/learning/prompts/mipro.py +0 -289
  313. synth_ai/learning/prompts/random_search.py +0 -246
  314. synth_ai/learning/prompts/run_mipro_banking77.py +0 -172
  315. synth_ai/learning/prompts/run_random_search_banking77.py +0 -324
  316. synth_ai/rl/secrets.py +0 -19
  317. synth_ai/scripts/verify_rewards.py +0 -100
  318. synth_ai/tracing/__init__.py +0 -30
  319. synth_ai/tracing_v1/__init__.py +0 -33
  320. synth_ai/tracing_v3/turso/__init__.py +0 -25
  321. synth_ai/tracing_v3/turso/manager.py +0 -774
  322. synth_ai/zyk/__init__.py +0 -30
  323. synth_ai-0.2.9.dev5.dist-info/METADATA +0 -131
  324. /synth_ai/{lm → v0/lm}/caching/__init__.py +0 -0
  325. /synth_ai/{lm → v0/lm}/caching/constants.py +0 -0
  326. /synth_ai/{lm → v0/lm}/caching/dbs.py +0 -0
  327. /synth_ai/{lm → v0/lm}/constants.py +0 -0
  328. /synth_ai/{lm → v0/lm}/core/__init__.py +0 -0
  329. /synth_ai/{lm → v0/lm}/cost/__init__.py +0 -0
  330. /synth_ai/{lm → v0/lm}/cost/monitor.py +0 -0
  331. /synth_ai/{lm → v0/lm}/cost/statefulness.py +0 -0
  332. /synth_ai/{lm → v0/lm}/injection.py +0 -0
  333. /synth_ai/{lm → v0/lm}/provider_support/__init__.py +0 -0
  334. /synth_ai/{lm → v0/lm}/provider_support/suppress_logging.py +0 -0
  335. /synth_ai/{lm → v0/lm}/structured_outputs/__init__.py +0 -0
  336. /synth_ai/{lm → v0/lm}/structured_outputs/inject.py +0 -0
  337. /synth_ai/{lm → v0/lm}/tools/__init__.py +0 -0
  338. /synth_ai/{lm → v0/lm}/tools/base.py +0 -0
  339. /synth_ai/{lm → v0/lm}/unified_interface.py +0 -0
  340. /synth_ai/{lm → v0/lm}/vendors/__init__.py +0 -0
  341. /synth_ai/{lm → v0/lm}/vendors/base.py +0 -0
  342. /synth_ai/{lm → v0/lm}/vendors/core/__init__.py +0 -0
  343. /synth_ai/{lm → v0/lm}/vendors/core/synth_dev_api.py +0 -0
  344. /synth_ai/{lm → v0/lm}/vendors/local/__init__.py +0 -0
  345. /synth_ai/{lm → v0/lm}/vendors/local/ollama.py +0 -0
  346. /synth_ai/{lm → v0/lm}/vendors/retries.py +0 -0
  347. /synth_ai/{lm → v0/lm}/vendors/supported/__init__.py +0 -0
  348. /synth_ai/{lm → v0/lm}/warmup.py +0 -0
  349. {synth_ai-0.2.9.dev5.dist-info → synth_ai-0.2.9.dev6.dist-info}/WHEEL +0 -0
  350. {synth_ai-0.2.9.dev5.dist-info → synth_ai-0.2.9.dev6.dist-info}/entry_points.txt +0 -0
  351. {synth_ai-0.2.9.dev5.dist-info → synth_ai-0.2.9.dev6.dist-info}/licenses/LICENSE +0 -0
@@ -1 +1 @@
1
- """Environment implementations."""
1
+ """Environment implementations."""
@@ -3,4 +3,4 @@
3
3
  from .environment import CrafterEnvironmentWrapper
4
4
  from .policy import CrafterPolicy
5
5
 
6
- __all__ = ["CrafterEnvironmentWrapper", "CrafterPolicy"]
6
+ __all__ = ["CrafterEnvironmentWrapper", "CrafterPolicy"]
@@ -1 +1 @@
1
- # wraps hosted app
1
+ # wraps hosted app
@@ -1,19 +1,56 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import Any, Dict, List, Optional
3
+ import base64
4
4
  import logging
5
+ from io import BytesIO
6
+ from typing import Any
5
7
 
6
- from synth_ai.environments.stateful.core import StatefulEnvironment
8
+ import numpy as np
9
+ from PIL import Image
7
10
  from synth_ai.environments.environment.tools import EnvToolCall
11
+ from synth_ai.environments.stateful.core import StatefulEnvironment
8
12
 
9
13
  from ...utils import convert_numpy_to_python
10
- from .tools import TOOLS_SCHEMA
11
14
  from .shared import CRAFTER_ACTIONS, _format_semantic_map_view
12
-
15
+ from .tools import TOOLS_SCHEMA
13
16
 
14
17
  logger = logging.getLogger(__name__)
15
18
 
16
19
 
20
+ def _encode_image_to_base64(image_array: Any) -> dict[str, Any] | None:
21
+ """Encode an RGB ndarray into a base64 PNG payload with metadata."""
22
+
23
+ if not isinstance(image_array, np.ndarray):
24
+ return None
25
+ if image_array.ndim != 3 or image_array.shape[-1] not in (1, 3, 4):
26
+ return None
27
+ try:
28
+ # Ensure uint8 for PIL compatibility
29
+ array_uint8 = (
30
+ image_array.astype("uint8")
31
+ if image_array.dtype != np.uint8
32
+ else image_array # pragma: no cover - fast path
33
+ )
34
+ mode = "L" if array_uint8.shape[-1] == 1 else "RGB"
35
+ if array_uint8.shape[-1] == 4:
36
+ mode = "RGBA"
37
+ img = Image.fromarray(array_uint8, mode=mode)
38
+ buffer = BytesIO()
39
+ img.save(buffer, format="PNG")
40
+ encoded = base64.b64encode(buffer.getvalue()).decode("ascii")
41
+ width = int(array_uint8.shape[1])
42
+ height = int(array_uint8.shape[0])
43
+ return {
44
+ "format": "png",
45
+ "width": width,
46
+ "height": height,
47
+ "data": encoded,
48
+ "data_url": f"data:image/png;base64,{encoded}",
49
+ }
50
+ except Exception:
51
+ return None
52
+
53
+
17
54
  class CrafterEnvironmentWrapper:
18
55
  """Host-side environment wrapper matching the sketch contract.
19
56
 
@@ -25,20 +62,20 @@ class CrafterEnvironmentWrapper:
25
62
  - snapshot()/restore() handled at route level; this wrapper exposes checkpoint via synth-ai
26
63
  """
27
64
 
28
- def __init__(self, env: StatefulEnvironment, seed: Optional[int] = None) -> None:
65
+ def __init__(self, env: StatefulEnvironment, seed: int | None = None) -> None:
29
66
  self.env = env
30
67
  self.seed = seed
31
68
  self.step_idx = 0
32
- self.last_observation: Optional[Dict[str, Any]] = None
33
- self.last_info: Optional[Dict[str, Any]] = None
69
+ self.last_observation: dict[str, Any] | None = None
70
+ self.last_info: dict[str, Any] | None = None
34
71
 
35
- async def initialize(self) -> Dict[str, Any]:
72
+ async def initialize(self) -> dict[str, Any]:
36
73
  obs = await self.env.initialize()
37
74
  # synth-ai InternalObservation expected to expose .observation (dict-like)
38
75
  self.step_idx = 0
39
76
  self.last_observation = getattr(obs, "observation", obs) # tolerate dict-like
40
77
  self.last_info = getattr(obs, "info", None)
41
- out_obs: Dict[str, Any] = convert_numpy_to_python(self.last_observation) or {}
78
+ out_obs = self._prepare_observation(self.last_observation)
42
79
  # Attach a 7x7 semantic map patch centered on player for client-side rendering
43
80
  try:
44
81
  pub = self.env.engine._get_public_state_from_env() # type: ignore[attr-defined]
@@ -47,13 +84,13 @@ class CrafterEnvironmentWrapper:
47
84
  size = 7
48
85
  half = size // 2
49
86
  patch = []
50
- H = len(sem) if hasattr(sem, "__len__") else 0
51
- W = len(sem[0]) if H and hasattr(sem[0], "__len__") else 0
87
+ height = len(sem) if hasattr(sem, "__len__") else 0
88
+ width = len(sem[0]) if height and hasattr(sem[0], "__len__") else 0
52
89
  for dy in range(-half, half + 1):
53
90
  row = []
54
91
  for dx in range(-half, half + 1):
55
92
  x, y = int(px) + dx, int(py) + dy
56
- if 0 <= x < H and 0 <= y < W:
93
+ if 0 <= x < height and 0 <= y < width:
57
94
  row.append(int(sem[x][y]))
58
95
  else:
59
96
  row.append(0)
@@ -68,7 +105,7 @@ class CrafterEnvironmentWrapper:
68
105
  "step_idx": self.step_idx,
69
106
  }
70
107
 
71
- async def step(self, tool_calls: List[Dict[str, Any]] | List[EnvToolCall]) -> Dict[str, Any]:
108
+ async def step(self, tool_calls: list[dict[str, Any]] | list[EnvToolCall]) -> dict[str, Any]:
72
109
  # Normalize JSON tool_calls into EnvToolCall instances if needed
73
110
  # Underlying synth-ai environment expects only tool="interact" with args={"action": <action_name>}.
74
111
  # LLM may emit:
@@ -79,9 +116,9 @@ class CrafterEnvironmentWrapper:
79
116
  allowed_actions = set(
80
117
  TOOLS_SCHEMA[0]["function"]["parameters"]["properties"]["actions"]["items"]["enum"]
81
118
  )
82
- normalized: List[EnvToolCall] = []
119
+ normalized: list[EnvToolCall] = []
83
120
 
84
- def _action_to_int(action: Any) -> Optional[int]:
121
+ def _action_to_int(action: Any) -> int | None:
85
122
  # Handle invalid actions gracefully instead of failing
86
123
  if isinstance(action, int):
87
124
  return action
@@ -90,6 +127,7 @@ class CrafterEnvironmentWrapper:
90
127
  logger.warning("Unknown Crafter action: %s - ignoring", action_str)
91
128
  return None # Signal to skip this action
92
129
  return CRAFTER_ACTIONS[action_str]
130
+
93
131
  for tc in tool_calls:
94
132
  if isinstance(tc, EnvToolCall):
95
133
  # Expand interact_many; otherwise coerce non-interact tools into interact(action=tool)
@@ -103,12 +141,12 @@ class CrafterEnvironmentWrapper:
103
141
  )
104
142
  elif tc.tool != "interact":
105
143
  candidate_action = tc.args.get("action") if isinstance(tc.args, dict) else None
106
- resolved_action = candidate_action if candidate_action in allowed_actions else tc.tool
144
+ resolved_action = (
145
+ candidate_action if candidate_action in allowed_actions else tc.tool
146
+ )
107
147
  action_int = _action_to_int(resolved_action)
108
148
  if action_int is not None: # Skip invalid actions
109
- normalized.append(
110
- EnvToolCall(tool="interact", args={"action": action_int})
111
- )
149
+ normalized.append(EnvToolCall(tool="interact", args={"action": action_int}))
112
150
  else:
113
151
  normalized.append(tc)
114
152
  else:
@@ -120,13 +158,14 @@ class CrafterEnvironmentWrapper:
120
158
  args = tc.get("arguments") or tc.get("args") or {}
121
159
  if isinstance(args, str):
122
160
  import json as _json
161
+
123
162
  try:
124
163
  args = _json.loads(args)
125
164
  except Exception:
126
165
  args = {}
127
166
  # Expand interact_many into multiple interacts
128
167
  if tool_name == "interact_many":
129
- for action in (args.get("actions") or []):
168
+ for action in args.get("actions") or []:
130
169
  action_int = _action_to_int(action)
131
170
  if action_int is not None: # Skip invalid actions
132
171
  normalized.append(
@@ -135,26 +174,35 @@ class CrafterEnvironmentWrapper:
135
174
  else:
136
175
  # For any non-interact tool, resolve to an interact action.
137
176
  # Support a packed list of actions under 'actions' for convenience.
138
- if isinstance(args, dict) and isinstance(args.get("actions"), list) and args.get("actions"):
177
+ if (
178
+ isinstance(args, dict)
179
+ and isinstance(args.get("actions"), list)
180
+ and args.get("actions")
181
+ ):
139
182
  for action in args.get("actions"):
140
183
  action_int = _action_to_int(action)
141
184
  if action_int is not None:
142
- normalized.append(EnvToolCall(tool="interact", args={"action": action_int}))
185
+ normalized.append(
186
+ EnvToolCall(tool="interact", args={"action": action_int})
187
+ )
143
188
  else:
144
189
  candidate_action = None
145
190
  if isinstance(args, dict) and "action" in args:
146
191
  candidate_action = args["action"]
147
192
  # If the caller provided a numeric action id, accept it directly
148
- action_int: Optional[int]
149
- if isinstance(candidate_action, int):
150
- action_int = _action_to_int(candidate_action)
151
- elif isinstance(candidate_action, str) and candidate_action in allowed_actions:
193
+ action_int: int | None
194
+ if isinstance(candidate_action, int) or (
195
+ isinstance(candidate_action, str)
196
+ and candidate_action in allowed_actions
197
+ ):
152
198
  action_int = _action_to_int(candidate_action)
153
199
  else:
154
200
  # Fallback: interpret the tool name itself as the action label
155
201
  action_int = _action_to_int(tool_name)
156
202
  if action_int is not None:
157
- normalized.append(EnvToolCall(tool="interact", args={"action": action_int}))
203
+ normalized.append(
204
+ EnvToolCall(tool="interact", args={"action": action_int})
205
+ )
158
206
 
159
207
  # Ensure we have at least one valid action; default to noop if none provided
160
208
  if not normalized:
@@ -162,7 +210,7 @@ class CrafterEnvironmentWrapper:
162
210
  normalized.append(EnvToolCall(tool="interact", args={"action": 0})) # noop action
163
211
 
164
212
  # Pre-step logging: capture current public state and print concise summary
165
- before_state: Optional[Dict[str, Any]] = None
213
+ before_state: dict[str, Any] | None = None
166
214
  try:
167
215
  pub_before = self.env.engine._get_public_state_from_env() # type: ignore[attr-defined]
168
216
  before_state = {
@@ -173,7 +221,9 @@ class CrafterEnvironmentWrapper:
173
221
  "semantic_map": pub_before.semantic_map,
174
222
  }
175
223
  actions_printable = [
176
- (tc.args.get("action") if isinstance(tc.args, dict) else None) if isinstance(tc, EnvToolCall) else None
224
+ (tc.args.get("action") if isinstance(tc.args, dict) else None)
225
+ if isinstance(tc, EnvToolCall)
226
+ else None
177
227
  for tc in normalized
178
228
  ]
179
229
  logger.info(
@@ -185,7 +235,11 @@ class CrafterEnvironmentWrapper:
185
235
  [k for k, v in before_state["achievements_status"].items() if v],
186
236
  actions_printable,
187
237
  )
188
- logger.info("Surroundings BEFORE (seed=%s):\n%s", str(self.seed), _format_semantic_map_view(before_state))
238
+ logger.info(
239
+ "Surroundings BEFORE (seed=%s):\n%s",
240
+ str(self.seed),
241
+ _format_semantic_map_view(before_state),
242
+ )
189
243
  except Exception as _:
190
244
  # Logging should not interfere with stepping; fail-fast elsewhere
191
245
  pass
@@ -212,7 +266,7 @@ class CrafterEnvironmentWrapper:
212
266
  ach_added_latest: list[str] | None = None
213
267
  try:
214
268
  pub_after = self.env.engine._get_public_state_from_env() # type: ignore[attr-defined]
215
- after_dict: Dict[str, Any] = {
269
+ after_dict: dict[str, Any] = {
216
270
  "inventory": pub_after.inventory,
217
271
  "achievements_status": pub_after.achievements_status,
218
272
  "player_position": list(pub_after.player_position),
@@ -236,8 +290,8 @@ class CrafterEnvironmentWrapper:
236
290
  # Position delta
237
291
  pb = before_state.get("player_position", [0, 0])
238
292
  pa = after_dict.get("player_position", [0, 0])
239
- pb_t = (int(pb[0]), int(pb[1])) if isinstance(pb, (list, tuple)) else (0, 0)
240
- pa_t = (int(pa[0]), int(pa[1])) if isinstance(pa, (list, tuple)) else (0, 0)
293
+ pb_t = (int(pb[0]), int(pb[1])) if isinstance(pb, list | tuple) else (0, 0)
294
+ pa_t = (int(pa[0]), int(pa[1])) if isinstance(pa, list | tuple) else (0, 0)
241
295
  delta = (pa_t[0] - pb_t[0], pa_t[1] - pb_t[1])
242
296
 
243
297
  # Inventory changes
@@ -253,11 +307,17 @@ class CrafterEnvironmentWrapper:
253
307
  inv_changes = ", ".join(changed_items) if changed_items else "none"
254
308
 
255
309
  # Achievements gained/lost
256
- ach_b = {k for k, v in (before_state.get("achievements_status", {}) or {}).items() if v}
257
- ach_a = {k for k, v in (after_dict.get("achievements_status", {}) or {}).items() if v}
258
- ach_added = sorted(list(ach_a - ach_b))
310
+ ach_b = {
311
+ k
312
+ for k, v in (before_state.get("achievements_status", {}) or {}).items()
313
+ if v
314
+ }
315
+ ach_a = {
316
+ k for k, v in (after_dict.get("achievements_status", {}) or {}).items() if v
317
+ }
318
+ ach_added = sorted(ach_a - ach_b)
259
319
  ach_added_latest = ach_added
260
- ach_removed = sorted(list(ach_b - ach_a))
320
+ ach_removed = sorted(ach_b - ach_a)
261
321
 
262
322
  logger.info(
263
323
  "Changes: pos %s->%s Δ=%s | inv %s | ach +%s -%s",
@@ -272,16 +332,23 @@ class CrafterEnvironmentWrapper:
272
332
  if reward is None and ach_added_latest:
273
333
  try:
274
334
  reward = float(len(ach_added_latest))
275
- logger.info("Reward shaping applied: +%s (achievements added)", len(ach_added_latest))
335
+ logger.info(
336
+ "Reward shaping applied: +%s (achievements added)",
337
+ len(ach_added_latest),
338
+ )
276
339
  except Exception:
277
340
  pass
278
341
  except Exception:
279
342
  pass
280
- logger.info("Surroundings AFTER (seed=%s):\n%s", str(self.seed), _format_semantic_map_view(after_dict))
343
+ logger.info(
344
+ "Surroundings AFTER (seed=%s):\n%s",
345
+ str(self.seed),
346
+ _format_semantic_map_view(after_dict),
347
+ )
281
348
  except Exception as _:
282
349
  pass
283
- result: Dict[str, Any] = {
284
- "observation": convert_numpy_to_python(observation),
350
+ result: dict[str, Any] = {
351
+ "observation": self._prepare_observation(observation),
285
352
  "step_idx": self.step_idx,
286
353
  "done": bool(done) if done is not None else False, # Ensure boolean
287
354
  }
@@ -293,13 +360,13 @@ class CrafterEnvironmentWrapper:
293
360
  size = 7
294
361
  half = size // 2
295
362
  patch = []
296
- H = len(sem) if hasattr(sem, "__len__") else 0
297
- W = len(sem[0]) if H and hasattr(sem[0], "__len__") else 0
363
+ height = len(sem) if hasattr(sem, "__len__") else 0
364
+ width = len(sem[0]) if height and hasattr(sem[0], "__len__") else 0
298
365
  for dy in range(-half, half + 1):
299
366
  row = []
300
367
  for dx in range(-half, half + 1):
301
368
  x, y = px + dx, py + dy
302
- if 0 <= x < H and 0 <= y < W:
369
+ if 0 <= x < height and 0 <= y < width:
303
370
  row.append(int(sem[x][y]))
304
371
  else:
305
372
  row.append(0)
@@ -309,10 +376,7 @@ class CrafterEnvironmentWrapper:
309
376
  obs_out["semantic_map_patch7"] = patch
310
377
  except Exception:
311
378
  pass
312
- if info is not None:
313
- result_info = convert_numpy_to_python(info)
314
- else:
315
- result_info = {}
379
+ result_info = convert_numpy_to_python(info) if info is not None else {}
316
380
  # Attach achievements delta for downstream metrics if useful
317
381
  if ach_added_latest is not None:
318
382
  try:
@@ -340,6 +404,7 @@ class CrafterEnvironmentWrapper:
340
404
  # Build reverse action map for readability
341
405
  int_to_action = {v: k for k, v in CRAFTER_ACTIONS.items()}
342
406
  from collections import Counter
407
+
343
408
  action_ids = []
344
409
  for tc in normalized:
345
410
  if isinstance(tc, EnvToolCall) and isinstance(tc.args, dict):
@@ -371,29 +436,57 @@ class CrafterEnvironmentWrapper:
371
436
  )
372
437
  except Exception:
373
438
  pass
439
+
374
440
  return result
375
441
 
376
- async def checkpoint(self) -> Dict[str, Any]:
442
+ def _prepare_observation(self, observation: Any) -> dict[str, Any]:
443
+ """Convert raw observation into a JSON-serializable dict with encoded image."""
444
+
445
+ obs_dict: dict[str, Any]
446
+ image_payload: dict[str, Any] | None = None
447
+
448
+ if isinstance(observation, dict):
449
+ image_payload = _encode_image_to_base64(observation.get("observation_image"))
450
+ # Work on a shallow copy to avoid mutating engine state
451
+ sanitized = dict(observation)
452
+ sanitized.pop("observation_image", None)
453
+ obs_dict = convert_numpy_to_python(sanitized) or {}
454
+ else:
455
+ obs_dict = convert_numpy_to_python(observation) or {}
456
+
457
+ if not isinstance(obs_dict, dict):
458
+ obs_dict = {"value": obs_dict}
459
+
460
+ if image_payload:
461
+ obs_dict["observation_image_base64"] = image_payload["data"]
462
+ obs_dict["observation_image_format"] = image_payload["format"]
463
+ obs_dict["observation_image_width"] = image_payload["width"]
464
+ obs_dict["observation_image_height"] = image_payload["height"]
465
+ obs_dict["observation_image_data_url"] = image_payload["data_url"]
466
+
467
+ return obs_dict
468
+
469
+ async def checkpoint(self) -> dict[str, Any]:
377
470
  obs = await self.env.checkpoint()
378
471
  observation = getattr(obs, "observation", obs)
379
472
  info = getattr(obs, "info", None)
380
473
  return {
381
474
  "observation": convert_numpy_to_python(observation),
382
475
  "info": convert_numpy_to_python(info) if info else None,
383
- "step_idx": self.step_idx
476
+ "step_idx": self.step_idx,
384
477
  }
385
478
 
386
- async def terminate(self) -> Dict[str, Any]:
479
+ async def terminate(self) -> dict[str, Any]:
387
480
  obs = await self.env.terminate()
388
481
  observation = getattr(obs, "observation", obs)
389
482
  info = getattr(obs, "info", None)
390
483
  return {
391
484
  "observation": convert_numpy_to_python(observation),
392
485
  "info": convert_numpy_to_python(info) if info else None,
393
- "step_idx": self.step_idx
486
+ "step_idx": self.step_idx,
394
487
  }
395
488
 
396
- def state_dict(self) -> Dict[str, Any]:
489
+ def state_dict(self) -> dict[str, Any]:
397
490
  return {
398
491
  "seed": self.seed,
399
492
  "step_idx": self.step_idx,
@@ -401,13 +494,13 @@ class CrafterEnvironmentWrapper:
401
494
  "last_info": self.last_info,
402
495
  }
403
496
 
404
- def load_state_dict(self, state: Dict[str, Any]) -> None:
497
+ def load_state_dict(self, state: dict[str, Any]) -> None:
405
498
  self.seed = state["seed"]
406
499
  self.step_idx = int(state["step_idx"])
407
500
  self.last_observation = state["last_observation"]
408
501
  self.last_info = state["last_info"]
409
502
 
410
- async def serialize(self) -> Dict[str, Any]:
503
+ async def serialize(self) -> dict[str, Any]:
411
504
  return {
412
505
  "name": "crafter",
413
506
  "config": {"seed": self.seed},
@@ -417,9 +510,9 @@ class CrafterEnvironmentWrapper:
417
510
  @classmethod
418
511
  async def deserialize(
419
512
  cls,
420
- payload: Dict[str, Any],
513
+ payload: dict[str, Any],
421
514
  env: StatefulEnvironment,
422
- ) -> "CrafterEnvironmentWrapper":
515
+ ) -> CrafterEnvironmentWrapper:
423
516
  seed = payload["config"]["seed"]
424
517
  wrapper = cls(env=env, seed=seed)
425
518
  wrapper.load_state_dict(payload["state"])