synth-ai 0.2.9.dev7__py3-none-any.whl → 0.2.9.dev9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of synth-ai might be problematic. Click here for more details.
- examples/__init__.py +16 -0
- examples/crafter_debug_render.py +8 -11
- examples/qwen_coder/README.md +102 -0
- examples/qwen_coder/_shared.py +113 -0
- examples/qwen_coder/configs/coder_lora_30b.toml +61 -0
- examples/qwen_coder/configs/coder_lora_4b.toml +57 -0
- examples/qwen_coder/configs/coder_lora_small.toml +58 -0
- examples/qwen_coder/generate_dataset.py +98 -0
- examples/qwen_coder/infer_ft_smoke.py +64 -0
- examples/qwen_coder/infer_prod_proxy.py +73 -0
- examples/qwen_coder/infer_via_synth.py +87 -0
- examples/qwen_coder/scripts/infer_coder.sh +18 -0
- examples/qwen_coder/scripts/train_coder_30b.sh +21 -0
- examples/qwen_coder/sft_full_17b.py +103 -0
- examples/qwen_coder/sft_lora_30b.py +110 -0
- examples/qwen_coder/subset_jsonl.py +38 -0
- examples/qwen_coder/validate_jsonl.py +59 -0
- examples/rl/run_eval.py +36 -37
- examples/rl/run_rl_and_save.py +5 -5
- examples/rl/task_app/math_single_step.py +65 -43
- examples/rl/task_app/math_task_app.py +3 -3
- examples/sft/README.md +139 -0
- examples/sft/configs/crafter_fft_qwen0p6b.toml +44 -0
- examples/sft/configs/crafter_lora_qwen0p6b.toml +45 -0
- examples/sft/evaluate.py +117 -0
- examples/sft/export_dataset.py +117 -0
- examples/sft/generate_traces.py +162 -0
- examples/swe/__init__.py +12 -0
- examples/swe/task_app/README.md +105 -0
- examples/swe/task_app/__init__.py +2 -0
- examples/swe/task_app/grpo_swe_mini.py +571 -0
- examples/swe/task_app/grpo_swe_mini_task_app.py +136 -0
- examples/swe/task_app/hosted/README.md +173 -0
- examples/swe/task_app/hosted/__init__.py +5 -0
- examples/swe/task_app/hosted/branching.py +143 -0
- examples/swe/task_app/hosted/environment_routes.py +1289 -0
- examples/swe/task_app/hosted/envs/__init__.py +1 -0
- examples/swe/task_app/hosted/envs/crafter/__init__.py +6 -0
- examples/swe/task_app/hosted/envs/crafter/app.py +1 -0
- examples/swe/task_app/hosted/envs/crafter/environment.py +522 -0
- examples/swe/task_app/hosted/envs/crafter/policy.py +478 -0
- examples/swe/task_app/hosted/envs/crafter/react_agent.py +108 -0
- examples/swe/task_app/hosted/envs/crafter/shared.py +305 -0
- examples/swe/task_app/hosted/envs/crafter/tools.py +47 -0
- examples/swe/task_app/hosted/envs/mini_swe/__init__.py +8 -0
- examples/swe/task_app/hosted/envs/mini_swe/environment.py +1164 -0
- examples/swe/task_app/hosted/envs/mini_swe/policy.py +355 -0
- examples/swe/task_app/hosted/envs/mini_swe/shared.py +83 -0
- examples/swe/task_app/hosted/envs/mini_swe/tools.py +96 -0
- examples/swe/task_app/hosted/hosted_app.py +204 -0
- examples/swe/task_app/hosted/inference/__init__.py +5 -0
- examples/swe/task_app/hosted/inference/openai_client.py +618 -0
- examples/swe/task_app/hosted/main.py +100 -0
- examples/swe/task_app/hosted/policy_routes.py +1079 -0
- examples/swe/task_app/hosted/registry.py +195 -0
- examples/swe/task_app/hosted/rollout.py +1869 -0
- examples/swe/task_app/hosted/storage/__init__.py +5 -0
- examples/swe/task_app/hosted/storage/volume.py +211 -0
- examples/swe/task_app/hosted/test_agents.py +161 -0
- examples/swe/task_app/hosted/test_service.py +137 -0
- examples/swe/task_app/hosted/utils.py +62 -0
- examples/vlm/README.md +68 -0
- examples/vlm/configs/crafter_vlm_gpt4o.toml +44 -0
- examples/vlm/crafter_image_only_agent.py +207 -0
- examples/vlm/crafter_openai_vlm_agent.py +277 -0
- examples/vlm/filter_image_rows.py +63 -0
- examples/vlm/run_crafter_vlm_benchmark.py +316 -0
- examples/warming_up_to_rl/analyze_trace_db.py +5 -5
- examples/warming_up_to_rl/configs/rl_from_base_qwen4b.toml +11 -1
- examples/warming_up_to_rl/export_trace_sft.py +78 -21
- examples/warming_up_to_rl/groq_test.py +4 -4
- examples/warming_up_to_rl/manage_secrets.py +13 -18
- examples/warming_up_to_rl/run_eval.py +42 -44
- examples/warming_up_to_rl/run_fft_and_save.py +11 -16
- examples/warming_up_to_rl/run_local_rollout.py +1 -3
- examples/warming_up_to_rl/run_local_rollout_modal.py +2 -4
- examples/warming_up_to_rl/run_local_rollout_parallel.py +1 -4
- examples/warming_up_to_rl/run_local_rollout_traced.py +3 -5
- examples/warming_up_to_rl/run_rl_and_save.py +5 -6
- examples/warming_up_to_rl/run_rollout_remote.py +8 -10
- examples/warming_up_to_rl/task_app/README.md +6 -2
- examples/warming_up_to_rl/task_app/grpo_crafter.py +234 -35
- examples/warming_up_to_rl/task_app/grpo_crafter_task_app.py +2 -3
- examples/warming_up_to_rl/task_app/synth_envs_hosted/__init__.py +1 -1
- examples/warming_up_to_rl/task_app/synth_envs_hosted/branching.py +9 -11
- examples/warming_up_to_rl/task_app/synth_envs_hosted/environment_routes.py +131 -114
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/environment.py +101 -41
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/policy.py +73 -51
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/react_agent.py +14 -6
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/shared.py +16 -16
- examples/warming_up_to_rl/task_app/synth_envs_hosted/hosted_app.py +32 -34
- examples/warming_up_to_rl/task_app/synth_envs_hosted/inference/openai_client.py +94 -31
- examples/warming_up_to_rl/task_app/synth_envs_hosted/main.py +0 -2
- examples/warming_up_to_rl/task_app/synth_envs_hosted/policy_routes.py +303 -203
- examples/warming_up_to_rl/task_app/synth_envs_hosted/registry.py +21 -23
- examples/warming_up_to_rl/task_app/synth_envs_hosted/rollout.py +328 -225
- examples/warming_up_to_rl/task_app/synth_envs_hosted/storage/volume.py +13 -13
- examples/warming_up_to_rl/task_app/synth_envs_hosted/test_agents.py +1 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/test_service.py +1 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/utils.py +4 -3
- synth/__init__.py +14 -0
- synth_ai/__init__.py +26 -4
- synth_ai/api/models/supported.py +376 -0
- synth_ai/api/train/builders.py +128 -21
- synth_ai/api/train/cli.py +80 -64
- synth_ai/api/train/config_finder.py +7 -2
- synth_ai/api/train/env_resolver.py +1 -1
- synth_ai/api/train/pollers.py +2 -1
- synth_ai/api/train/supported_algos.py +139 -0
- synth_ai/api/train/task_app.py +1 -2
- synth_ai/api/train/utils.py +13 -44
- synth_ai/cli/__init__.py +8 -0
- synth_ai/cli/_modal_wrapper.py +28 -0
- synth_ai/cli/_typer_patch.py +49 -0
- synth_ai/cli/balance.py +1 -2
- synth_ai/cli/calc.py +1 -1
- synth_ai/cli/demo.py +2 -1
- synth_ai/cli/recent.py +2 -2
- synth_ai/cli/rl_demo.py +2 -1
- synth_ai/cli/root.py +11 -13
- synth_ai/cli/status.py +2 -2
- synth_ai/cli/task_apps.py +529 -179
- synth_ai/cli/traces.py +6 -4
- synth_ai/cli/watch.py +12 -18
- synth_ai/demo_registry.py +1 -1
- synth_ai/demos/core/cli.py +36 -43
- synth_ai/demos/demo_task_apps/__init__.py +3 -3
- synth_ai/demos/demo_task_apps/core.py +17 -25
- synth_ai/demos/demo_task_apps/crafter/grpo_crafter_task_app.py +3 -4
- synth_ai/demos/demo_task_apps/math/app.py +2 -1
- synth_ai/demos/demo_task_apps/math/deploy_modal.py +3 -4
- synth_ai/demos/demo_task_apps/math/modal_task_app.py +16 -18
- synth_ai/demos/demo_task_apps/math/task_app_entry.py +0 -1
- synth_ai/environments/examples/crafter_classic/environment.py +76 -1
- synth_ai/environments/reproducibility/tree.py +2 -5
- synth_ai/environments/service/app.py +11 -12
- synth_ai/environments/service/core_routes.py +4 -7
- synth_ai/environments/stateful/engine.py +1 -1
- synth_ai/environments/tasks/core.py +1 -0
- synth_ai/environments/tasks/filters.py +5 -6
- synth_ai/environments/tasks/utils.py +4 -5
- synth_ai/handshake.py +9 -9
- synth_ai/http.py +1 -1
- synth_ai/http_client.py +18 -10
- synth_ai/inference/client.py +15 -5
- synth_ai/jobs/client.py +78 -83
- synth_ai/learning/__init__.py +41 -6
- synth_ai/learning/algorithms.py +14 -0
- synth_ai/learning/client.py +91 -24
- synth_ai/learning/config.py +2 -38
- synth_ai/learning/ft_client.py +4 -59
- synth_ai/learning/health.py +5 -6
- synth_ai/learning/jobs.py +31 -47
- synth_ai/{rl → learning/rl}/__init__.py +14 -4
- synth_ai/learning/rl/client.py +267 -0
- synth_ai/learning/rl/config.py +31 -0
- synth_ai/{rl → learning/rl}/contracts.py +5 -8
- synth_ai/{rl → learning/rl}/env_keys.py +39 -15
- synth_ai/learning/rl/secrets.py +13 -0
- synth_ai/learning/rl_client.py +2 -281
- synth_ai/learning/sft/__init__.py +29 -0
- synth_ai/learning/sft/client.py +68 -0
- synth_ai/learning/sft/config.py +270 -0
- synth_ai/learning/sft/data.py +295 -0
- synth_ai/learning/sse.py +25 -24
- synth_ai/learning/validators.py +25 -28
- synth_ai/lm/__init__.py +21 -47
- synth_ai/main.py +6 -0
- synth_ai/task/__init__.py +25 -27
- synth_ai/task/apps/__init__.py +7 -8
- synth_ai/task/auth.py +8 -8
- synth_ai/task/client.py +14 -14
- synth_ai/task/contracts.py +36 -35
- synth_ai/task/datasets.py +6 -5
- synth_ai/task/errors.py +10 -10
- synth_ai/task/health.py +17 -9
- synth_ai/task/json.py +58 -23
- synth_ai/task/proxy.py +13 -9
- synth_ai/task/rubrics.py +16 -15
- synth_ai/task/server.py +12 -12
- synth_ai/task/tracing_utils.py +4 -4
- synth_ai/task/vendors.py +5 -6
- synth_ai/tracing_v3/__init__.py +2 -0
- synth_ai/tracing_v3/abstractions.py +21 -4
- synth_ai/tracing_v3/decorators.py +18 -16
- synth_ai/tracing_v3/hooks.py +5 -5
- synth_ai/tracing_v3/llm_call_record_helpers.py +6 -6
- synth_ai/tracing_v3/session_tracer.py +40 -14
- synth_ai/tracing_v3/storage/base.py +85 -0
- synth_ai/tracing_v3/storage/config.py +21 -8
- synth_ai/tracing_v3/storage/factory.py +10 -7
- synth_ai/tracing_v3/storage/utils.py +4 -2
- synth_ai/tracing_v3/turso/daemon.py +7 -2
- synth_ai/tracing_v3/turso/models.py +2 -2
- synth_ai/tracing_v3/turso/native_manager.py +1173 -0
- synth_ai/tracing_v3/utils.py +4 -4
- synth_ai/v0/api/__init__.py +8 -0
- synth_ai/v0/api/models/__init__.py +8 -0
- synth_ai/v0/api/models/supported.py +8 -0
- synth_ai/v0/config/__init__.py +15 -0
- synth_ai/v0/config/base_url.py +12 -0
- synth_ai/v0/lm/__init__.py +51 -0
- synth_ai/{lm → v0/lm}/caching/ephemeral.py +2 -2
- synth_ai/{lm → v0/lm}/caching/handler.py +4 -4
- synth_ai/{lm → v0/lm}/caching/initialize.py +1 -1
- synth_ai/{lm → v0/lm}/caching/persistent.py +1 -1
- synth_ai/{lm → v0/lm}/config.py +6 -1
- synth_ai/{lm → v0/lm}/core/all.py +9 -9
- synth_ai/{lm → v0/lm}/core/main.py +6 -6
- synth_ai/{lm → v0/lm}/core/main_v3.py +10 -10
- synth_ai/{lm → v0/lm}/core/synth_models.py +2 -14
- synth_ai/{lm → v0/lm}/core/vendor_clients.py +2 -2
- synth_ai/{lm → v0/lm}/overrides.py +2 -2
- synth_ai/{lm → v0/lm}/provider_support/anthropic.py +4 -4
- synth_ai/{lm → v0/lm}/provider_support/openai.py +5 -5
- synth_ai/{lm → v0/lm}/structured_outputs/handler.py +5 -5
- synth_ai/{lm → v0/lm}/structured_outputs/rehabilitate.py +1 -1
- synth_ai/{lm → v0/lm}/vendors/core/anthropic_api.py +9 -9
- synth_ai/{lm → v0/lm}/vendors/core/gemini_api.py +5 -5
- synth_ai/{lm → v0/lm}/vendors/core/mistral_api.py +5 -5
- synth_ai/{lm → v0/lm}/vendors/core/openai_api.py +10 -10
- synth_ai/{lm → v0/lm}/vendors/openai_standard.py +8 -8
- synth_ai/{lm → v0/lm}/vendors/openai_standard_responses.py +2 -2
- synth_ai/{lm → v0/lm}/vendors/supported/custom_endpoint.py +3 -3
- synth_ai/{lm → v0/lm}/vendors/supported/deepseek.py +2 -2
- synth_ai/{lm → v0/lm}/vendors/supported/grok.py +2 -2
- synth_ai/{lm → v0/lm}/vendors/supported/groq.py +1 -1
- synth_ai/{lm → v0/lm}/vendors/supported/ollama.py +1 -1
- synth_ai/{lm → v0/lm}/vendors/supported/openrouter.py +3 -3
- synth_ai/{lm → v0/lm}/vendors/supported/together.py +1 -1
- synth_ai/{lm → v0/lm}/vendors/synth_client.py +1 -1
- synth_ai/v0/tracing_v3/__init__.py +10 -0
- synth_ai/v0/tracing_v3/abstractions.py +3 -0
- synth_ai/v0/tracing_v3/decorators.py +3 -0
- synth_ai/v0/tracing_v3/llm_call_record_helpers.py +3 -0
- synth_ai/v0/tracing_v3/session_tracer.py +3 -0
- synth_ai-0.2.9.dev9.dist-info/METADATA +191 -0
- {synth_ai-0.2.9.dev7.dist-info → synth_ai-0.2.9.dev9.dist-info}/RECORD +268 -238
- {synth_ai-0.2.9.dev7.dist-info → synth_ai-0.2.9.dev9.dist-info}/top_level.txt +1 -0
- examples/common_old/backend.py +0 -20
- examples/evals_old/README.md +0 -98
- examples/evals_old/__init__.py +0 -6
- examples/evals_old/compare_models.py +0 -1038
- examples/evals_old/example_log.md +0 -145
- examples/evals_old/run_demo.sh +0 -126
- examples/evals_old/trace_analysis.py +0 -270
- examples/finetuning_old/_backup_synth_qwen/config.toml +0 -29
- examples/finetuning_old/_backup_synth_qwen/example_log.md +0 -324
- examples/finetuning_old/_backup_synth_qwen/filter_traces.py +0 -60
- examples/finetuning_old/_backup_synth_qwen/filter_traces_achievements.py +0 -243
- examples/finetuning_old/_backup_synth_qwen/purge_v3_traces.py +0 -109
- examples/finetuning_old/_backup_synth_qwen/react_agent_lm.py +0 -1924
- examples/finetuning_old/_backup_synth_qwen/readme.md +0 -49
- examples/finetuning_old/_backup_synth_qwen/run_crafter_qwen4b.py +0 -114
- examples/finetuning_old/_backup_synth_qwen/run_demo.sh +0 -195
- examples/finetuning_old/_backup_synth_qwen/sft_kickoff.py +0 -119
- examples/finetuning_old/synth_qwen_v1/README.md +0 -68
- examples/finetuning_old/synth_qwen_v1/filter_traces.py +0 -60
- examples/finetuning_old/synth_qwen_v1/filter_traces_achievements.py +0 -243
- examples/finetuning_old/synth_qwen_v1/finetune.py +0 -46
- examples/finetuning_old/synth_qwen_v1/hello_ft_model.py +0 -71
- examples/finetuning_old/synth_qwen_v1/infer.py +0 -36
- examples/finetuning_old/synth_qwen_v1/poll.py +0 -46
- examples/finetuning_old/synth_qwen_v1/prepare_data.py +0 -35
- examples/finetuning_old/synth_qwen_v1/purge_v3_traces.py +0 -109
- examples/finetuning_old/synth_qwen_v1/react_agent_lm.py +0 -1933
- examples/finetuning_old/synth_qwen_v1/run_crafter_sft_job.py +0 -210
- examples/finetuning_old/synth_qwen_v1/run_ft_job.py +0 -237
- examples/finetuning_old/synth_qwen_v1/upload_data.py +0 -34
- examples/finetuning_old/synth_qwen_v1/util.py +0 -152
- examples/rl_old/task_app.py +0 -1131
- examples/warming_up_to_rl/old/event_rewards.md +0 -234
- examples/warming_up_to_rl/old/notes.md +0 -73
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/filter_traces_sft_turso.py +0 -738
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/filter_traces_sft_turso.py +0 -580
- synth_ai/experimental/synth_oss.py +0 -445
- synth_ai/learning/filtering.py +0 -0
- synth_ai/learning/offline/dpo.py +0 -0
- synth_ai/learning/offline/providers.py +0 -7
- synth_ai/learning/offline/sft.py +0 -0
- synth_ai/learning/offline/shared.py +0 -0
- synth_ai/learning/online/grpo.py +0 -0
- synth_ai/learning/online/irft.py +0 -0
- synth_ai/learning/prompts/banking77_injection_eval.py +0 -168
- synth_ai/learning/prompts/gepa.py +0 -0
- synth_ai/learning/prompts/hello_world_in_context_injection_ex.py +0 -211
- synth_ai/learning/prompts/mipro.py +0 -289
- synth_ai/learning/prompts/random_search.py +0 -249
- synth_ai/learning/prompts/run_mipro_banking77.py +0 -172
- synth_ai/learning/prompts/run_random_search_banking77.py +0 -329
- synth_ai/rl/secrets.py +0 -19
- synth_ai/scripts/verify_rewards.py +0 -100
- synth_ai/tracing/__init__.py +0 -30
- synth_ai/tracing_v1/__init__.py +0 -33
- synth_ai/tracing_v3/turso/__init__.py +0 -25
- synth_ai/tracing_v3/turso/manager.py +0 -838
- synth_ai/zyk/__init__.py +0 -30
- synth_ai-0.2.9.dev7.dist-info/METADATA +0 -131
- /synth_ai/{lm → v0/lm}/caching/__init__.py +0 -0
- /synth_ai/{lm → v0/lm}/caching/constants.py +0 -0
- /synth_ai/{lm → v0/lm}/caching/dbs.py +0 -0
- /synth_ai/{lm → v0/lm}/constants.py +0 -0
- /synth_ai/{lm → v0/lm}/core/__init__.py +0 -0
- /synth_ai/{lm → v0/lm}/core/exceptions.py +0 -0
- /synth_ai/{lm → v0/lm}/cost/__init__.py +0 -0
- /synth_ai/{lm → v0/lm}/cost/monitor.py +0 -0
- /synth_ai/{lm → v0/lm}/cost/statefulness.py +0 -0
- /synth_ai/{lm → v0/lm}/injection.py +0 -0
- /synth_ai/{lm → v0/lm}/provider_support/__init__.py +0 -0
- /synth_ai/{lm → v0/lm}/provider_support/suppress_logging.py +0 -0
- /synth_ai/{lm → v0/lm}/structured_outputs/__init__.py +0 -0
- /synth_ai/{lm → v0/lm}/structured_outputs/inject.py +0 -0
- /synth_ai/{lm → v0/lm}/tools/__init__.py +0 -0
- /synth_ai/{lm → v0/lm}/tools/base.py +0 -0
- /synth_ai/{lm → v0/lm}/unified_interface.py +0 -0
- /synth_ai/{lm → v0/lm}/vendors/__init__.py +0 -0
- /synth_ai/{lm → v0/lm}/vendors/base.py +0 -0
- /synth_ai/{lm → v0/lm}/vendors/core/__init__.py +0 -0
- /synth_ai/{lm → v0/lm}/vendors/core/synth_dev_api.py +0 -0
- /synth_ai/{lm → v0/lm}/vendors/local/__init__.py +0 -0
- /synth_ai/{lm → v0/lm}/vendors/local/ollama.py +0 -0
- /synth_ai/{lm → v0/lm}/vendors/retries.py +0 -0
- /synth_ai/{lm → v0/lm}/vendors/supported/__init__.py +0 -0
- /synth_ai/{lm → v0/lm}/warmup.py +0 -0
- {synth_ai-0.2.9.dev7.dist-info → synth_ai-0.2.9.dev9.dist-info}/WHEEL +0 -0
- {synth_ai-0.2.9.dev7.dist-info → synth_ai-0.2.9.dev9.dist-info}/entry_points.txt +0 -0
- {synth_ai-0.2.9.dev7.dist-info → synth_ai-0.2.9.dev9.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
|
|
6
|
+
from synth_ai.api.models.supported import (
|
|
7
|
+
RL_SUPPORTED_MODELS,
|
|
8
|
+
SFT_SUPPORTED_MODELS,
|
|
9
|
+
training_modes_for_model,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True)
|
|
14
|
+
class AlgorithmSpec:
|
|
15
|
+
algo_type: str
|
|
16
|
+
method: str
|
|
17
|
+
variety: str
|
|
18
|
+
label: str # Human readable identifier (e.g. "RL / GSPO")
|
|
19
|
+
required_training_mode: str # Expected training mode on the model record (e.g. "rl", "sft")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _normalize(value: object) -> str:
|
|
23
|
+
if value is None:
|
|
24
|
+
return ""
|
|
25
|
+
return str(value).strip().lower()
|
|
26
|
+
|
|
27
|
+
RL_ALGORITHM_MODEL_IDS: tuple[str, ...] = tuple(sorted(RL_SUPPORTED_MODELS))
|
|
28
|
+
RL_ALGORITHM_MODEL_SET: frozenset[str] = frozenset(RL_ALGORITHM_MODEL_IDS)
|
|
29
|
+
|
|
30
|
+
SFT_ALGORITHM_MODEL_IDS: tuple[str, ...] = tuple(sorted(SFT_SUPPORTED_MODELS))
|
|
31
|
+
SFT_ALGORITHM_MODEL_SET: frozenset[str] = frozenset(SFT_ALGORITHM_MODEL_IDS)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
SUPPORTED_ALGORITHMS: tuple[AlgorithmSpec, ...] = (
|
|
35
|
+
AlgorithmSpec(
|
|
36
|
+
"online", "policy_gradient", "gspo", "online policy_gradient / gspo", "rl"
|
|
37
|
+
),
|
|
38
|
+
AlgorithmSpec(
|
|
39
|
+
"offline", "supervised_finetune", "fft", "offline supervised_finetune / fft", "sft"
|
|
40
|
+
),
|
|
41
|
+
AlgorithmSpec("offline", "sft", "fft", "offline sft / fft", "sft"),
|
|
42
|
+
# Accept explicit LoRA variety for SFT as a first-class alias of FFT SFT.
|
|
43
|
+
# This allows configs to declare intent with variety="lora" while still
|
|
44
|
+
# using SFT training mode; actual adapter selection is driven by [training].
|
|
45
|
+
AlgorithmSpec("offline", "sft", "lora", "offline sft / lora", "sft"),
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
_SUPPORTED_LOOKUP = {
|
|
49
|
+
(spec.algo_type, spec.method, spec.variety): spec for spec in SUPPORTED_ALGORITHMS
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class AlgorithmValidationError(ValueError):
|
|
54
|
+
"""Raised when an algorithm block contains unsupported combinations."""
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def validate_algorithm_config(
|
|
58
|
+
algorithm_block: Mapping[str, object] | None,
|
|
59
|
+
*,
|
|
60
|
+
expected_family: str | None = None,
|
|
61
|
+
) -> AlgorithmSpec:
|
|
62
|
+
"""Validate the [algorithm] section of a training config.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
algorithm_block: Parsed mapping from the TOML config.
|
|
66
|
+
expected_family: Optional expected family label ("rl" or "sft").
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
The matched AlgorithmSpec describing the supported combination.
|
|
70
|
+
|
|
71
|
+
Raises:
|
|
72
|
+
AlgorithmValidationError: if the combination is missing or unsupported.
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
if algorithm_block is None:
|
|
76
|
+
raise AlgorithmValidationError("Missing required [algorithm] section in config.")
|
|
77
|
+
if not isinstance(algorithm_block, Mapping):
|
|
78
|
+
raise AlgorithmValidationError("[algorithm] section must be a mapping/object.")
|
|
79
|
+
|
|
80
|
+
algo_type = _normalize(algorithm_block.get("type"))
|
|
81
|
+
method = _normalize(algorithm_block.get("method"))
|
|
82
|
+
variety = _normalize(algorithm_block.get("variety"))
|
|
83
|
+
|
|
84
|
+
key = (algo_type, method, variety)
|
|
85
|
+
spec = _SUPPORTED_LOOKUP.get(key)
|
|
86
|
+
if spec is None:
|
|
87
|
+
supported = "; ".join(
|
|
88
|
+
f"type='{entry.algo_type}', method='{entry.method}', variety='{entry.variety}'"
|
|
89
|
+
for entry in SUPPORTED_ALGORITHMS
|
|
90
|
+
)
|
|
91
|
+
raise AlgorithmValidationError(
|
|
92
|
+
"Unsupported algorithm configuration:\n"
|
|
93
|
+
f" type={algo_type!r}, method={method!r}, variety={variety!r}\n"
|
|
94
|
+
f"Supported combinations are: {supported}"
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
if expected_family:
|
|
98
|
+
expected_family = expected_family.lower()
|
|
99
|
+
family_map = {
|
|
100
|
+
("online", "policy_gradient", "gspo"): "rl",
|
|
101
|
+
("offline", "supervised_finetune", "fft"): "sft",
|
|
102
|
+
("offline", "sft", "fft"): "sft",
|
|
103
|
+
("offline", "sft", "lora"): "sft",
|
|
104
|
+
}
|
|
105
|
+
family = family_map.get(key)
|
|
106
|
+
if family != expected_family:
|
|
107
|
+
raise AlgorithmValidationError(
|
|
108
|
+
f"Config contains algorithm {spec.label!r}, "
|
|
109
|
+
f"but the current command expects {expected_family.upper()}."
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
return spec
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def ensure_model_supported_for_algorithm(model_id: str, spec: AlgorithmSpec) -> None:
|
|
116
|
+
"""Ensure that the given model supports the training mode required by *spec*."""
|
|
117
|
+
|
|
118
|
+
modes = {mode.lower() for mode in training_modes_for_model(model_id)}
|
|
119
|
+
required = spec.required_training_mode.lower()
|
|
120
|
+
if required not in modes:
|
|
121
|
+
if required == "rl":
|
|
122
|
+
allowed = ", ".join(RL_ALGORITHM_MODEL_IDS)
|
|
123
|
+
else:
|
|
124
|
+
allowed = ", ".join(SFT_ALGORITHM_MODEL_IDS)
|
|
125
|
+
raise AlgorithmValidationError(
|
|
126
|
+
f"Model '{model_id}' does not support {spec.label} workloads "
|
|
127
|
+
f"(missing training mode '{required}'). Supported model IDs: {allowed}"
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
__all__ = [
|
|
132
|
+
"AlgorithmSpec",
|
|
133
|
+
"AlgorithmValidationError",
|
|
134
|
+
"RL_ALGORITHM_MODEL_IDS",
|
|
135
|
+
"SFT_ALGORITHM_MODEL_IDS",
|
|
136
|
+
"SUPPORTED_ALGORITHMS",
|
|
137
|
+
"validate_algorithm_config",
|
|
138
|
+
"ensure_model_supported_for_algorithm",
|
|
139
|
+
]
|
synth_ai/api/train/task_app.py
CHANGED
synth_ai/api/train/utils.py
CHANGED
|
@@ -4,15 +4,16 @@ import json
|
|
|
4
4
|
import os
|
|
5
5
|
import re
|
|
6
6
|
import subprocess
|
|
7
|
-
import sys
|
|
8
7
|
import tempfile
|
|
9
8
|
import time
|
|
9
|
+
import tomllib
|
|
10
|
+
from collections.abc import Iterable, Mapping
|
|
10
11
|
from dataclasses import dataclass
|
|
11
12
|
from pathlib import Path
|
|
12
|
-
from typing import Any
|
|
13
|
+
from typing import Any
|
|
13
14
|
|
|
14
15
|
import requests
|
|
15
|
-
import
|
|
16
|
+
from synth_ai.learning.sft import collect_sft_jsonl_errors
|
|
16
17
|
|
|
17
18
|
REPO_ROOT = Path(__file__).resolve().parents[3]
|
|
18
19
|
|
|
@@ -139,49 +140,17 @@ def fmt_duration(seconds: float) -> str:
|
|
|
139
140
|
|
|
140
141
|
|
|
141
142
|
def validate_sft_jsonl(path: Path, *, max_errors: int = 20) -> None:
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
fh = path.open("r", encoding="utf-8")
|
|
145
|
-
except FileNotFoundError as exc: # pragma: no cover - upstream ensures existence
|
|
146
|
-
raise TrainError(f"Dataset not found: {path}") from exc
|
|
147
|
-
|
|
148
|
-
with fh:
|
|
149
|
-
for idx, line in enumerate(fh, start=1):
|
|
150
|
-
stripped = line.strip()
|
|
151
|
-
if not stripped:
|
|
152
|
-
continue
|
|
153
|
-
try:
|
|
154
|
-
record = json.loads(stripped)
|
|
155
|
-
except json.JSONDecodeError as exc:
|
|
156
|
-
errors.append(f"Line {idx}: invalid JSON ({exc.msg})")
|
|
157
|
-
if len(errors) >= max_errors:
|
|
158
|
-
break
|
|
159
|
-
continue
|
|
160
|
-
|
|
161
|
-
messages = record.get("messages")
|
|
162
|
-
if not isinstance(messages, list) or not messages:
|
|
163
|
-
errors.append(f"Line {idx}: missing or empty 'messages' list")
|
|
164
|
-
if len(errors) >= max_errors:
|
|
165
|
-
break
|
|
166
|
-
continue
|
|
143
|
+
if not path.exists():
|
|
144
|
+
raise TrainError(f"Dataset not found: {path}")
|
|
167
145
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
break
|
|
172
|
-
if "role" not in msg or "content" not in msg:
|
|
173
|
-
errors.append(f"Line {idx}: message {msg_idx} missing 'role' or 'content'")
|
|
174
|
-
break
|
|
175
|
-
if not isinstance(msg["role"], str) or not isinstance(msg["content"], str):
|
|
176
|
-
errors.append(f"Line {idx}: message {msg_idx} has non-string role/content")
|
|
177
|
-
break
|
|
178
|
-
if len(errors) >= max_errors:
|
|
179
|
-
break
|
|
146
|
+
issues = collect_sft_jsonl_errors(path, min_messages=1, max_errors=max_errors)
|
|
147
|
+
if not issues:
|
|
148
|
+
return
|
|
180
149
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
150
|
+
truncated = max_errors is not None and len(issues) >= max_errors
|
|
151
|
+
suffix = "" if not truncated else f" (showing first {max_errors} issues)"
|
|
152
|
+
details = "\n - ".join(issues)
|
|
153
|
+
raise TrainError(f"{path}: Dataset validation failed{suffix}:\n - {details}")
|
|
185
154
|
|
|
186
155
|
|
|
187
156
|
def limit_jsonl_examples(src: Path, limit: int) -> Path:
|
synth_ai/cli/__init__.py
CHANGED
|
@@ -17,6 +17,13 @@ except Exception:
|
|
|
17
17
|
# dotenv is optional at runtime; proceed if unavailable
|
|
18
18
|
pass
|
|
19
19
|
|
|
20
|
+
try:
|
|
21
|
+
from ._typer_patch import patch_typer_make_metavar
|
|
22
|
+
|
|
23
|
+
patch_typer_make_metavar()
|
|
24
|
+
except Exception:
|
|
25
|
+
pass
|
|
26
|
+
|
|
20
27
|
|
|
21
28
|
from .root import cli # new canonical CLI entrypoint
|
|
22
29
|
|
|
@@ -105,3 +112,4 @@ cli.add_command(task_app_group.commands["serve"], name="serve")
|
|
|
105
112
|
cli.add_command(task_app_group.commands["deploy"], name="deploy")
|
|
106
113
|
|
|
107
114
|
cli.add_command(task_app_group.commands["modal-serve"], name="modal-serve")
|
|
115
|
+
cli.add_command(task_app_group.commands["info"], name="info")
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def main() -> int:
|
|
7
|
+
# Apply Typer compatibility patch before Modal CLI bootstraps Click/Typer internals.
|
|
8
|
+
try:
|
|
9
|
+
from ._typer_patch import patch_typer_make_metavar
|
|
10
|
+
|
|
11
|
+
patch_typer_make_metavar()
|
|
12
|
+
except Exception:
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
from modal.__main__ import main as modal_main
|
|
16
|
+
|
|
17
|
+
# Present ourselves as the upstream `modal` CLI so Typer/Click parsing stays intact.
|
|
18
|
+
if sys.argv:
|
|
19
|
+
sys.argv[0] = "modal"
|
|
20
|
+
else:
|
|
21
|
+
sys.argv = ["modal"]
|
|
22
|
+
|
|
23
|
+
return modal_main()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
if __name__ == "__main__":
|
|
27
|
+
sys.exit(main())
|
|
28
|
+
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from click import Parameter
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def patch_typer_make_metavar() -> None:
|
|
7
|
+
try:
|
|
8
|
+
from typer.main import TyperArgument, TyperOption
|
|
9
|
+
except Exception:
|
|
10
|
+
return
|
|
11
|
+
|
|
12
|
+
def _call_get_metavar(param_type: object, param: Parameter) -> str | None:
|
|
13
|
+
getter = getattr(param_type, "get_metavar", None)
|
|
14
|
+
if getter is None:
|
|
15
|
+
return None
|
|
16
|
+
try:
|
|
17
|
+
return getter(param)
|
|
18
|
+
except TypeError:
|
|
19
|
+
try:
|
|
20
|
+
return getter(param, None)
|
|
21
|
+
except TypeError:
|
|
22
|
+
return None
|
|
23
|
+
|
|
24
|
+
def _patched_argument_make_metavar(self, ctx=None) -> str:
|
|
25
|
+
if self.metavar is not None:
|
|
26
|
+
return self.metavar
|
|
27
|
+
var = (self.name or "").upper()
|
|
28
|
+
if not self.required:
|
|
29
|
+
var = f"[{var}]"
|
|
30
|
+
type_var = _call_get_metavar(self.type, self)
|
|
31
|
+
if type_var:
|
|
32
|
+
var += f":{type_var}"
|
|
33
|
+
if self.nargs != 1:
|
|
34
|
+
var += "..."
|
|
35
|
+
return var
|
|
36
|
+
|
|
37
|
+
def _patched_option_make_metavar(self, ctx=None) -> str:
|
|
38
|
+
if self.metavar is not None:
|
|
39
|
+
return self.metavar
|
|
40
|
+
metavar = _call_get_metavar(self.type, self)
|
|
41
|
+
if not metavar:
|
|
42
|
+
name = getattr(self.type, "name", "") or ""
|
|
43
|
+
metavar = name.upper()
|
|
44
|
+
if self.nargs != 1:
|
|
45
|
+
metavar += "..."
|
|
46
|
+
return metavar
|
|
47
|
+
|
|
48
|
+
TyperArgument.make_metavar = _patched_argument_make_metavar # type: ignore[assignment]
|
|
49
|
+
TyperOption.make_metavar = _patched_option_make_metavar # type: ignore[assignment]
|
synth_ai/cli/balance.py
CHANGED
|
@@ -6,7 +6,6 @@ CLI: check remaining credit balance from Synth backend.
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
8
|
import os
|
|
9
|
-
from urllib.parse import urlparse
|
|
10
9
|
|
|
11
10
|
import click
|
|
12
11
|
import requests
|
|
@@ -15,7 +14,7 @@ from rich import box
|
|
|
15
14
|
from rich.console import Console
|
|
16
15
|
from rich.table import Table
|
|
17
16
|
|
|
18
|
-
from synth_ai.config.base_url import
|
|
17
|
+
from synth_ai.config.base_url import PROD_BASE_URL_DEFAULT, get_backend_from_env
|
|
19
18
|
|
|
20
19
|
PROD_BACKEND_BASE = f"{PROD_BASE_URL_DEFAULT.rstrip('/')}/api/v1"
|
|
21
20
|
|
synth_ai/cli/calc.py
CHANGED
|
@@ -30,7 +30,7 @@ def _safe_eval(expr: str) -> float:
|
|
|
30
30
|
def _eval(n):
|
|
31
31
|
if isinstance(n, ast.Expression):
|
|
32
32
|
return _eval(n.body)
|
|
33
|
-
if isinstance(n, ast.Num): # 3.8 and earlier
|
|
33
|
+
if hasattr(ast, "Num") and isinstance(n, ast.Num): # 3.8 and earlier
|
|
34
34
|
return n.n
|
|
35
35
|
if isinstance(n, ast.Constant): # 3.8+
|
|
36
36
|
if isinstance(n.value, int | float):
|
synth_ai/cli/demo.py
CHANGED
|
@@ -101,7 +101,8 @@ def register(cli):
|
|
|
101
101
|
# (prepare command removed; configure now prepares baseline TOML)
|
|
102
102
|
|
|
103
103
|
# Help pyright understand dynamic Click group attributes
|
|
104
|
-
from typing import Any
|
|
104
|
+
from typing import Any
|
|
105
|
+
from typing import cast as _cast
|
|
105
106
|
|
|
106
107
|
_dg = _cast(Any, demo)
|
|
107
108
|
|
synth_ai/cli/recent.py
CHANGED
|
@@ -34,11 +34,11 @@ def _fmt_time(v) -> str:
|
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
async def _fetch_recent(db_url: str, hours: float):
|
|
37
|
-
|
|
37
|
+
from synth_ai.tracing_v3.storage.factory import create_storage, StorageConfig
|
|
38
38
|
|
|
39
39
|
start_time = datetime.now() - timedelta(hours=hours)
|
|
40
40
|
|
|
41
|
-
db =
|
|
41
|
+
db = create_storage(StorageConfig(connection_string=db_url))
|
|
42
42
|
await db.initialize()
|
|
43
43
|
try:
|
|
44
44
|
query = """
|
synth_ai/cli/rl_demo.py
CHANGED
|
@@ -37,7 +37,8 @@ def register(cli):
|
|
|
37
37
|
"""RL Demo commands (separate from legacy demo)."""
|
|
38
38
|
|
|
39
39
|
# Help pyright understand dynamic Click group attributes
|
|
40
|
-
from typing import Any
|
|
40
|
+
from typing import Any
|
|
41
|
+
from typing import cast as _cast
|
|
41
42
|
|
|
42
43
|
_rlg = _cast(Any, rl_demo)
|
|
43
44
|
|
synth_ai/cli/root.py
CHANGED
|
@@ -5,6 +5,7 @@ Canonical CLI entrypoint for Synth AI (moved from synth_ai/cli.py).
|
|
|
5
5
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
|
+
import contextlib
|
|
8
9
|
import logging
|
|
9
10
|
import os
|
|
10
11
|
import shutil
|
|
@@ -18,7 +19,8 @@ import time
|
|
|
18
19
|
import click
|
|
19
20
|
|
|
20
21
|
try:
|
|
21
|
-
from importlib.metadata import PackageNotFoundError
|
|
22
|
+
from importlib.metadata import PackageNotFoundError
|
|
23
|
+
from importlib.metadata import version as _pkg_version
|
|
22
24
|
|
|
23
25
|
try:
|
|
24
26
|
__pkg_version__ = _pkg_version("synth-ai")
|
|
@@ -91,9 +93,8 @@ def install_sqld() -> str:
|
|
|
91
93
|
|
|
92
94
|
click.echo("📥 Downloading sqld via 'turso dev' (this may take a few seconds)…")
|
|
93
95
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
temp_db.close()
|
|
96
|
+
with tempfile.NamedTemporaryFile(prefix="synth_sqld_", suffix=".db", delete=False) as temp_db:
|
|
97
|
+
temp_db_path = temp_db.name
|
|
97
98
|
|
|
98
99
|
env = os.environ.copy()
|
|
99
100
|
env.setdefault("TURSO_NONINTERACTIVE", "1")
|
|
@@ -129,15 +130,12 @@ def install_sqld() -> str:
|
|
|
129
130
|
proc.kill()
|
|
130
131
|
stdout_data, stderr_data = proc.communicate()
|
|
131
132
|
finally:
|
|
132
|
-
if proc and proc.returncode not in (0, None):
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
try:
|
|
133
|
+
if proc and proc.returncode not in (0, None) and (stdout_data or stderr_data):
|
|
134
|
+
logging.getLogger(__name__).debug(
|
|
135
|
+
"turso dev stdout: %s\nstderr: %s", stdout_data, stderr_data
|
|
136
|
+
)
|
|
137
|
+
with contextlib.suppress(OSError):
|
|
138
138
|
os.unlink(temp_db_path)
|
|
139
|
-
except OSError:
|
|
140
|
-
pass
|
|
141
139
|
|
|
142
140
|
sqld_path = find_sqld_binary()
|
|
143
141
|
if sqld_path:
|
|
@@ -171,7 +169,7 @@ def _forward_to_demo(args: list[str]) -> None:
|
|
|
171
169
|
except Exception as e: # pragma: no cover
|
|
172
170
|
click.echo(f"Failed to import demo CLI: {e}")
|
|
173
171
|
sys.exit(1)
|
|
174
|
-
rc = int(
|
|
172
|
+
rc = int(demo_cli.main(args) or 0) # type: ignore[attr-defined]
|
|
175
173
|
if rc != 0:
|
|
176
174
|
sys.exit(rc)
|
|
177
175
|
|
synth_ai/cli/status.py
CHANGED
|
@@ -14,9 +14,9 @@ from rich.table import Table
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
async def _db_stats(db_url: str) -> dict:
|
|
17
|
-
from synth_ai.tracing_v3.
|
|
17
|
+
from synth_ai.tracing_v3.storage.factory import StorageConfig, create_storage
|
|
18
18
|
|
|
19
|
-
db =
|
|
19
|
+
db = create_storage(StorageConfig(connection_string=db_url))
|
|
20
20
|
await db.initialize()
|
|
21
21
|
try:
|
|
22
22
|
out: dict = {}
|