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.
- examples/README.md +1 -0
- examples/analyze_semantic_words.sh +2 -2
- examples/blog_posts/pokemon_vl/README.md +98 -0
- examples/blog_posts/pokemon_vl/configs/eval_qwen3_vl.toml +25 -0
- examples/blog_posts/pokemon_vl/configs/eval_rl_final.toml +24 -0
- examples/blog_posts/pokemon_vl/configs/filter_high_reward.toml +10 -0
- examples/blog_posts/pokemon_vl/configs/train_rl_from_sft.toml +42 -0
- examples/blog_posts/pokemon_vl/configs/train_sft_qwen4b_vl.toml +40 -0
- examples/blog_posts/warming_up_to_rl/README.md +158 -0
- examples/blog_posts/warming_up_to_rl/configs/eval_ft_qwen4b.toml +25 -0
- examples/blog_posts/warming_up_to_rl/configs/eval_groq_qwen32b.toml +25 -0
- examples/blog_posts/warming_up_to_rl/configs/eval_openai_gpt_oss_120b.toml +29 -0
- examples/blog_posts/warming_up_to_rl/configs/filter_high_reward_dataset.toml +10 -0
- examples/blog_posts/warming_up_to_rl/configs/train_rl_from_sft.toml +41 -0
- examples/blog_posts/warming_up_to_rl/configs/train_sft_qwen4b.toml +40 -0
- examples/dev/qwen3_32b_qlora_4xh100.toml +5 -0
- examples/multi_step/SFT_README.md +147 -0
- examples/multi_step/configs/crafter_rl_outcome.toml +1 -1
- examples/multi_step/configs/crafter_rl_stepwise_hosted_judge.toml +73 -115
- examples/multi_step/configs/crafter_rl_stepwise_shaped.toml +1 -1
- examples/multi_step/configs/crafter_rl_stepwise_simple.toml +1 -1
- examples/multi_step/configs/crafter_rl_stepwise_simple_NEW_FORMAT.toml +105 -0
- examples/multi_step/configs/crafter_sft_qwen30b_lora.toml +62 -0
- examples/multi_step/configs/verilog_rl_lora.toml +80 -123
- examples/multi_step/convert_traces_to_sft.py +84 -0
- examples/multi_step/run_sft_qwen30b.sh +45 -0
- examples/qwen_coder/configs/coder_lora_30b.toml +1 -2
- examples/qwen_coder/configs/coder_lora_4b.toml +5 -1
- examples/qwen_coder/configs/coder_lora_small.toml +1 -2
- examples/qwen_vl/BUGS_AND_FIXES.md +232 -0
- examples/qwen_vl/IMAGE_VALIDATION_COMPLETE.md +271 -0
- examples/qwen_vl/IMAGE_VALIDATION_SUMMARY.md +260 -0
- examples/qwen_vl/INFERENCE_SFT_TESTS.md +412 -0
- examples/qwen_vl/NEXT_STEPS_2B.md +325 -0
- examples/qwen_vl/QUICKSTART.md +327 -0
- examples/qwen_vl/QUICKSTART_RL_VISION.md +110 -0
- examples/qwen_vl/README.md +152 -0
- examples/qwen_vl/RL_VISION_COMPLETE.md +475 -0
- examples/qwen_vl/RL_VISION_TESTING.md +333 -0
- examples/qwen_vl/SDK_VISION_INTEGRATION.md +328 -0
- examples/qwen_vl/SETUP_COMPLETE.md +274 -0
- examples/qwen_vl/VISION_TESTS_COMPLETE.md +489 -0
- examples/qwen_vl/VLM_PIPELINE_COMPLETE.md +242 -0
- examples/qwen_vl/__init__.py +2 -0
- examples/qwen_vl/collect_data_via_cli.md +415 -0
- examples/qwen_vl/collect_vision_traces.py +368 -0
- examples/qwen_vl/configs/crafter_rl_vision_qwen3vl4b.toml +110 -0
- examples/qwen_vl/configs/crafter_vlm_sft_example.toml +59 -0
- examples/qwen_vl/configs/eval_gpt4o_mini_vision.toml +26 -0
- examples/qwen_vl/configs/eval_gpt4o_vision_proper.toml +29 -0
- examples/qwen_vl/configs/eval_gpt5nano_vision.toml +26 -0
- examples/qwen_vl/configs/eval_qwen3vl_vision.toml +26 -0
- examples/qwen_vl/configs/filter_qwen3vl_sft.toml +49 -0
- examples/qwen_vl/configs/filter_vision_sft.toml +52 -0
- examples/qwen_vl/configs/filter_vision_test.toml +8 -0
- examples/qwen_vl/configs/sft_qwen3_vl_2b_test.toml +54 -0
- examples/qwen_vl/crafter_gpt5nano_agent.py +308 -0
- examples/qwen_vl/crafter_qwen_vl_agent.py +300 -0
- examples/qwen_vl/run_vision_comparison.sh +61 -0
- examples/qwen_vl/run_vision_sft_pipeline.sh +175 -0
- examples/qwen_vl/test_image_validation.py +201 -0
- examples/qwen_vl/test_sft_vision_data.py +110 -0
- examples/rl/README.md +6 -6
- examples/rl/configs/eval_base_qwen.toml +17 -0
- examples/rl/configs/eval_rl_qwen.toml +13 -0
- examples/rl/configs/rl_from_base_qwen.toml +62 -0
- examples/rl/configs/rl_from_base_qwen17.toml +79 -0
- examples/rl/configs/rl_from_ft_qwen.toml +37 -0
- examples/rl/run_eval.py +436 -0
- examples/rl/run_rl_and_save.py +111 -0
- examples/rl/task_app/README.md +21 -0
- examples/rl/task_app/math_single_step.py +990 -0
- examples/rl/task_app/math_task_app.py +111 -0
- examples/run_crafter_demo.sh +2 -2
- examples/sft/README.md +6 -6
- examples/sft/configs/crafter_fft_qwen0p6b.toml +7 -2
- examples/sft/configs/crafter_lora_qwen0p6b.toml +7 -3
- examples/sft/evaluate.py +2 -4
- examples/sft/export_dataset.py +7 -4
- examples/swe/task_app/README.md +33 -3
- examples/swe/task_app/grpo_swe_mini.py +4 -1
- examples/swe/task_app/grpo_swe_mini_task_app.py +0 -12
- examples/swe/task_app/hosted/envs/crafter/react_agent.py +1 -1
- examples/swe/task_app/hosted/envs/mini_swe/environment.py +50 -23
- examples/swe/task_app/hosted/inference/openai_client.py +4 -4
- examples/swe/task_app/hosted/policy_routes.py +0 -2
- examples/swe/task_app/hosted/rollout.py +0 -8
- examples/swe/task_app/morph_backend.py +178 -0
- examples/task_apps/crafter/task_app/README.md +1 -1
- examples/task_apps/crafter/task_app/grpo_crafter.py +70 -10
- examples/task_apps/crafter/task_app/grpo_crafter_task_app.py +1 -1
- examples/task_apps/crafter/task_app/synth_envs_hosted/envs/crafter/policy.py +63 -27
- examples/task_apps/crafter/task_app/synth_envs_hosted/envs/crafter/react_agent.py +1 -2
- examples/task_apps/crafter/task_app/synth_envs_hosted/inference/openai_client.py +48 -50
- examples/task_apps/crafter/task_app/synth_envs_hosted/policy_routes.py +75 -36
- examples/task_apps/crafter/task_app/synth_envs_hosted/rollout.py +31 -15
- examples/task_apps/enron/__init__.py +1 -0
- examples/task_apps/enron/task_app/grpo_enron_task_app.py +1 -1
- examples/task_apps/math/README.md +1 -2
- examples/task_apps/pokemon_red/README.md +3 -4
- examples/task_apps/pokemon_red/eval_image_only_gpt4o.toml +6 -5
- examples/task_apps/pokemon_red/eval_pokemon_red_policy.py +1 -2
- examples/task_apps/pokemon_red/task_app.py +36 -5
- examples/task_apps/sokoban/README.md +2 -3
- examples/task_apps/verilog/eval_groq_qwen32b.toml +12 -14
- examples/task_apps/verilog/task_app/grpo_verilog_task_app.py +1 -1
- examples/vlm/README.md +3 -3
- examples/vlm/configs/crafter_vlm_gpt4o.toml +5 -0
- examples/vlm/crafter_openai_vlm_agent.py +3 -5
- examples/vlm/filter_image_rows.py +1 -1
- examples/vlm/run_crafter_vlm_benchmark.py +2 -2
- examples/warming_up_to_rl/_utils.py +92 -0
- examples/warming_up_to_rl/analyze_trace_db.py +1 -1
- examples/warming_up_to_rl/configs/crafter_fft.toml +5 -0
- examples/warming_up_to_rl/configs/eval_fft_qwen4b.toml +2 -0
- examples/warming_up_to_rl/configs/eval_groq_qwen32b.toml +2 -0
- examples/warming_up_to_rl/configs/eval_modal_qwen4b.toml +2 -1
- examples/warming_up_to_rl/configs/rl_from_base_qwen4b.toml +2 -1
- examples/warming_up_to_rl/configs/rl_from_ft.toml +2 -0
- examples/warming_up_to_rl/export_trace_sft.py +174 -60
- examples/warming_up_to_rl/readme.md +63 -132
- examples/warming_up_to_rl/run_fft_and_save.py +1 -1
- examples/warming_up_to_rl/run_local_rollout_traced.py +1 -1
- examples/warming_up_to_rl/run_rl_and_save.py +1 -1
- examples/warming_up_to_rl/task_app/README.md +42 -0
- examples/warming_up_to_rl/task_app/grpo_crafter.py +827 -0
- examples/warming_up_to_rl/task_app/grpo_crafter_task_app.py +135 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/README.md +173 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/__init__.py +5 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/branching.py +143 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/environment_routes.py +1226 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/__init__.py +1 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/__init__.py +6 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/app.py +1 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/environment.py +522 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/policy.py +454 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/react_agent.py +108 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/shared.py +305 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/tools.py +47 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/hosted_app.py +204 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/inference/__init__.py +5 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/inference/openai_client.py +618 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/main.py +100 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/policy_routes.py +1084 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/registry.py +195 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/rollout.py +1861 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/storage/__init__.py +5 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/storage/volume.py +211 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/test_agents.py +161 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/test_service.py +137 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/utils.py +62 -0
- examples/workflows/math_rl/configs/rl_from_base_qwen.toml +27 -0
- examples/workflows/math_rl/configs/rl_from_base_qwen17.toml +5 -0
- synth_ai/__init__.py +44 -30
- synth_ai/_utils/__init__.py +47 -0
- synth_ai/_utils/base_url.py +10 -0
- synth_ai/_utils/http.py +10 -0
- synth_ai/_utils/prompts.py +10 -0
- synth_ai/_utils/task_app_state.py +12 -0
- synth_ai/_utils/user_config.py +10 -0
- synth_ai/api/models/supported.py +144 -7
- synth_ai/api/train/__init__.py +13 -1
- synth_ai/api/train/builders.py +9 -3
- synth_ai/api/train/cli.py +155 -17
- synth_ai/api/train/config_finder.py +18 -11
- synth_ai/api/train/configs/__init__.py +8 -1
- synth_ai/api/train/configs/rl.py +32 -7
- synth_ai/api/train/configs/sft.py +6 -2
- synth_ai/api/train/configs/shared.py +59 -2
- synth_ai/api/train/env_resolver.py +13 -10
- synth_ai/auth/credentials.py +119 -0
- synth_ai/cli/__init__.py +61 -69
- synth_ai/cli/_modal_wrapper.py +7 -5
- synth_ai/cli/_typer_patch.py +0 -2
- synth_ai/cli/_validate_task_app.py +22 -4
- synth_ai/cli/commands/__init__.py +17 -0
- synth_ai/cli/commands/demo/__init__.py +6 -0
- synth_ai/cli/commands/demo/core.py +163 -0
- synth_ai/cli/commands/deploy/__init__.py +23 -0
- synth_ai/cli/commands/deploy/core.py +614 -0
- synth_ai/cli/commands/deploy/errors.py +72 -0
- synth_ai/cli/commands/deploy/validation.py +11 -0
- synth_ai/cli/commands/eval/__init__.py +19 -0
- synth_ai/cli/commands/eval/core.py +1109 -0
- synth_ai/cli/commands/eval/errors.py +81 -0
- synth_ai/cli/commands/eval/validation.py +133 -0
- synth_ai/cli/commands/filter/__init__.py +12 -0
- synth_ai/cli/commands/filter/core.py +388 -0
- synth_ai/cli/commands/filter/errors.py +55 -0
- synth_ai/cli/commands/filter/validation.py +77 -0
- synth_ai/cli/commands/help/__init__.py +177 -0
- synth_ai/cli/commands/help/core.py +73 -0
- synth_ai/cli/commands/status/__init__.py +64 -0
- synth_ai/cli/commands/status/client.py +192 -0
- synth_ai/cli/commands/status/config.py +92 -0
- synth_ai/cli/commands/status/errors.py +20 -0
- synth_ai/cli/commands/status/formatters.py +164 -0
- synth_ai/cli/commands/status/subcommands/__init__.py +9 -0
- synth_ai/cli/commands/status/subcommands/files.py +79 -0
- synth_ai/cli/commands/status/subcommands/jobs.py +334 -0
- synth_ai/cli/commands/status/subcommands/models.py +79 -0
- synth_ai/cli/commands/status/subcommands/runs.py +81 -0
- synth_ai/cli/commands/status/subcommands/summary.py +47 -0
- synth_ai/cli/commands/status/utils.py +114 -0
- synth_ai/cli/commands/train/__init__.py +53 -0
- synth_ai/cli/commands/train/core.py +21 -0
- synth_ai/cli/commands/train/errors.py +117 -0
- synth_ai/cli/commands/train/judge_schemas.py +199 -0
- synth_ai/cli/commands/train/judge_validation.py +304 -0
- synth_ai/cli/commands/train/validation.py +443 -0
- synth_ai/cli/demo.py +2 -162
- synth_ai/cli/deploy/__init__.py +28 -0
- synth_ai/cli/deploy/core.py +5 -0
- synth_ai/cli/deploy/errors.py +23 -0
- synth_ai/cli/deploy/validation.py +5 -0
- synth_ai/cli/eval/__init__.py +36 -0
- synth_ai/cli/eval/core.py +5 -0
- synth_ai/cli/eval/errors.py +31 -0
- synth_ai/cli/eval/validation.py +5 -0
- synth_ai/cli/filter/__init__.py +28 -0
- synth_ai/cli/filter/core.py +5 -0
- synth_ai/cli/filter/errors.py +23 -0
- synth_ai/cli/filter/validation.py +5 -0
- synth_ai/cli/legacy_root_backup.py +3 -1
- synth_ai/cli/lib/__init__.py +10 -0
- synth_ai/cli/lib/task_app_discovery.py +7 -0
- synth_ai/cli/lib/task_app_env.py +518 -0
- synth_ai/cli/modal_serve/__init__.py +12 -0
- synth_ai/cli/modal_serve/core.py +14 -0
- synth_ai/cli/modal_serve/errors.py +8 -0
- synth_ai/cli/modal_serve/validation.py +11 -0
- synth_ai/cli/recent.py +2 -1
- synth_ai/cli/serve/__init__.py +12 -0
- synth_ai/cli/serve/core.py +14 -0
- synth_ai/cli/serve/errors.py +8 -0
- synth_ai/cli/serve/validation.py +11 -0
- synth_ai/cli/setup.py +21 -0
- synth_ai/cli/status.py +7 -126
- synth_ai/cli/task_app_deploy.py +7 -0
- synth_ai/cli/task_app_list.py +25 -0
- synth_ai/cli/task_app_modal_serve.py +11 -0
- synth_ai/cli/task_app_serve.py +11 -0
- synth_ai/cli/task_apps.py +110 -1499
- synth_ai/cli/traces.py +1 -1
- synth_ai/cli/train/__init__.py +12 -0
- synth_ai/cli/train/core.py +21 -0
- synth_ai/cli/train/errors.py +8 -0
- synth_ai/cli/train/validation.py +24 -0
- synth_ai/cli/train.py +5 -0
- synth_ai/cli/turso.py +1 -1
- synth_ai/cli/watch.py +1 -1
- synth_ai/demos/__init__.py +10 -0
- synth_ai/demos/core/__init__.py +28 -1
- synth_ai/demos/crafter/__init__.py +1 -0
- synth_ai/demos/crafter/crafter_fft_4b.toml +55 -0
- synth_ai/demos/crafter/grpo_crafter_task_app.py +185 -0
- synth_ai/demos/crafter/rl_from_base_qwen4b.toml +74 -0
- synth_ai/demos/demo_registry.py +176 -0
- synth_ai/demos/demo_task_apps/crafter/grpo_crafter_task_app.py +1 -1
- synth_ai/demos/math/__init__.py +1 -0
- synth_ai/demos/math/_common.py +16 -0
- synth_ai/demos/math/app.py +38 -0
- synth_ai/demos/math/config.toml +76 -0
- synth_ai/demos/math/deploy_modal.py +54 -0
- synth_ai/demos/math/modal_task_app.py +702 -0
- synth_ai/demos/math/task_app_entry.py +51 -0
- synth_ai/environments/environment/core.py +7 -1
- synth_ai/environments/examples/bandit/engine.py +0 -1
- synth_ai/environments/examples/bandit/environment.py +0 -1
- synth_ai/environments/examples/red/engine.py +33 -12
- synth_ai/environments/examples/red/engine_helpers/reward_components.py +151 -179
- synth_ai/environments/examples/red/environment.py +26 -0
- synth_ai/environments/examples/red/trace_hooks_v3.py +168 -0
- synth_ai/environments/examples/wordle/environment.py +0 -1
- synth_ai/evals/base.py +16 -5
- synth_ai/evals/client.py +1 -1
- synth_ai/http.py +8 -22
- synth_ai/inference/client.py +1 -1
- synth_ai/judge_schemas.py +4 -5
- synth_ai/learning/client.py +1 -1
- synth_ai/learning/health.py +1 -1
- synth_ai/learning/jobs.py +1 -1
- synth_ai/learning/rl/client.py +4 -2
- synth_ai/learning/rl/env_keys.py +1 -1
- synth_ai/learning/rl/secrets.py +1 -1
- synth_ai/learning/sft/client.py +1 -1
- synth_ai/learning/sft/data.py +407 -4
- synth_ai/learning/validators.py +4 -1
- synth_ai/streaming/__init__.py +29 -0
- synth_ai/streaming/config.py +94 -0
- synth_ai/streaming/handlers.py +469 -0
- synth_ai/streaming/streamer.py +301 -0
- synth_ai/streaming/types.py +95 -0
- synth_ai/task/apps/__init__.py +4 -2
- synth_ai/task/config.py +6 -4
- synth_ai/task/rubrics/__init__.py +1 -2
- synth_ai/task/rubrics/loaders.py +14 -10
- synth_ai/task/rubrics.py +219 -0
- synth_ai/task/trace_correlation_helpers.py +24 -11
- synth_ai/task/tracing_utils.py +14 -3
- synth_ai/task/validators.py +0 -1
- synth_ai/tracing_v3/abstractions.py +3 -3
- synth_ai/tracing_v3/config.py +15 -13
- synth_ai/tracing_v3/constants.py +21 -0
- synth_ai/tracing_v3/db_config.py +3 -1
- synth_ai/tracing_v3/decorators.py +10 -7
- synth_ai/tracing_v3/llm_call_record_helpers.py +5 -5
- synth_ai/tracing_v3/migration_helper.py +1 -2
- synth_ai/tracing_v3/session_tracer.py +7 -7
- synth_ai/tracing_v3/storage/base.py +29 -29
- synth_ai/tracing_v3/storage/config.py +3 -3
- synth_ai/tracing_v3/turso/daemon.py +8 -9
- synth_ai/tracing_v3/turso/native_manager.py +80 -72
- synth_ai/tracing_v3/utils.py +2 -2
- synth_ai/utils/__init__.py +101 -0
- synth_ai/utils/base_url.py +94 -0
- synth_ai/utils/cli.py +131 -0
- synth_ai/utils/env.py +294 -0
- synth_ai/utils/http.py +172 -0
- synth_ai/utils/modal.py +308 -0
- synth_ai/utils/process.py +212 -0
- synth_ai/utils/prompts.py +39 -0
- synth_ai/utils/sqld.py +122 -0
- synth_ai/utils/task_app_discovery.py +882 -0
- synth_ai/utils/task_app_env.py +186 -0
- synth_ai/utils/task_app_state.py +318 -0
- synth_ai/utils/user_config.py +137 -0
- synth_ai/v0/config/__init__.py +1 -5
- synth_ai/v0/config/base_url.py +1 -7
- synth_ai/v0/tracing/config.py +1 -1
- synth_ai/v0/tracing/decorators.py +1 -1
- synth_ai/v0/tracing/upload.py +1 -1
- synth_ai/v0/tracing_v1/config.py +1 -1
- synth_ai/v0/tracing_v1/decorators.py +1 -1
- synth_ai/v0/tracing_v1/upload.py +1 -1
- {synth_ai-0.2.14.dist-info ā synth_ai-0.2.17.dist-info}/METADATA +91 -32
- {synth_ai-0.2.14.dist-info ā synth_ai-0.2.17.dist-info}/RECORD +341 -154
- synth_ai/cli/man.py +0 -106
- synth_ai/cli/tui.py +0 -57
- synth_ai/compound/cais.py +0 -0
- synth_ai/core/experiment.py +0 -13
- synth_ai/core/system.py +0 -15
- synth_ai/demo_registry.py +0 -295
- synth_ai/handshake.py +0 -109
- synth_ai/tui/__init__.py +0 -5
- synth_ai/tui/__main__.py +0 -13
- synth_ai/tui/cli/__init__.py +0 -1
- synth_ai/tui/cli/query_experiments.py +0 -164
- synth_ai/tui/cli/query_experiments_v3.py +0 -164
- synth_ai/tui/dashboard.py +0 -906
- {synth_ai-0.2.14.dist-info ā synth_ai-0.2.17.dist-info}/WHEEL +0 -0
- {synth_ai-0.2.14.dist-info ā synth_ai-0.2.17.dist-info}/entry_points.txt +0 -0
- {synth_ai-0.2.14.dist-info ā synth_ai-0.2.17.dist-info}/licenses/LICENSE +0 -0
- {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
|
|
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
|
-
|
|
25
|
-
|
|
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
|
-
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
synth_ai/cli/_modal_wrapper.py
CHANGED
|
@@ -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
|
-
|
|
10
|
-
|
|
11
|
-
patch_typer_make_metavar()
|
|
10
|
+
module = importlib.import_module("synth_ai.cli._typer_patch")
|
|
12
11
|
except Exception:
|
|
13
|
-
|
|
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
|
-
|
synth_ai/cli/_typer_patch.py
CHANGED
|
@@ -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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
import importlib
|
|
7
|
+
from collections.abc import Callable
|
|
8
|
+
from typing import Any
|
|
9
9
|
|
|
10
|
-
|
|
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,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
|
+
]
|