synth-ai 0.2.14__py3-none-any.whl → 0.2.17__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 (354) hide show
  1. examples/README.md +1 -0
  2. examples/analyze_semantic_words.sh +2 -2
  3. examples/blog_posts/pokemon_vl/README.md +98 -0
  4. examples/blog_posts/pokemon_vl/configs/eval_qwen3_vl.toml +25 -0
  5. examples/blog_posts/pokemon_vl/configs/eval_rl_final.toml +24 -0
  6. examples/blog_posts/pokemon_vl/configs/filter_high_reward.toml +10 -0
  7. examples/blog_posts/pokemon_vl/configs/train_rl_from_sft.toml +42 -0
  8. examples/blog_posts/pokemon_vl/configs/train_sft_qwen4b_vl.toml +40 -0
  9. examples/blog_posts/warming_up_to_rl/README.md +158 -0
  10. examples/blog_posts/warming_up_to_rl/configs/eval_ft_qwen4b.toml +25 -0
  11. examples/blog_posts/warming_up_to_rl/configs/eval_groq_qwen32b.toml +25 -0
  12. examples/blog_posts/warming_up_to_rl/configs/eval_openai_gpt_oss_120b.toml +29 -0
  13. examples/blog_posts/warming_up_to_rl/configs/filter_high_reward_dataset.toml +10 -0
  14. examples/blog_posts/warming_up_to_rl/configs/train_rl_from_sft.toml +41 -0
  15. examples/blog_posts/warming_up_to_rl/configs/train_sft_qwen4b.toml +40 -0
  16. examples/dev/qwen3_32b_qlora_4xh100.toml +5 -0
  17. examples/multi_step/SFT_README.md +147 -0
  18. examples/multi_step/configs/crafter_rl_outcome.toml +1 -1
  19. examples/multi_step/configs/crafter_rl_stepwise_hosted_judge.toml +73 -115
  20. examples/multi_step/configs/crafter_rl_stepwise_shaped.toml +1 -1
  21. examples/multi_step/configs/crafter_rl_stepwise_simple.toml +1 -1
  22. examples/multi_step/configs/crafter_rl_stepwise_simple_NEW_FORMAT.toml +105 -0
  23. examples/multi_step/configs/crafter_sft_qwen30b_lora.toml +62 -0
  24. examples/multi_step/configs/verilog_rl_lora.toml +80 -123
  25. examples/multi_step/convert_traces_to_sft.py +84 -0
  26. examples/multi_step/run_sft_qwen30b.sh +45 -0
  27. examples/qwen_coder/configs/coder_lora_30b.toml +1 -2
  28. examples/qwen_coder/configs/coder_lora_4b.toml +5 -1
  29. examples/qwen_coder/configs/coder_lora_small.toml +1 -2
  30. examples/qwen_vl/BUGS_AND_FIXES.md +232 -0
  31. examples/qwen_vl/IMAGE_VALIDATION_COMPLETE.md +271 -0
  32. examples/qwen_vl/IMAGE_VALIDATION_SUMMARY.md +260 -0
  33. examples/qwen_vl/INFERENCE_SFT_TESTS.md +412 -0
  34. examples/qwen_vl/NEXT_STEPS_2B.md +325 -0
  35. examples/qwen_vl/QUICKSTART.md +327 -0
  36. examples/qwen_vl/QUICKSTART_RL_VISION.md +110 -0
  37. examples/qwen_vl/README.md +152 -0
  38. examples/qwen_vl/RL_VISION_COMPLETE.md +475 -0
  39. examples/qwen_vl/RL_VISION_TESTING.md +333 -0
  40. examples/qwen_vl/SDK_VISION_INTEGRATION.md +328 -0
  41. examples/qwen_vl/SETUP_COMPLETE.md +274 -0
  42. examples/qwen_vl/VISION_TESTS_COMPLETE.md +489 -0
  43. examples/qwen_vl/VLM_PIPELINE_COMPLETE.md +242 -0
  44. examples/qwen_vl/__init__.py +2 -0
  45. examples/qwen_vl/collect_data_via_cli.md +415 -0
  46. examples/qwen_vl/collect_vision_traces.py +368 -0
  47. examples/qwen_vl/configs/crafter_rl_vision_qwen3vl4b.toml +110 -0
  48. examples/qwen_vl/configs/crafter_vlm_sft_example.toml +59 -0
  49. examples/qwen_vl/configs/eval_gpt4o_mini_vision.toml +26 -0
  50. examples/qwen_vl/configs/eval_gpt4o_vision_proper.toml +29 -0
  51. examples/qwen_vl/configs/eval_gpt5nano_vision.toml +26 -0
  52. examples/qwen_vl/configs/eval_qwen3vl_vision.toml +26 -0
  53. examples/qwen_vl/configs/filter_qwen3vl_sft.toml +49 -0
  54. examples/qwen_vl/configs/filter_vision_sft.toml +52 -0
  55. examples/qwen_vl/configs/filter_vision_test.toml +8 -0
  56. examples/qwen_vl/configs/sft_qwen3_vl_2b_test.toml +54 -0
  57. examples/qwen_vl/crafter_gpt5nano_agent.py +308 -0
  58. examples/qwen_vl/crafter_qwen_vl_agent.py +300 -0
  59. examples/qwen_vl/run_vision_comparison.sh +61 -0
  60. examples/qwen_vl/run_vision_sft_pipeline.sh +175 -0
  61. examples/qwen_vl/test_image_validation.py +201 -0
  62. examples/qwen_vl/test_sft_vision_data.py +110 -0
  63. examples/rl/README.md +6 -6
  64. examples/rl/configs/eval_base_qwen.toml +17 -0
  65. examples/rl/configs/eval_rl_qwen.toml +13 -0
  66. examples/rl/configs/rl_from_base_qwen.toml +62 -0
  67. examples/rl/configs/rl_from_base_qwen17.toml +79 -0
  68. examples/rl/configs/rl_from_ft_qwen.toml +37 -0
  69. examples/rl/run_eval.py +436 -0
  70. examples/rl/run_rl_and_save.py +111 -0
  71. examples/rl/task_app/README.md +21 -0
  72. examples/rl/task_app/math_single_step.py +990 -0
  73. examples/rl/task_app/math_task_app.py +111 -0
  74. examples/run_crafter_demo.sh +2 -2
  75. examples/sft/README.md +6 -6
  76. examples/sft/configs/crafter_fft_qwen0p6b.toml +7 -2
  77. examples/sft/configs/crafter_lora_qwen0p6b.toml +7 -3
  78. examples/sft/evaluate.py +2 -4
  79. examples/sft/export_dataset.py +7 -4
  80. examples/swe/task_app/README.md +33 -3
  81. examples/swe/task_app/grpo_swe_mini.py +4 -1
  82. examples/swe/task_app/grpo_swe_mini_task_app.py +0 -12
  83. examples/swe/task_app/hosted/envs/crafter/react_agent.py +1 -1
  84. examples/swe/task_app/hosted/envs/mini_swe/environment.py +50 -23
  85. examples/swe/task_app/hosted/inference/openai_client.py +4 -4
  86. examples/swe/task_app/hosted/policy_routes.py +0 -2
  87. examples/swe/task_app/hosted/rollout.py +0 -8
  88. examples/swe/task_app/morph_backend.py +178 -0
  89. examples/task_apps/crafter/task_app/README.md +1 -1
  90. examples/task_apps/crafter/task_app/grpo_crafter.py +70 -10
  91. examples/task_apps/crafter/task_app/grpo_crafter_task_app.py +1 -1
  92. examples/task_apps/crafter/task_app/synth_envs_hosted/envs/crafter/policy.py +63 -27
  93. examples/task_apps/crafter/task_app/synth_envs_hosted/envs/crafter/react_agent.py +1 -2
  94. examples/task_apps/crafter/task_app/synth_envs_hosted/inference/openai_client.py +48 -50
  95. examples/task_apps/crafter/task_app/synth_envs_hosted/policy_routes.py +75 -36
  96. examples/task_apps/crafter/task_app/synth_envs_hosted/rollout.py +31 -15
  97. examples/task_apps/enron/__init__.py +1 -0
  98. examples/task_apps/enron/task_app/grpo_enron_task_app.py +1 -1
  99. examples/task_apps/math/README.md +1 -2
  100. examples/task_apps/pokemon_red/README.md +3 -4
  101. examples/task_apps/pokemon_red/eval_image_only_gpt4o.toml +6 -5
  102. examples/task_apps/pokemon_red/eval_pokemon_red_policy.py +1 -2
  103. examples/task_apps/pokemon_red/task_app.py +36 -5
  104. examples/task_apps/sokoban/README.md +2 -3
  105. examples/task_apps/verilog/eval_groq_qwen32b.toml +12 -14
  106. examples/task_apps/verilog/task_app/grpo_verilog_task_app.py +1 -1
  107. examples/vlm/README.md +3 -3
  108. examples/vlm/configs/crafter_vlm_gpt4o.toml +5 -0
  109. examples/vlm/crafter_openai_vlm_agent.py +3 -5
  110. examples/vlm/filter_image_rows.py +1 -1
  111. examples/vlm/run_crafter_vlm_benchmark.py +2 -2
  112. examples/warming_up_to_rl/_utils.py +92 -0
  113. examples/warming_up_to_rl/analyze_trace_db.py +1 -1
  114. examples/warming_up_to_rl/configs/crafter_fft.toml +5 -0
  115. examples/warming_up_to_rl/configs/eval_fft_qwen4b.toml +2 -0
  116. examples/warming_up_to_rl/configs/eval_groq_qwen32b.toml +2 -0
  117. examples/warming_up_to_rl/configs/eval_modal_qwen4b.toml +2 -1
  118. examples/warming_up_to_rl/configs/rl_from_base_qwen4b.toml +2 -1
  119. examples/warming_up_to_rl/configs/rl_from_ft.toml +2 -0
  120. examples/warming_up_to_rl/export_trace_sft.py +174 -60
  121. examples/warming_up_to_rl/readme.md +63 -132
  122. examples/warming_up_to_rl/run_fft_and_save.py +1 -1
  123. examples/warming_up_to_rl/run_local_rollout_traced.py +1 -1
  124. examples/warming_up_to_rl/run_rl_and_save.py +1 -1
  125. examples/warming_up_to_rl/task_app/README.md +42 -0
  126. examples/warming_up_to_rl/task_app/grpo_crafter.py +827 -0
  127. examples/warming_up_to_rl/task_app/grpo_crafter_task_app.py +135 -0
  128. examples/warming_up_to_rl/task_app/synth_envs_hosted/README.md +173 -0
  129. examples/warming_up_to_rl/task_app/synth_envs_hosted/__init__.py +5 -0
  130. examples/warming_up_to_rl/task_app/synth_envs_hosted/branching.py +143 -0
  131. examples/warming_up_to_rl/task_app/synth_envs_hosted/environment_routes.py +1226 -0
  132. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/__init__.py +1 -0
  133. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/__init__.py +6 -0
  134. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/app.py +1 -0
  135. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/environment.py +522 -0
  136. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/policy.py +454 -0
  137. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/react_agent.py +108 -0
  138. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/shared.py +305 -0
  139. examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/tools.py +47 -0
  140. examples/warming_up_to_rl/task_app/synth_envs_hosted/hosted_app.py +204 -0
  141. examples/warming_up_to_rl/task_app/synth_envs_hosted/inference/__init__.py +5 -0
  142. examples/warming_up_to_rl/task_app/synth_envs_hosted/inference/openai_client.py +618 -0
  143. examples/warming_up_to_rl/task_app/synth_envs_hosted/main.py +100 -0
  144. examples/warming_up_to_rl/task_app/synth_envs_hosted/policy_routes.py +1084 -0
  145. examples/warming_up_to_rl/task_app/synth_envs_hosted/registry.py +195 -0
  146. examples/warming_up_to_rl/task_app/synth_envs_hosted/rollout.py +1861 -0
  147. examples/warming_up_to_rl/task_app/synth_envs_hosted/storage/__init__.py +5 -0
  148. examples/warming_up_to_rl/task_app/synth_envs_hosted/storage/volume.py +211 -0
  149. examples/warming_up_to_rl/task_app/synth_envs_hosted/test_agents.py +161 -0
  150. examples/warming_up_to_rl/task_app/synth_envs_hosted/test_service.py +137 -0
  151. examples/warming_up_to_rl/task_app/synth_envs_hosted/utils.py +62 -0
  152. examples/workflows/math_rl/configs/rl_from_base_qwen.toml +27 -0
  153. examples/workflows/math_rl/configs/rl_from_base_qwen17.toml +5 -0
  154. synth_ai/__init__.py +44 -30
  155. synth_ai/_utils/__init__.py +47 -0
  156. synth_ai/_utils/base_url.py +10 -0
  157. synth_ai/_utils/http.py +10 -0
  158. synth_ai/_utils/prompts.py +10 -0
  159. synth_ai/_utils/task_app_state.py +12 -0
  160. synth_ai/_utils/user_config.py +10 -0
  161. synth_ai/api/models/supported.py +144 -7
  162. synth_ai/api/train/__init__.py +13 -1
  163. synth_ai/api/train/builders.py +9 -3
  164. synth_ai/api/train/cli.py +155 -17
  165. synth_ai/api/train/config_finder.py +18 -11
  166. synth_ai/api/train/configs/__init__.py +8 -1
  167. synth_ai/api/train/configs/rl.py +32 -7
  168. synth_ai/api/train/configs/sft.py +6 -2
  169. synth_ai/api/train/configs/shared.py +59 -2
  170. synth_ai/api/train/env_resolver.py +13 -10
  171. synth_ai/auth/credentials.py +119 -0
  172. synth_ai/cli/__init__.py +61 -69
  173. synth_ai/cli/_modal_wrapper.py +7 -5
  174. synth_ai/cli/_typer_patch.py +0 -2
  175. synth_ai/cli/_validate_task_app.py +22 -4
  176. synth_ai/cli/commands/__init__.py +17 -0
  177. synth_ai/cli/commands/demo/__init__.py +6 -0
  178. synth_ai/cli/commands/demo/core.py +163 -0
  179. synth_ai/cli/commands/deploy/__init__.py +23 -0
  180. synth_ai/cli/commands/deploy/core.py +614 -0
  181. synth_ai/cli/commands/deploy/errors.py +72 -0
  182. synth_ai/cli/commands/deploy/validation.py +11 -0
  183. synth_ai/cli/commands/eval/__init__.py +19 -0
  184. synth_ai/cli/commands/eval/core.py +1109 -0
  185. synth_ai/cli/commands/eval/errors.py +81 -0
  186. synth_ai/cli/commands/eval/validation.py +133 -0
  187. synth_ai/cli/commands/filter/__init__.py +12 -0
  188. synth_ai/cli/commands/filter/core.py +388 -0
  189. synth_ai/cli/commands/filter/errors.py +55 -0
  190. synth_ai/cli/commands/filter/validation.py +77 -0
  191. synth_ai/cli/commands/help/__init__.py +177 -0
  192. synth_ai/cli/commands/help/core.py +73 -0
  193. synth_ai/cli/commands/status/__init__.py +64 -0
  194. synth_ai/cli/commands/status/client.py +192 -0
  195. synth_ai/cli/commands/status/config.py +92 -0
  196. synth_ai/cli/commands/status/errors.py +20 -0
  197. synth_ai/cli/commands/status/formatters.py +164 -0
  198. synth_ai/cli/commands/status/subcommands/__init__.py +9 -0
  199. synth_ai/cli/commands/status/subcommands/files.py +79 -0
  200. synth_ai/cli/commands/status/subcommands/jobs.py +334 -0
  201. synth_ai/cli/commands/status/subcommands/models.py +79 -0
  202. synth_ai/cli/commands/status/subcommands/runs.py +81 -0
  203. synth_ai/cli/commands/status/subcommands/summary.py +47 -0
  204. synth_ai/cli/commands/status/utils.py +114 -0
  205. synth_ai/cli/commands/train/__init__.py +53 -0
  206. synth_ai/cli/commands/train/core.py +21 -0
  207. synth_ai/cli/commands/train/errors.py +117 -0
  208. synth_ai/cli/commands/train/judge_schemas.py +199 -0
  209. synth_ai/cli/commands/train/judge_validation.py +304 -0
  210. synth_ai/cli/commands/train/validation.py +443 -0
  211. synth_ai/cli/demo.py +2 -162
  212. synth_ai/cli/deploy/__init__.py +28 -0
  213. synth_ai/cli/deploy/core.py +5 -0
  214. synth_ai/cli/deploy/errors.py +23 -0
  215. synth_ai/cli/deploy/validation.py +5 -0
  216. synth_ai/cli/eval/__init__.py +36 -0
  217. synth_ai/cli/eval/core.py +5 -0
  218. synth_ai/cli/eval/errors.py +31 -0
  219. synth_ai/cli/eval/validation.py +5 -0
  220. synth_ai/cli/filter/__init__.py +28 -0
  221. synth_ai/cli/filter/core.py +5 -0
  222. synth_ai/cli/filter/errors.py +23 -0
  223. synth_ai/cli/filter/validation.py +5 -0
  224. synth_ai/cli/legacy_root_backup.py +3 -1
  225. synth_ai/cli/lib/__init__.py +10 -0
  226. synth_ai/cli/lib/task_app_discovery.py +7 -0
  227. synth_ai/cli/lib/task_app_env.py +518 -0
  228. synth_ai/cli/modal_serve/__init__.py +12 -0
  229. synth_ai/cli/modal_serve/core.py +14 -0
  230. synth_ai/cli/modal_serve/errors.py +8 -0
  231. synth_ai/cli/modal_serve/validation.py +11 -0
  232. synth_ai/cli/recent.py +2 -1
  233. synth_ai/cli/serve/__init__.py +12 -0
  234. synth_ai/cli/serve/core.py +14 -0
  235. synth_ai/cli/serve/errors.py +8 -0
  236. synth_ai/cli/serve/validation.py +11 -0
  237. synth_ai/cli/setup.py +21 -0
  238. synth_ai/cli/status.py +7 -126
  239. synth_ai/cli/task_app_deploy.py +7 -0
  240. synth_ai/cli/task_app_list.py +25 -0
  241. synth_ai/cli/task_app_modal_serve.py +11 -0
  242. synth_ai/cli/task_app_serve.py +11 -0
  243. synth_ai/cli/task_apps.py +110 -1499
  244. synth_ai/cli/traces.py +1 -1
  245. synth_ai/cli/train/__init__.py +12 -0
  246. synth_ai/cli/train/core.py +21 -0
  247. synth_ai/cli/train/errors.py +8 -0
  248. synth_ai/cli/train/validation.py +24 -0
  249. synth_ai/cli/train.py +5 -0
  250. synth_ai/cli/turso.py +1 -1
  251. synth_ai/cli/watch.py +1 -1
  252. synth_ai/demos/__init__.py +10 -0
  253. synth_ai/demos/core/__init__.py +28 -1
  254. synth_ai/demos/crafter/__init__.py +1 -0
  255. synth_ai/demos/crafter/crafter_fft_4b.toml +55 -0
  256. synth_ai/demos/crafter/grpo_crafter_task_app.py +185 -0
  257. synth_ai/demos/crafter/rl_from_base_qwen4b.toml +74 -0
  258. synth_ai/demos/demo_registry.py +176 -0
  259. synth_ai/demos/demo_task_apps/crafter/grpo_crafter_task_app.py +1 -1
  260. synth_ai/demos/math/__init__.py +1 -0
  261. synth_ai/demos/math/_common.py +16 -0
  262. synth_ai/demos/math/app.py +38 -0
  263. synth_ai/demos/math/config.toml +76 -0
  264. synth_ai/demos/math/deploy_modal.py +54 -0
  265. synth_ai/demos/math/modal_task_app.py +702 -0
  266. synth_ai/demos/math/task_app_entry.py +51 -0
  267. synth_ai/environments/environment/core.py +7 -1
  268. synth_ai/environments/examples/bandit/engine.py +0 -1
  269. synth_ai/environments/examples/bandit/environment.py +0 -1
  270. synth_ai/environments/examples/red/engine.py +33 -12
  271. synth_ai/environments/examples/red/engine_helpers/reward_components.py +151 -179
  272. synth_ai/environments/examples/red/environment.py +26 -0
  273. synth_ai/environments/examples/red/trace_hooks_v3.py +168 -0
  274. synth_ai/environments/examples/wordle/environment.py +0 -1
  275. synth_ai/evals/base.py +16 -5
  276. synth_ai/evals/client.py +1 -1
  277. synth_ai/http.py +8 -22
  278. synth_ai/inference/client.py +1 -1
  279. synth_ai/judge_schemas.py +4 -5
  280. synth_ai/learning/client.py +1 -1
  281. synth_ai/learning/health.py +1 -1
  282. synth_ai/learning/jobs.py +1 -1
  283. synth_ai/learning/rl/client.py +4 -2
  284. synth_ai/learning/rl/env_keys.py +1 -1
  285. synth_ai/learning/rl/secrets.py +1 -1
  286. synth_ai/learning/sft/client.py +1 -1
  287. synth_ai/learning/sft/data.py +407 -4
  288. synth_ai/learning/validators.py +4 -1
  289. synth_ai/streaming/__init__.py +29 -0
  290. synth_ai/streaming/config.py +94 -0
  291. synth_ai/streaming/handlers.py +469 -0
  292. synth_ai/streaming/streamer.py +301 -0
  293. synth_ai/streaming/types.py +95 -0
  294. synth_ai/task/apps/__init__.py +4 -2
  295. synth_ai/task/config.py +6 -4
  296. synth_ai/task/rubrics/__init__.py +1 -2
  297. synth_ai/task/rubrics/loaders.py +14 -10
  298. synth_ai/task/rubrics.py +219 -0
  299. synth_ai/task/trace_correlation_helpers.py +24 -11
  300. synth_ai/task/tracing_utils.py +14 -3
  301. synth_ai/task/validators.py +0 -1
  302. synth_ai/tracing_v3/abstractions.py +3 -3
  303. synth_ai/tracing_v3/config.py +15 -13
  304. synth_ai/tracing_v3/constants.py +21 -0
  305. synth_ai/tracing_v3/db_config.py +3 -1
  306. synth_ai/tracing_v3/decorators.py +10 -7
  307. synth_ai/tracing_v3/llm_call_record_helpers.py +5 -5
  308. synth_ai/tracing_v3/migration_helper.py +1 -2
  309. synth_ai/tracing_v3/session_tracer.py +7 -7
  310. synth_ai/tracing_v3/storage/base.py +29 -29
  311. synth_ai/tracing_v3/storage/config.py +3 -3
  312. synth_ai/tracing_v3/turso/daemon.py +8 -9
  313. synth_ai/tracing_v3/turso/native_manager.py +80 -72
  314. synth_ai/tracing_v3/utils.py +2 -2
  315. synth_ai/utils/__init__.py +101 -0
  316. synth_ai/utils/base_url.py +94 -0
  317. synth_ai/utils/cli.py +131 -0
  318. synth_ai/utils/env.py +294 -0
  319. synth_ai/utils/http.py +172 -0
  320. synth_ai/utils/modal.py +308 -0
  321. synth_ai/utils/process.py +212 -0
  322. synth_ai/utils/prompts.py +39 -0
  323. synth_ai/utils/sqld.py +122 -0
  324. synth_ai/utils/task_app_discovery.py +882 -0
  325. synth_ai/utils/task_app_env.py +186 -0
  326. synth_ai/utils/task_app_state.py +318 -0
  327. synth_ai/utils/user_config.py +137 -0
  328. synth_ai/v0/config/__init__.py +1 -5
  329. synth_ai/v0/config/base_url.py +1 -7
  330. synth_ai/v0/tracing/config.py +1 -1
  331. synth_ai/v0/tracing/decorators.py +1 -1
  332. synth_ai/v0/tracing/upload.py +1 -1
  333. synth_ai/v0/tracing_v1/config.py +1 -1
  334. synth_ai/v0/tracing_v1/decorators.py +1 -1
  335. synth_ai/v0/tracing_v1/upload.py +1 -1
  336. {synth_ai-0.2.14.dist-info → synth_ai-0.2.17.dist-info}/METADATA +91 -32
  337. {synth_ai-0.2.14.dist-info → synth_ai-0.2.17.dist-info}/RECORD +341 -154
  338. synth_ai/cli/man.py +0 -106
  339. synth_ai/cli/tui.py +0 -57
  340. synth_ai/compound/cais.py +0 -0
  341. synth_ai/core/experiment.py +0 -13
  342. synth_ai/core/system.py +0 -15
  343. synth_ai/demo_registry.py +0 -295
  344. synth_ai/handshake.py +0 -109
  345. synth_ai/tui/__init__.py +0 -5
  346. synth_ai/tui/__main__.py +0 -13
  347. synth_ai/tui/cli/__init__.py +0 -1
  348. synth_ai/tui/cli/query_experiments.py +0 -164
  349. synth_ai/tui/cli/query_experiments_v3.py +0 -164
  350. synth_ai/tui/dashboard.py +0 -906
  351. {synth_ai-0.2.14.dist-info → synth_ai-0.2.17.dist-info}/WHEEL +0 -0
  352. {synth_ai-0.2.14.dist-info → synth_ai-0.2.17.dist-info}/entry_points.txt +0 -0
  353. {synth_ai-0.2.14.dist-info → synth_ai-0.2.17.dist-info}/licenses/LICENSE +0 -0
  354. {synth_ai-0.2.14.dist-info → synth_ai-0.2.17.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,119 @@
1
+ import contextlib
2
+ import os
3
+ import time
4
+ import webbrowser
5
+
6
+ import requests
7
+ from requests import RequestException
8
+ from synth_ai.utils.env import resolve_env_var, write_env_var_to_dotenv, write_env_var_to_json
9
+
10
+
11
+ def fetch_credentials_from_web_browser_session(
12
+ browser: bool = True,
13
+ prod: bool = True
14
+ ) -> None:
15
+ synth_api_key = ''
16
+ env_api_key = ''
17
+ org_name = ''
18
+
19
+ if browser:
20
+ origin = "https://www.usesynth.ai" if prod else "http://localhost:3000"
21
+ init_url = f"{origin}/api/sdk/handshake/init"
22
+ token_url =f"{origin}/api/sdk/handshake/token"
23
+
24
+ print(f"\n🌐 Connecting to {origin} to fetch your Synth credentials")
25
+
26
+ # 1. Initialize browser handshake
27
+ try:
28
+ init_res = requests.post(init_url, timeout=10)
29
+ except RequestException as exc:
30
+ raise RuntimeError(f"Failed to reach handshake init endpoint: {exc}") from exc
31
+
32
+ if init_res.status_code != 200:
33
+ body = init_res.text.strip()
34
+ raise RuntimeError(f"Handshake init failed ({init_res.status_code}): {body or 'no response body'}")
35
+
36
+ try:
37
+ init_data = init_res.json()
38
+ except ValueError as exc:
39
+ raise RuntimeError("Handshake init returned malformed JSON.") from exc
40
+
41
+ device_code = str(init_data.get("device_code") or "").strip()
42
+ verification_uri = str(init_data.get("verification_uri") or "").strip()
43
+ if not device_code or not verification_uri:
44
+ raise RuntimeError("Handshake init response missing device_code or verification_uri.")
45
+
46
+ try:
47
+ expires_in = int(init_data.get("expires_in") or 600)
48
+ except (TypeError, ValueError):
49
+ expires_in = 120
50
+ try:
51
+ interval = max(int(init_data.get("interval") or 3), 1)
52
+ except (TypeError, ValueError):
53
+ interval = 3
54
+
55
+ # 2. Open browser to verification URL
56
+ with contextlib.suppress(Exception):
57
+ webbrowser.open(verification_uri)
58
+
59
+ deadline = time.time() + expires_in
60
+ handshake_data = None
61
+
62
+ # 3. Poll handshake token endpoint
63
+ while time.time() <= deadline:
64
+ try:
65
+ handshake_res = requests.post(
66
+ token_url,
67
+ json={"device_code": device_code},
68
+ timeout=10,
69
+ )
70
+ except RequestException:
71
+ time.sleep(interval)
72
+ continue
73
+
74
+ if handshake_res.status_code == 200:
75
+ try:
76
+ handshake_data = handshake_res.json()
77
+ except ValueError as exc:
78
+ raise RuntimeError("Handshake token returned malformed JSON.") from exc
79
+ break
80
+
81
+ if handshake_res.status_code in (404, 410):
82
+ raise RuntimeError("Handshake failed: device code expired or was revoked.")
83
+
84
+ time.sleep(interval)
85
+
86
+ if handshake_data is None:
87
+ raise TimeoutError("Handshake timed out before credentials were returned.")
88
+
89
+ # 4. Extract credentials from handshake payload
90
+ org = handshake_data.get("org")
91
+ if not isinstance(org, dict):
92
+ org = {}
93
+ org_name = str(org.get("name") or "your organization").strip()
94
+
95
+ credentials = handshake_data.get("keys")
96
+ if not isinstance(credentials, dict):
97
+ credentials = {}
98
+
99
+ synth_api_key = str(credentials.get("synth") or "").strip()
100
+ env_api_key = str(credentials.get("rl_env") or "").strip()
101
+
102
+ print(f"\nāœ… Connected to {org_name}")
103
+
104
+ # Load credentials to process environment and save credentials to .env and ~/synth-ai/config.json
105
+ if synth_api_key:
106
+ print("\nLoading SYNTH_API_KEY into process environment")
107
+ os.environ["SYNTH_API_KEY"] = synth_api_key
108
+ synth_api_key = resolve_env_var("SYNTH_API_KEY")
109
+ if env_api_key:
110
+ print("\nLoading ENVIRONMENT_API_KEY into process environment")
111
+ os.environ["ENVIRONMENT_API_KEY"] = env_api_key
112
+ env_api_key = resolve_env_var("ENVIRONMENT_API_KEY")
113
+
114
+ if browser:
115
+ print('')
116
+ write_env_var_to_json("SYNTH_API_KEY", synth_api_key, "~/.synth-ai/config.json")
117
+ write_env_var_to_dotenv("SYNTH_API_KEY", synth_api_key)
118
+ write_env_var_to_json("ENVIRONMENT_API_KEY", env_api_key, "~/.synth-ai/config.json")
119
+ write_env_var_to_dotenv("ENVIRONMENT_API_KEY", env_api_key)
synth_ai/cli/__init__.py CHANGED
@@ -8,8 +8,9 @@ pyproject entry point `synth_ai.cli:cli`.
8
8
  from __future__ import annotations
9
9
 
10
10
  import importlib
11
+ import sys
11
12
  from collections.abc import Callable
12
- from typing import Any, cast
13
+ from typing import Any
13
14
 
14
15
  # Load environment variables from a local .env if present (repo root)
15
16
  try:
@@ -21,86 +22,77 @@ except Exception:
21
22
  # dotenv is optional at runtime; proceed if unavailable
22
23
  pass
23
24
 
24
- try:
25
- from synth_ai.cli._typer_patch import patch_typer_make_metavar
26
-
27
- patch_typer_make_metavar()
28
- except Exception:
29
- pass
25
+ def _callable_from(module: Any, attr: str) -> Callable[..., Any] | None:
26
+ candidate = getattr(module, attr, None)
27
+ return candidate if callable(candidate) else None
30
28
 
31
29
 
32
- from synth_ai.cli.root import cli # new canonical CLI entrypoint
30
+ def _maybe_import(module_path: str) -> Any | None:
31
+ try:
32
+ return importlib.import_module(module_path)
33
+ except Exception:
34
+ return None
33
35
 
34
- # Register subcommands from this package onto the group
35
- # Deprecated/legacy commands intentionally not registered: watch/experiments, balance, calc,
36
- # man, recent, status, traces
37
- try:
38
- from synth_ai.cli import demo as _demo
39
36
 
40
- _demo.register(cli)
41
- except Exception:
42
- pass
43
- try:
44
- from synth_ai.cli import turso as _turso
37
+ def _maybe_call(module_path: str, attr: str, *args: Any, **kwargs: Any) -> None:
38
+ module = _maybe_import(module_path)
39
+ if not module:
40
+ return
41
+ fn = _callable_from(module, attr)
42
+ if fn:
43
+ fn(*args, **kwargs)
45
44
 
46
- _turso.register(cli)
47
- except Exception:
48
- pass
49
- try:
50
- _train_module = cast(Any, importlib.import_module("synth_ai.api.train"))
51
- _train_register = cast(Callable[[Any], None], _train_module.register)
52
- _train_register(cli)
53
- except Exception:
54
- pass
55
45
 
46
+ # Apply Typer patch if available
47
+ _maybe_call("synth_ai.cli._typer_patch", "patch_typer_make_metavar")
56
48
 
57
- # Import task_app_group conditionally
58
- try:
59
- from synth_ai.cli.task_apps import task_app_group
60
- cli.add_command(task_app_group, name="task-app")
61
- except Exception:
62
- # Task app functionality not available
63
- pass
64
49
 
50
+ _cli_module = _maybe_import("synth_ai.cli.root")
51
+ if not _cli_module:
52
+ raise ImportError("synth_ai.cli.root is required for CLI entrypoint")
53
+ cli = _cli_module.cli # type: ignore[attr-defined]
65
54
 
55
+ # Register core commands implemented as standalone modules
66
56
  try:
67
- # Make task_apps import more robust to handle missing optional dependencies
68
- import importlib
69
- task_apps_module = importlib.import_module('synth_ai.cli.task_apps')
70
- task_apps_module.register(cli)
71
- except (ImportError, ModuleNotFoundError, TypeError, RuntimeError) as e:
72
- # Task apps module not available (missing optional dependencies)
73
- # This is expected - silently skip
74
- pass
75
-
76
- # Register TUI command - make import completely isolated
77
- def _register_tui_command():
78
- """Register TUI command only when called, not during CLI startup."""
79
- try:
80
- # Import TUI only when the command is actually used
81
- from synth_ai.cli.tui import register as tui_register
82
- tui_register(cli)
83
- except Exception:
84
- # TUI not available - this is expected if dependencies are missing
85
- pass
57
+ from synth_ai.cli.setup import setup_cmd
86
58
 
87
- # Add TUI command as a lazy-registered command
88
- try:
89
- # Try to import and register immediately for normal cases
90
- from synth_ai.cli.tui import register as tui_register
91
- tui_register(cli)
59
+ cli.add_command(setup_cmd, name="setup")
92
60
  except Exception:
93
- # If that fails, add a lazy registration that will only happen when called
94
- # For now, just skip - the command won't be available but CLI won't crash
95
61
  pass
96
62
 
97
- # Add task app commands if available
98
- try:
99
- if 'task_app_group' in locals() and hasattr(task_app_group, 'commands'):
100
- cli.add_command(task_app_group.commands["serve"], name="serve")
101
- cli.add_command(task_app_group.commands["deploy"], name="deploy")
102
- cli.add_command(task_app_group.commands["modal-serve"], name="modal-serve")
103
- except Exception:
104
- # Task app commands not available
105
- pass
63
+
64
+ # Register optional subcommands packaged under synth_ai.cli.*
65
+ for _module_path in ("synth_ai.cli.commands.demo", "synth_ai.cli.commands.status", "synth_ai.cli.turso"):
66
+ module = _maybe_import(_module_path)
67
+ if not module:
68
+ continue
69
+ sub_name = _module_path.rsplit(".", 1)[-1]
70
+ setattr(sys.modules[__name__], sub_name, module)
71
+ fn = _callable_from(module, "register")
72
+ if fn:
73
+ fn(cli)
74
+
75
+ # Register help command
76
+ _maybe_call("synth_ai.cli.commands.help.core", "register", cli)
77
+
78
+ # Train CLI lives under synth_ai.api.train
79
+ _maybe_call("synth_ai.api.train", "register", cli)
80
+
81
+ # Task app group/commands are optional and have richer API surface
82
+ _task_apps_module = _maybe_import("synth_ai.cli.task_apps")
83
+ if _task_apps_module:
84
+ task_app_group = getattr(_task_apps_module, "task_app_group", None)
85
+ if task_app_group is not None:
86
+ cli.add_command(task_app_group, name="task-app")
87
+ # Expose common aliases when present
88
+ commands = getattr(task_app_group, "commands", None)
89
+ if isinstance(commands, dict):
90
+ for alias, name in (("serve", "serve"), ("deploy", "deploy"), ("modal-serve", "modal-serve")):
91
+ command = commands.get(name)
92
+ if command is not None:
93
+ cli.add_command(command, name=alias)
94
+ register_task_apps = _callable_from(_task_apps_module, "register")
95
+ if register_task_apps:
96
+ register_task_apps(cli)
97
+
106
98
  # Top-level 'info' alias removed; use `synth-ai task-app info` instead
@@ -1,16 +1,19 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import importlib
3
4
  import sys
4
5
 
5
6
 
6
7
  def main() -> int:
7
8
  # Apply Typer compatibility patch before Modal CLI bootstraps Click/Typer internals.
8
9
  try:
9
- from synth_ai.cli._typer_patch import patch_typer_make_metavar
10
-
11
- patch_typer_make_metavar()
10
+ module = importlib.import_module("synth_ai.cli._typer_patch")
12
11
  except Exception:
13
- pass
12
+ module = None
13
+ if module is not None:
14
+ patch = getattr(module, "patch_typer_make_metavar", None)
15
+ if callable(patch):
16
+ patch()
14
17
 
15
18
  from modal.__main__ import main as modal_main
16
19
 
@@ -26,4 +29,3 @@ def main() -> int:
26
29
 
27
30
  if __name__ == "__main__":
28
31
  sys.exit(main())
29
-
@@ -1,5 +1,3 @@
1
- from __future__ import annotations
2
-
3
1
  from click import Parameter
4
2
 
5
3
 
@@ -3,9 +3,27 @@
3
3
  # This module provides the validate_task_app function for CLI use
4
4
  # The actual implementation is imported from the task module
5
5
 
6
- from synth_ai.task.validators import (
7
- validate_task_app_endpoint as validate_task_app, # type: ignore[attr-defined]
8
- )
6
+ import importlib
7
+ from collections.abc import Callable
8
+ from typing import Any
9
9
 
10
- __all__ = ["validate_task_app"]
10
+ _validators_module: Any | None = None
11
+ validate_task_app: Callable[..., Any] | None = None
12
+
13
+ try:
14
+ _validators_module = importlib.import_module("synth_ai.task.validators")
15
+ except Exception:
16
+ _validators_module = None
17
+
18
+ if _validators_module is not None:
19
+ candidate = getattr(_validators_module, "validate_task_app_endpoint", None)
20
+ if callable(candidate):
21
+ validate_task_app = candidate
11
22
 
23
+ if validate_task_app is None:
24
+ def _missing_validate_task_app(*_args: Any, **_kwargs: Any) -> None:
25
+ raise RuntimeError("task validation utilities are unavailable in this environment")
26
+
27
+ validate_task_app = _missing_validate_task_app
28
+
29
+ __all__ = ["validate_task_app"]
@@ -0,0 +1,17 @@
1
+ """
2
+ Structured CLI command implementations.
3
+
4
+ Each subpackage under this namespace provides the core command entrypoints,
5
+ validation helpers, and error types for a top-level CLI command (e.g. train,
6
+ eval, deploy).
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ __all__ = [
12
+ "train",
13
+ "eval",
14
+ "filter",
15
+ "deploy",
16
+ "status",
17
+ ]
@@ -0,0 +1,6 @@
1
+ from __future__ import annotations
2
+
3
+ from .core import command, register
4
+
5
+ __all__ = ["command", "register"]
6
+
@@ -0,0 +1,163 @@
1
+ from __future__ import annotations
2
+
3
+ import importlib
4
+ import os
5
+ import subprocess
6
+ from pathlib import Path
7
+ from typing import Any, cast
8
+
9
+ import click
10
+ from click.exceptions import Exit
11
+
12
+ __all__ = ["command", "register"]
13
+
14
+ _demo_cli = cast(Any, importlib.import_module("synth_ai.demos.core.cli"))
15
+
16
+
17
+ def _find_demo_scripts(root: Path) -> list[Path]:
18
+ if not root.exists():
19
+ return []
20
+ return sorted(path for path in root.rglob("run_demo.sh") if path.is_file())
21
+
22
+
23
+ def _run_demo_command(func: Any, *args: Any, **kwargs: Any) -> None:
24
+ """Invoke a demo command and map non-zero exits to Click exits."""
25
+
26
+ try:
27
+ result = func(*args, **kwargs)
28
+ except SystemExit as exc: # pragma: no cover - defensive shim
29
+ raise Exit(exc.code or 1) from exc
30
+
31
+ if result is None:
32
+ return
33
+
34
+ try:
35
+ code = int(result)
36
+ except (TypeError, ValueError):
37
+ return
38
+ if code != 0:
39
+ raise Exit(code)
40
+
41
+
42
+ @click.group(
43
+ "demo",
44
+ invoke_without_command=True,
45
+ help="Demo helpers for the math RL pipeline.",
46
+ )
47
+ @click.option(
48
+ "--force",
49
+ is_flag=True,
50
+ help="Overwrite existing files in the current directory when initializing demo assets.",
51
+ )
52
+ @click.option("--list", "list_only", is_flag=True, help="List available legacy demos and exit.")
53
+ @click.option("-f", "filter_term", default="", help="Filter legacy demos by substring.")
54
+ @click.pass_context
55
+ def command(ctx: click.Context, force: bool, list_only: bool, filter_term: str) -> None:
56
+ """Default command: initialize RL demo files into ./synth_demo/ (alias of `demo init`)."""
57
+ if ctx.invoked_subcommand is not None:
58
+ return
59
+
60
+ if list_only:
61
+ repo_root = Path(os.getcwd())
62
+ examples_dir = repo_root / "examples"
63
+ demos = _find_demo_scripts(examples_dir)
64
+ if filter_term:
65
+ term = filter_term.lower()
66
+ demos = [path for path in demos if term in str(path).lower()]
67
+
68
+ if not demos:
69
+ click.echo("No run_demo.sh scripts found under examples/.")
70
+ return
71
+
72
+ click.echo("Available demos:")
73
+ for idx, path in enumerate(demos, start=1):
74
+ click.echo(f" {idx}. {path.relative_to(repo_root)}")
75
+ click.echo("")
76
+
77
+ def _validate_choice(val: str) -> int:
78
+ try:
79
+ selection = int(val)
80
+ except Exception as err: # pragma: no cover - Click handles prompt errors
81
+ raise click.BadParameter("Enter a number from the list") from err
82
+ if selection < 1 or selection > len(demos):
83
+ raise click.BadParameter(f"Choose a number between 1 and {len(demos)}")
84
+ return selection
85
+
86
+ choice = click.prompt("Select a demo to run", value_proc=_validate_choice)
87
+ script = demos[choice - 1]
88
+
89
+ click.echo("")
90
+ click.echo(f"šŸš€ Running {script.relative_to(repo_root)}\n")
91
+
92
+ try:
93
+ subprocess.run(["bash", str(script)], check=True)
94
+ except subprocess.CalledProcessError as exc:
95
+ click.echo(f"āŒ Demo exited with non-zero status: {exc.returncode}")
96
+ except KeyboardInterrupt:
97
+ click.echo("\nšŸ›‘ Demo interrupted by user")
98
+ return
99
+
100
+ _run_demo_command(_demo_cli.init, force=force)
101
+
102
+
103
+ @command.command("deploy")
104
+ @click.option("--local", is_flag=True, help="Run the local FastAPI app instead of deploying to Modal.")
105
+ @click.option("--app", type=click.Path(), default=None, help="Path to Modal app.py for `uv run modal deploy`.")
106
+ @click.option("--name", type=str, default="synth-math-demo", help="Modal app name.")
107
+ @click.option(
108
+ "--script",
109
+ type=click.Path(),
110
+ default=None,
111
+ help="Path to deploy_task_app.sh (optional legacy helper).",
112
+ )
113
+ def demo_deploy(local: bool, app: str | None, name: str, script: str | None) -> None:
114
+ _run_demo_command(
115
+ _demo_cli.deploy,
116
+ local=local,
117
+ app=app,
118
+ name=name,
119
+ script=script,
120
+ )
121
+
122
+
123
+ @command.command("configure")
124
+ def demo_configure() -> None:
125
+ _run_demo_command(_demo_cli.run)
126
+
127
+
128
+ @command.command("setup")
129
+ def demo_setup() -> None:
130
+ _run_demo_command(_demo_cli.setup)
131
+
132
+
133
+ @command.command("run")
134
+ @click.option("--batch-size", type=int, default=None)
135
+ @click.option("--group-size", type=int, default=None)
136
+ @click.option("--model", type=str, default=None)
137
+ @click.option("--timeout", type=int, default=600)
138
+ def demo_run(
139
+ batch_size: int | None,
140
+ group_size: int | None,
141
+ model: str | None,
142
+ timeout: int,
143
+ ) -> None:
144
+ _run_demo_command(
145
+ _demo_cli.run,
146
+ batch_size=batch_size,
147
+ group_size=group_size,
148
+ model=model,
149
+ timeout=timeout,
150
+ )
151
+
152
+
153
+ @click.command("setup")
154
+ def setup_alias() -> None:
155
+ """Perform SDK handshake and write keys to .env."""
156
+ _run_demo_command(_demo_cli.setup)
157
+
158
+
159
+ def register(cli: click.Group) -> None:
160
+ """Attach the demo command group and related aliases to the CLI."""
161
+ cli.add_command(command)
162
+ cli.add_command(setup_alias)
163
+
@@ -0,0 +1,23 @@
1
+ from __future__ import annotations
2
+
3
+ from .core import (
4
+ command,
5
+ get_command,
6
+ modal_serve_command,
7
+ register_task_app_commands,
8
+ run_modal_runtime,
9
+ run_uvicorn_runtime,
10
+ )
11
+ from .errors import DeployCliError
12
+ from .validation import validate_deploy_options
13
+
14
+ __all__ = [
15
+ "command",
16
+ "get_command",
17
+ "modal_serve_command",
18
+ "register_task_app_commands",
19
+ "run_modal_runtime",
20
+ "run_uvicorn_runtime",
21
+ "DeployCliError",
22
+ "validate_deploy_options",
23
+ ]