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,443 @@
1
+ """TOML validation logic for train commands (SFT and RL)."""
2
+
3
+ from collections.abc import MutableMapping
4
+ from pathlib import Path
5
+ from typing import Any
6
+
7
+ from pydantic import ValidationError
8
+ from synth_ai.api.train.configs.rl import RLConfig
9
+ from synth_ai.api.train.configs.sft import SFTConfig
10
+ from synth_ai.api.train.utils import load_toml
11
+
12
+ from .errors import (
13
+ InvalidJudgeConfigError,
14
+ InvalidRLConfigError,
15
+ InvalidRubricConfigError,
16
+ InvalidSFTConfigError,
17
+ MissingAlgorithmError,
18
+ MissingComputeError,
19
+ MissingDatasetError,
20
+ MissingModelError,
21
+ TomlParseError,
22
+ UnsupportedAlgorithmError,
23
+ )
24
+ from .judge_validation import extract_and_validate_judge_rubric
25
+
26
+ __all__ = [
27
+ "validate_sft_config",
28
+ "validate_rl_config",
29
+ "load_and_validate_sft",
30
+ "load_and_validate_rl",
31
+ ]
32
+
33
+
34
+ def validate_sft_config(config: MutableMapping[str, Any]) -> dict[str, Any]:
35
+ """Validate SFT configuration from TOML.
36
+
37
+ Args:
38
+ config: Raw configuration dictionary from TOML
39
+
40
+ Returns:
41
+ Validated configuration dictionary
42
+
43
+ Raises:
44
+ InvalidSFTConfigError: If validation fails
45
+ MissingAlgorithmError: If algorithm section is missing or invalid
46
+ MissingModelError: If model is not specified
47
+ MissingDatasetError: If dataset path is not specified
48
+ MissingComputeError: If compute section is missing required fields
49
+ """
50
+ # Check for required top-level sections
51
+ if "algorithm" not in config or not config["algorithm"]:
52
+ raise MissingAlgorithmError(
53
+ detail="[algorithm] section is required for SFT configs"
54
+ )
55
+
56
+ if "job" not in config or not config["job"]:
57
+ raise InvalidSFTConfigError(
58
+ detail="[job] section is required for SFT configs"
59
+ )
60
+
61
+ job = config.get("job", {})
62
+ if not job.get("model"):
63
+ raise MissingModelError(
64
+ detail="[job].model is required (e.g., 'Qwen/Qwen3-4B')"
65
+ )
66
+
67
+ # Check that at least one dataset source is specified
68
+ if not (job.get("data") or job.get("data_path")):
69
+ raise MissingDatasetError(
70
+ detail="[job].data or [job].data_path must be specified",
71
+ hint="Provide path to training JSONL file"
72
+ )
73
+
74
+ # Validate algorithm type, method, and variety
75
+ algorithm = config.get("algorithm", {})
76
+ if algorithm.get("type") not in {"offline", None}:
77
+ raise UnsupportedAlgorithmError(
78
+ algorithm_type=algorithm.get("type", "unknown"),
79
+ expected="offline",
80
+ hint="SFT requires algorithm.type = 'offline'"
81
+ )
82
+
83
+ method = algorithm.get("method", "")
84
+ if method and method not in {"sft", "supervised_finetune"}:
85
+ raise UnsupportedAlgorithmError(
86
+ algorithm_type=method,
87
+ expected="sft or supervised_finetune",
88
+ hint="SFT requires algorithm.method = 'sft' or 'supervised_finetune'"
89
+ )
90
+
91
+ # Validate variety is present
92
+ if not algorithm.get("variety"):
93
+ raise MissingAlgorithmError(
94
+ detail="[algorithm].variety is required (e.g., 'fft', 'lora', 'qlora')"
95
+ )
96
+
97
+ # Validate compute section
98
+ compute = config.get("compute", {})
99
+ if not compute:
100
+ raise MissingComputeError(
101
+ detail="[compute] section is required",
102
+ hint="Specify gpu_type, gpu_count, and nodes"
103
+ )
104
+
105
+ if not compute.get("gpu_type"):
106
+ raise MissingComputeError(
107
+ detail="[compute].gpu_type is required (e.g., 'H100', 'A100')"
108
+ )
109
+
110
+ if not compute.get("gpu_count"):
111
+ raise MissingComputeError(
112
+ detail="[compute].gpu_count is required"
113
+ )
114
+
115
+ # Validate using Pydantic model
116
+ try:
117
+ validated = SFTConfig.from_mapping(config)
118
+ return validated.to_dict()
119
+ except ValidationError as exc:
120
+ errors = []
121
+ for error in exc.errors():
122
+ loc = ".".join(str(x) for x in error["loc"])
123
+ msg = error["msg"]
124
+ errors.append(f" • {loc}: {msg}")
125
+ raise InvalidSFTConfigError(
126
+ detail="Pydantic validation failed:\n" + "\n".join(errors)
127
+ ) from exc
128
+
129
+
130
+ def validate_rl_config(config: MutableMapping[str, Any]) -> dict[str, Any]:
131
+ """Validate RL configuration from TOML.
132
+
133
+ Args:
134
+ config: Raw configuration dictionary from TOML
135
+
136
+ Returns:
137
+ Validated configuration dictionary
138
+
139
+ Raises:
140
+ InvalidRLConfigError: If validation fails
141
+ MissingAlgorithmError: If algorithm section is missing or invalid
142
+ MissingModelError: If model is not specified
143
+ MissingComputeError: If compute section is missing required fields
144
+ """
145
+ # Check for required top-level sections
146
+ if "algorithm" not in config or not config["algorithm"]:
147
+ raise MissingAlgorithmError(
148
+ detail="[algorithm] section is required for RL configs"
149
+ )
150
+
151
+ # Check for model OR policy (policy is the new format)
152
+ if "policy" not in config and "model" not in config:
153
+ raise MissingModelError(
154
+ detail="[policy] or [model] section is required for RL configs"
155
+ )
156
+
157
+ # Validate algorithm type, method, and variety
158
+ algorithm = config.get("algorithm", {})
159
+ if algorithm.get("type") not in {"online", None}:
160
+ raise UnsupportedAlgorithmError(
161
+ algorithm_type=algorithm.get("type", "unknown"),
162
+ expected="online",
163
+ hint="RL requires algorithm.type = 'online'"
164
+ )
165
+
166
+ method = algorithm.get("method", "")
167
+ if method and method not in {"policy_gradient", "ppo", "gspo"}:
168
+ raise UnsupportedAlgorithmError(
169
+ algorithm_type=method,
170
+ expected="policy_gradient",
171
+ hint="RL requires algorithm.method = 'policy_gradient'"
172
+ )
173
+
174
+ # Validate variety is present
175
+ if not algorithm.get("variety"):
176
+ raise MissingAlgorithmError(
177
+ detail="[algorithm].variety is required (e.g., 'gspo', 'ppo')"
178
+ )
179
+
180
+ # Validate model/policy section
181
+ model = config.get("model", {})
182
+ policy = config.get("policy", {})
183
+
184
+ # Use policy if available, otherwise fall back to model
185
+ if policy:
186
+ if not policy.get("model_name") and not policy.get("source"):
187
+ raise MissingModelError(
188
+ detail="[policy].model_name or [policy].source must be specified",
189
+ hint="Provide base model (e.g., 'Qwen/Qwen3-4B') or source checkpoint"
190
+ )
191
+
192
+ if not policy.get("trainer_mode"):
193
+ raise InvalidRLConfigError(
194
+ detail="[policy].trainer_mode is required (e.g., 'full', 'lora')"
195
+ )
196
+
197
+ if not policy.get("label"):
198
+ raise InvalidRLConfigError(
199
+ detail="[policy].label is required (e.g., 'my-rl-model')",
200
+ hint="Provide a descriptive label for this model"
201
+ )
202
+ elif model:
203
+ if not model.get("base") and not model.get("source"):
204
+ raise MissingModelError(
205
+ detail="[model].base or [model].source must be specified",
206
+ hint="Provide base model (e.g., 'Qwen/Qwen3-4B') or source checkpoint"
207
+ )
208
+
209
+ if not model.get("trainer_mode"):
210
+ raise InvalidRLConfigError(
211
+ detail="[model].trainer_mode is required (e.g., 'full', 'lora')"
212
+ )
213
+
214
+ if not model.get("label"):
215
+ raise InvalidRLConfigError(
216
+ detail="[model].label is required (e.g., 'my-rl-model')",
217
+ hint="Provide a descriptive label for this model"
218
+ )
219
+
220
+ # Validate compute section
221
+ compute = config.get("compute", {})
222
+ if not compute:
223
+ raise MissingComputeError(
224
+ detail="[compute] section is required",
225
+ hint="Specify gpu_type and gpu_count"
226
+ )
227
+
228
+ if not compute.get("gpu_type"):
229
+ raise MissingComputeError(
230
+ detail="[compute].gpu_type is required (e.g., 'H100', 'A100')"
231
+ )
232
+
233
+ if not compute.get("gpu_count"):
234
+ raise MissingComputeError(
235
+ detail="[compute].gpu_count is required"
236
+ )
237
+
238
+ # Check for rollout configuration
239
+ rollout = config.get("rollout", {})
240
+ if not rollout:
241
+ raise InvalidRLConfigError(
242
+ detail="[rollout] section is required for RL configs",
243
+ hint="Specify env_name, policy_name, max_turns, etc."
244
+ )
245
+
246
+ if not rollout.get("env_name"):
247
+ raise InvalidRLConfigError(
248
+ detail="[rollout].env_name is required (e.g., 'math', 'crafter')"
249
+ )
250
+
251
+ if not rollout.get("policy_name"):
252
+ raise InvalidRLConfigError(
253
+ detail="[rollout].policy_name is required"
254
+ )
255
+
256
+ # Validate topology section (can be top-level or under compute)
257
+ topology = config.get("topology") or compute.get("topology", {})
258
+ if not topology:
259
+ raise InvalidRLConfigError(
260
+ detail="[topology] or [compute.topology] section is required",
261
+ hint="Specify gpus_for_vllm, gpus_for_training, etc."
262
+ )
263
+
264
+ # Validate vllm section and tensor_parallel consistency
265
+ vllm = config.get("vllm", {})
266
+ topology_tensor_parallel = topology.get("tensor_parallel")
267
+ vllm_tensor_parallel = vllm.get("tensor_parallel_size")
268
+
269
+ if topology_tensor_parallel and not vllm_tensor_parallel:
270
+ raise InvalidRLConfigError(
271
+ detail="Both [topology].tensor_parallel and [vllm].tensor_parallel_size must be provided",
272
+ hint=f"Add [vllm] section with tensor_parallel_size={topology_tensor_parallel}"
273
+ )
274
+
275
+ if vllm_tensor_parallel and not topology_tensor_parallel:
276
+ raise InvalidRLConfigError(
277
+ detail="Both [topology].tensor_parallel and [vllm].tensor_parallel_size must be provided",
278
+ hint=f"Add tensor_parallel={vllm_tensor_parallel} to [topology] section"
279
+ )
280
+
281
+ # Check for training section and its required fields
282
+ training = config.get("training", {})
283
+ if training:
284
+ required_training_fields = {
285
+ "num_epochs": "number of training epochs",
286
+ "iterations_per_epoch": "iterations per epoch",
287
+ "max_turns": "maximum turns",
288
+ "batch_size": "batch size",
289
+ "group_size": "group size",
290
+ "learning_rate": "learning rate",
291
+ "weight_sync_interval": "weight sync interval",
292
+ "log_interval": "logging interval",
293
+ }
294
+
295
+ for field, description in required_training_fields.items():
296
+ if field not in training:
297
+ raise InvalidRLConfigError(
298
+ detail=f"[training].{field} is required ({description})",
299
+ hint=f"Add {field} to the [training] section"
300
+ )
301
+
302
+ # Validate weight_sync_interval is positive
303
+ weight_sync_interval = training.get("weight_sync_interval")
304
+ if weight_sync_interval is not None and weight_sync_interval <= 0:
305
+ raise InvalidRLConfigError(
306
+ detail="[training].weight_sync_interval must be a positive integer",
307
+ hint="Set weight_sync_interval to a value >= 1"
308
+ )
309
+
310
+ # Ensure weight_sync block exists with proper defaults
311
+ # Backend requires mode="direct" - always inject it
312
+ if "weight_sync" not in training:
313
+ training["weight_sync"] = {
314
+ "enable": True,
315
+ "mode": "direct", # Backend requirement
316
+ "targets": ["policy"],
317
+ "interval": training.get("weight_sync_interval", 1),
318
+ }
319
+ else:
320
+ weight_sync = training["weight_sync"]
321
+ # Always force mode to "direct" (backend requirement)
322
+ weight_sync["mode"] = "direct"
323
+
324
+ # Validate existing weight_sync block
325
+ if not weight_sync.get("enable"):
326
+ raise InvalidRLConfigError(
327
+ detail="[training.weight_sync].enable must be true",
328
+ hint="Set enable=true in the weight_sync section"
329
+ )
330
+ targets = weight_sync.get("targets", [])
331
+ if not targets or "policy" not in targets:
332
+ raise InvalidRLConfigError(
333
+ detail="[training.weight_sync].targets must include 'policy'",
334
+ hint="Add targets=['policy'] to the weight_sync section"
335
+ )
336
+ # Inject interval if not present
337
+ if "interval" not in weight_sync:
338
+ weight_sync["interval"] = training.get("weight_sync_interval", 1)
339
+
340
+ # Check for evaluation section
341
+ evaluation = config.get("evaluation", {})
342
+ if evaluation:
343
+ required_eval_fields = {
344
+ "instances": "number of evaluation instances",
345
+ "every_n_iters": "evaluation frequency",
346
+ "seeds": "evaluation seeds",
347
+ }
348
+
349
+ for field, description in required_eval_fields.items():
350
+ if field not in evaluation:
351
+ raise InvalidRLConfigError(
352
+ detail=f"[evaluation].{field} is required ({description})",
353
+ hint=f"Add {field} to the [evaluation] section"
354
+ )
355
+
356
+ # Inject services section if not present (will be populated at runtime)
357
+ if "services" not in config:
358
+ config["services"] = {
359
+ "task_url": "placeholder", # Will be resolved at runtime
360
+ }
361
+
362
+ # Inject reference placement if not present (like builders.py does)
363
+ # Reference is now under compute.topology.reference_placement
364
+ if "compute" not in config:
365
+ config["compute"] = {}
366
+ if "topology" not in config["compute"]:
367
+ config["compute"]["topology"] = {}
368
+ if "reference_placement" not in config["compute"]["topology"]:
369
+ config["compute"]["topology"]["reference_placement"] = "none"
370
+
371
+ # Validate judge/rubric configuration with formalized Pydantic models
372
+ # This will emit deprecation warnings for dead fields and validate structure
373
+ try:
374
+ rubric_config, judge_config = extract_and_validate_judge_rubric(config)
375
+ # Validation passed - configs are clean and ready for use
376
+ # The validated Pydantic models can be used by training code if needed
377
+ except (InvalidJudgeConfigError, InvalidRubricConfigError) as exc:
378
+ raise InvalidRLConfigError(
379
+ detail=f"Judge/Rubric validation failed: {exc.detail}",
380
+ hint="Check JUDGE_RUBRIC_CLEANUP_GUIDE.md for migration help."
381
+ ) from exc
382
+
383
+ # Validate using Pydantic model
384
+ try:
385
+ validated = RLConfig.from_mapping(config)
386
+ return validated.to_dict()
387
+ except ValidationError as exc:
388
+ errors = []
389
+ for error in exc.errors():
390
+ loc = ".".join(str(x) for x in error["loc"])
391
+ msg = error["msg"]
392
+ errors.append(f" • {loc}: {msg}")
393
+ raise InvalidRLConfigError(
394
+ detail="Pydantic validation failed:\n" + "\n".join(errors)
395
+ ) from exc
396
+
397
+
398
+ def load_and_validate_sft(config_path: Path) -> dict[str, Any]:
399
+ """Load and validate an SFT TOML configuration file.
400
+
401
+ Args:
402
+ config_path: Path to TOML configuration file
403
+
404
+ Returns:
405
+ Validated configuration dictionary
406
+
407
+ Raises:
408
+ TomlParseError: If TOML parsing fails
409
+ InvalidSFTConfigError: If validation fails
410
+ """
411
+ try:
412
+ raw_config = load_toml(config_path)
413
+ except Exception as exc:
414
+ raise TomlParseError(
415
+ path=str(config_path),
416
+ detail=str(exc)
417
+ ) from exc
418
+
419
+ return validate_sft_config(raw_config)
420
+
421
+
422
+ def load_and_validate_rl(config_path: Path) -> dict[str, Any]:
423
+ """Load and validate an RL TOML configuration file.
424
+
425
+ Args:
426
+ config_path: Path to TOML configuration file
427
+
428
+ Returns:
429
+ Validated configuration dictionary
430
+
431
+ Raises:
432
+ TomlParseError: If TOML parsing fails
433
+ InvalidRLConfigError: If validation fails
434
+ """
435
+ try:
436
+ raw_config = load_toml(config_path)
437
+ except Exception as exc:
438
+ raise TomlParseError(
439
+ path=str(config_path),
440
+ detail=str(exc)
441
+ ) from exc
442
+
443
+ return validate_rl_config(raw_config)
synth_ai/cli/demo.py CHANGED
@@ -1,165 +1,5 @@
1
- #!/usr/bin/env python3
2
- """
3
- CLI: interactive launcher for example demos and RL demo helpers.
4
-
5
- - `synth-ai demo` (no subcommand) -> initialize RL demo files into ./synth_demo/
6
- - `synth-ai demo deploy|configure|run` -> invoke RL demo helpers directly.
7
- """
8
-
9
1
  from __future__ import annotations
10
2
 
11
- import importlib
12
- import os
13
- import subprocess
14
- from pathlib import Path
15
- from typing import Any, cast
16
-
17
- import click
18
- from click.exceptions import Exit
19
-
20
- demo_commands = cast(
21
- Any, importlib.import_module("synth_ai.demos.core.cli")
22
- )
23
-
24
-
25
- def _find_demo_scripts(root: Path) -> list[Path]:
26
- if not root.exists():
27
- return []
28
- return sorted([p for p in root.rglob("run_demo.sh") if p.is_file()])
29
-
30
-
31
- def _run_demo_command(func, *args, **kwargs) -> None:
32
- """Invoke a demo command and exit via Click on non-zero status codes."""
33
-
34
- try:
35
- result = func(*args, **kwargs)
36
- except SystemExit as exc: # pragma: no cover - defensive
37
- raise Exit(exc.code or 1) from exc
38
-
39
- if result is None:
40
- return
41
-
42
- try:
43
- code = int(result)
44
- except (TypeError, ValueError):
45
- return
46
- if code != 0:
47
- raise Exit(code)
48
-
49
-
50
- def register(cli):
51
- @cli.group("demo", invoke_without_command=True)
52
- @click.option(
53
- "--force", is_flag=True, help="Overwrite existing files in CWD when initializing demo"
54
- )
55
- @click.option("--list", "list_only", is_flag=True, help="List available legacy demos and exit")
56
- @click.option("-f", "filter_term", default="", help="Filter legacy demos by substring")
57
- @click.pass_context
58
- def demo(ctx: click.Context, force: bool, list_only: bool, filter_term: str):
59
- """Demo helpers.
60
-
61
- - Default (no subcommand): initialize RL demo files into ./synth_demo/ (alias of rl_demo init)
62
- - Legacy mode: with --list, find and run examples/*/run_demo.sh
63
- - New RL demo subcommands: deploy, configure, run
64
- """
65
- if ctx.invoked_subcommand is not None:
66
- return
67
-
68
- # If explicitly asked to list legacy demos, show interactive picker
69
- if list_only:
70
- repo_root = Path(os.getcwd())
71
- examples_dir = repo_root / "examples"
72
- demos = _find_demo_scripts(examples_dir)
73
- if filter_term:
74
- demos = [p for p in demos if filter_term.lower() in str(p).lower()]
75
-
76
- if not demos:
77
- click.echo("No run_demo.sh scripts found under examples/.")
78
- return
79
-
80
- click.echo("Available demos:")
81
- for idx, p in enumerate(demos, start=1):
82
- click.echo(f" {idx}. {p.relative_to(repo_root)}")
83
- click.echo("")
84
-
85
- def _validate_choice(val: str) -> int:
86
- try:
87
- i = int(val)
88
- except Exception as err:
89
- raise click.BadParameter("Enter a number from the list") from err
90
- if i < 1 or i > len(demos):
91
- raise click.BadParameter(f"Choose a number between 1 and {len(demos)}")
92
- return i
93
-
94
- choice = click.prompt("Select a demo to run", value_proc=_validate_choice)
95
- script = demos[choice - 1]
96
-
97
- click.echo("")
98
- click.echo(f"šŸš€ Running {script.relative_to(repo_root)}\n")
99
-
100
- try:
101
- subprocess.run(["bash", str(script)], check=True)
102
- except subprocess.CalledProcessError as e:
103
- click.echo(f"āŒ Demo exited with non-zero status: {e.returncode}")
104
- except KeyboardInterrupt:
105
- click.echo("\nšŸ›‘ Demo interrupted by user")
106
- return
107
-
108
- # Default: initialize RL demo files via new command
109
- _run_demo_command(demo_commands.init, force=force)
110
-
111
- # (prepare command removed; configure now prepares baseline TOML)
112
-
113
- # Help pyright understand dynamic Click group attributes
114
- _dg = cast(Any, demo)
115
-
116
- @_dg.command("deploy")
117
- @click.option("--local", is_flag=True, help="Run local FastAPI instead of Modal deploy")
118
- @click.option(
119
- "--app",
120
- type=click.Path(),
121
- default=None,
122
- help="Path to Modal app.py for uv run modal deploy",
123
- )
124
- @click.option("--name", type=str, default="synth-math-demo", help="Modal app name")
125
- @click.option(
126
- "--script",
127
- type=click.Path(),
128
- default=None,
129
- help="Path to deploy_task_app.sh (optional legacy)",
130
- )
131
- def demo_deploy(local: bool, app: str | None, name: str, script: str | None):
132
- _run_demo_command(
133
- demo_commands.deploy,
134
- local=local,
135
- app=app,
136
- name=name,
137
- script=script,
138
- )
139
-
140
- @_dg.command("configure")
141
- def demo_configure():
142
- _run_demo_command(demo_commands.run)
143
-
144
- @_dg.command("setup")
145
- def demo_setup():
146
- _run_demo_command(demo_commands.setup)
147
-
148
- @_dg.command("run")
149
- @click.option("--batch-size", type=int, default=None)
150
- @click.option("--group-size", type=int, default=None)
151
- @click.option("--model", type=str, default=None)
152
- @click.option("--timeout", type=int, default=600)
153
- def demo_run(batch_size: int | None, group_size: int | None, model: str | None, timeout: int):
154
- _run_demo_command(
155
- demo_commands.run,
156
- batch_size=batch_size,
157
- group_size=group_size,
158
- model=model,
159
- timeout=timeout,
160
- )
3
+ from synth_ai.cli.commands.demo.core import register
161
4
 
162
- @cli.command("setup")
163
- def setup_alias():
164
- """Perform SDK handshake and write keys to .env."""
165
- _run_demo_command(demo_commands.setup)
5
+ __all__ = ["register"]
@@ -0,0 +1,28 @@
1
+ from __future__ import annotations
2
+
3
+ from .core import command, get_command
4
+ from .errors import (
5
+ DeployCliError,
6
+ EnvFileDiscoveryError,
7
+ EnvironmentKeyLoadError,
8
+ EnvKeyPreflightError,
9
+ MissingEnvironmentApiKeyError,
10
+ ModalCliResolutionError,
11
+ ModalExecutionError,
12
+ TaskAppNotFoundError,
13
+ )
14
+ from .validation import validate_deploy_options
15
+
16
+ __all__ = [
17
+ "command",
18
+ "get_command",
19
+ "DeployCliError",
20
+ "MissingEnvironmentApiKeyError",
21
+ "EnvironmentKeyLoadError",
22
+ "EnvFileDiscoveryError",
23
+ "TaskAppNotFoundError",
24
+ "ModalCliResolutionError",
25
+ "ModalExecutionError",
26
+ "EnvKeyPreflightError",
27
+ "validate_deploy_options",
28
+ ]
@@ -0,0 +1,5 @@
1
+ from __future__ import annotations
2
+
3
+ from synth_ai.cli.commands.deploy.core import command, get_command
4
+
5
+ __all__ = ["command", "get_command"]
@@ -0,0 +1,23 @@
1
+ from __future__ import annotations
2
+
3
+ from synth_ai.cli.commands.deploy.errors import (
4
+ DeployCliError,
5
+ EnvFileDiscoveryError,
6
+ EnvironmentKeyLoadError,
7
+ EnvKeyPreflightError,
8
+ MissingEnvironmentApiKeyError,
9
+ ModalCliResolutionError,
10
+ ModalExecutionError,
11
+ TaskAppNotFoundError,
12
+ )
13
+
14
+ __all__ = [
15
+ "DeployCliError",
16
+ "MissingEnvironmentApiKeyError",
17
+ "EnvironmentKeyLoadError",
18
+ "EnvFileDiscoveryError",
19
+ "TaskAppNotFoundError",
20
+ "ModalCliResolutionError",
21
+ "ModalExecutionError",
22
+ "EnvKeyPreflightError",
23
+ ]
@@ -0,0 +1,5 @@
1
+ from __future__ import annotations
2
+
3
+ from synth_ai.cli.commands.deploy.validation import validate_deploy_options
4
+
5
+ __all__ = ["validate_deploy_options"]