synth-ai 0.2.9.dev11__py3-none-any.whl → 0.4.1__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.
- synth_ai/__init__.py +44 -45
- synth_ai/__main__.py +30 -3
- synth_ai/cli/__init__.py +104 -78
- synth_ai/cli/__main__.py +42 -0
- synth_ai/cli/_internal/__init__.py +5 -0
- synth_ai/cli/_internal/modal_wrapper.py +31 -0
- synth_ai/cli/_internal/storage.py +20 -0
- synth_ai/cli/_internal/typer_patch.py +47 -0
- synth_ai/cli/_internal/validate_task_app.py +29 -0
- synth_ai/cli/agents/__init__.py +17 -0
- synth_ai/cli/agents/claude.py +77 -0
- synth_ai/cli/agents/codex.py +265 -0
- synth_ai/cli/agents/opencode.py +253 -0
- synth_ai/cli/commands/__init__.py +18 -0
- synth_ai/cli/commands/artifacts/__init__.py +13 -0
- synth_ai/cli/commands/artifacts/client.py +119 -0
- synth_ai/cli/commands/artifacts/config.py +57 -0
- synth_ai/cli/commands/artifacts/core.py +24 -0
- synth_ai/cli/commands/artifacts/download.py +188 -0
- synth_ai/cli/commands/artifacts/export.py +186 -0
- synth_ai/cli/commands/artifacts/list.py +156 -0
- synth_ai/cli/commands/artifacts/parsing.py +250 -0
- synth_ai/cli/commands/artifacts/show.py +336 -0
- synth_ai/cli/commands/baseline/__init__.py +12 -0
- synth_ai/cli/commands/baseline/core.py +636 -0
- synth_ai/cli/commands/baseline/list.py +94 -0
- synth_ai/cli/commands/demo/__init__.py +3 -0
- synth_ai/cli/commands/demo/core.py +153 -0
- synth_ai/cli/commands/eval/__init__.py +19 -0
- synth_ai/cli/commands/eval/core.py +1113 -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 +424 -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 +185 -0
- synth_ai/cli/commands/help/core.py +72 -0
- synth_ai/cli/commands/scan/__init__.py +19 -0
- synth_ai/cli/commands/scan/cloudflare_scanner.py +403 -0
- synth_ai/cli/commands/scan/core.py +344 -0
- synth_ai/cli/commands/scan/health_checker.py +242 -0
- synth_ai/cli/commands/scan/local_scanner.py +278 -0
- synth_ai/cli/commands/scan/models.py +83 -0
- synth_ai/cli/commands/smoke/__init__.py +7 -0
- synth_ai/cli/commands/smoke/core.py +1438 -0
- synth_ai/cli/commands/status/__init__.py +66 -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/pricing.py +23 -0
- synth_ai/cli/commands/status/subcommands/runs.py +81 -0
- synth_ai/cli/commands/status/subcommands/session.py +182 -0
- synth_ai/cli/commands/status/subcommands/summary.py +47 -0
- synth_ai/cli/commands/status/subcommands/usage.py +203 -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 +22 -0
- synth_ai/cli/commands/train/errors.py +117 -0
- synth_ai/cli/commands/train/judge_schemas.py +201 -0
- synth_ai/cli/commands/train/judge_validation.py +305 -0
- synth_ai/cli/commands/train/prompt_learning_validation.py +633 -0
- synth_ai/cli/commands/train/validation.py +392 -0
- synth_ai/cli/demo_apps/__init__.py +10 -0
- synth_ai/cli/demo_apps/core/__init__.py +28 -0
- synth_ai/cli/demo_apps/core/cli.py +1735 -0
- synth_ai/cli/demo_apps/crafter/crafter_fft_4b.toml +55 -0
- synth_ai/cli/demo_apps/crafter/grpo_crafter_task_app.py +186 -0
- synth_ai/cli/demo_apps/crafter/rl_from_base_qwen4b.toml +74 -0
- synth_ai/cli/demo_apps/demo_registry.py +176 -0
- synth_ai/cli/demo_apps/demo_task_apps/core.py +440 -0
- synth_ai/cli/demo_apps/demo_task_apps/crafter/__init__.py +1 -0
- synth_ai/cli/demo_apps/demo_task_apps/crafter/grpo_crafter_task_app.py +185 -0
- synth_ai/cli/demo_apps/demo_task_apps/math/modal_task_app.py +742 -0
- synth_ai/cli/demo_apps/demo_task_apps/math/task_app_entry.py +39 -0
- synth_ai/cli/demo_apps/math/__init__.py +1 -0
- synth_ai/cli/demo_apps/math/_common.py +16 -0
- synth_ai/cli/demo_apps/math/app.py +38 -0
- synth_ai/cli/demo_apps/math/config.toml +76 -0
- synth_ai/cli/demo_apps/math/deploy_modal.py +54 -0
- synth_ai/cli/demo_apps/math/modal_task_app.py +702 -0
- synth_ai/cli/demo_apps/math/task_app_entry.py +53 -0
- synth_ai/cli/demo_apps/mipro/main.py +271 -0
- synth_ai/cli/demo_apps/mipro/task_app.py +933 -0
- synth_ai/cli/demo_apps/mipro/train_cfg.toml +92 -0
- synth_ai/cli/demos/__init__.py +12 -0
- synth_ai/cli/demos/demo.py +32 -0
- synth_ai/cli/demos/rl_demo.py +254 -0
- synth_ai/cli/deploy.py +216 -0
- synth_ai/cli/infra/__init__.py +14 -0
- synth_ai/cli/infra/balance.py +216 -0
- synth_ai/cli/infra/mcp.py +35 -0
- synth_ai/cli/infra/modal_app.py +36 -0
- synth_ai/cli/infra/setup.py +69 -0
- synth_ai/cli/infra/status.py +16 -0
- synth_ai/cli/infra/turso.py +77 -0
- synth_ai/cli/lib/__init__.py +10 -0
- synth_ai/cli/lib/agents.py +76 -0
- synth_ai/cli/lib/apps/modal_app.py +101 -0
- synth_ai/cli/lib/apps/task_app.py +643 -0
- synth_ai/cli/lib/bin.py +39 -0
- synth_ai/cli/lib/env.py +375 -0
- synth_ai/cli/lib/errors.py +85 -0
- synth_ai/cli/lib/modal.py +315 -0
- synth_ai/cli/lib/plotting.py +126 -0
- synth_ai/cli/lib/prompt_args.py +39 -0
- synth_ai/cli/lib/prompts.py +284 -0
- synth_ai/cli/lib/sqld.py +122 -0
- synth_ai/cli/lib/task_app_discovery.py +884 -0
- synth_ai/cli/lib/task_app_env.py +295 -0
- synth_ai/cli/lib/train_cfgs.py +300 -0
- synth_ai/cli/lib/tunnel_records.py +207 -0
- synth_ai/cli/local/__init__.py +14 -0
- synth_ai/cli/local/experiment_queue/__init__.py +72 -0
- synth_ai/cli/local/experiment_queue/api_schemas.py +221 -0
- synth_ai/cli/local/experiment_queue/celery_app.py +208 -0
- synth_ai/cli/local/experiment_queue/config.py +128 -0
- synth_ai/cli/local/experiment_queue/config_utils.py +272 -0
- synth_ai/cli/local/experiment_queue/database.py +175 -0
- synth_ai/cli/local/experiment_queue/dispatcher.py +119 -0
- synth_ai/cli/local/experiment_queue/models.py +231 -0
- synth_ai/cli/local/experiment_queue/progress_info.py +160 -0
- synth_ai/cli/local/experiment_queue/results.py +373 -0
- synth_ai/cli/local/experiment_queue/schemas.py +131 -0
- synth_ai/cli/local/experiment_queue/service.py +344 -0
- synth_ai/cli/local/experiment_queue/status.py +372 -0
- synth_ai/cli/local/experiment_queue/status_tracker.py +360 -0
- synth_ai/cli/local/experiment_queue/tasks.py +1984 -0
- synth_ai/cli/local/experiment_queue/trace_storage.py +65 -0
- synth_ai/cli/local/experiment_queue/validation.py +157 -0
- synth_ai/cli/local/session/__init__.py +92 -0
- synth_ai/cli/local/session/client.py +383 -0
- synth_ai/cli/local/session/constants.py +63 -0
- synth_ai/cli/local/session/exceptions.py +105 -0
- synth_ai/cli/local/session/manager.py +139 -0
- synth_ai/cli/local/session/models.py +89 -0
- synth_ai/cli/local/session/query.py +110 -0
- synth_ai/cli/root.py +30 -103
- synth_ai/cli/task_apps/__init__.py +26 -0
- synth_ai/cli/task_apps/commands.py +3153 -0
- synth_ai/cli/task_apps/deploy.py +7 -0
- synth_ai/cli/task_apps/list.py +26 -0
- synth_ai/cli/task_apps/main.py +36 -0
- synth_ai/cli/task_apps/modal_serve.py +11 -0
- synth_ai/cli/task_apps/serve.py +11 -0
- synth_ai/cli/training/__init__.py +8 -0
- synth_ai/cli/training/train.py +5 -0
- synth_ai/cli/training/train_cfg.py +34 -0
- synth_ai/cli/training/watch.py +506 -0
- synth_ai/cli/turso.py +34 -55
- synth_ai/cli/usage.py +159 -0
- synth_ai/cli/utils/__init__.py +8 -0
- synth_ai/cli/utils/experiments.py +235 -0
- synth_ai/cli/utils/queue.py +504 -0
- synth_ai/cli/utils/recent.py +133 -0
- synth_ai/cli/utils/traces.py +164 -0
- synth_ai/contracts/__init__.py +67 -0
- synth_ai/core/__init__.py +100 -0
- synth_ai/core/_utils/__init__.py +54 -0
- synth_ai/core/_utils/base_url.py +10 -0
- synth_ai/core/_utils/http.py +10 -0
- synth_ai/core/_utils/prompts.py +14 -0
- synth_ai/core/_utils/task_app_state.py +12 -0
- synth_ai/core/_utils/user_config.py +10 -0
- synth_ai/core/apps/common.py +116 -0
- synth_ai/core/auth.py +95 -0
- synth_ai/core/cfgs.py +240 -0
- synth_ai/core/config/__init__.py +16 -0
- synth_ai/core/config/base.py +168 -0
- synth_ai/core/config/resolver.py +89 -0
- synth_ai/core/env.py +220 -0
- synth_ai/core/errors.py +126 -0
- synth_ai/core/http.py +230 -0
- synth_ai/core/integrations/__init__.py +11 -0
- synth_ai/core/integrations/cloudflare.py +1710 -0
- synth_ai/core/integrations/mcp/__init__.py +6 -0
- synth_ai/core/integrations/mcp/__main__.py +8 -0
- synth_ai/core/integrations/mcp/claude.py +36 -0
- synth_ai/core/integrations/mcp/main.py +254 -0
- synth_ai/core/integrations/mcp/setup.py +100 -0
- synth_ai/core/integrations/modal.py +277 -0
- synth_ai/core/json.py +72 -0
- synth_ai/core/log_filter.py +99 -0
- synth_ai/core/logging.py +82 -0
- synth_ai/core/paths.py +107 -0
- synth_ai/core/pricing.py +109 -0
- synth_ai/core/process.py +233 -0
- synth_ai/core/ssl.py +25 -0
- synth_ai/core/storage/__init__.py +71 -0
- synth_ai/core/task_app_state.py +318 -0
- synth_ai/core/telemetry.py +282 -0
- synth_ai/core/tracing_v3/__init__.py +99 -0
- synth_ai/core/tracing_v3/config.py +229 -0
- synth_ai/core/tracing_v3/constants.py +21 -0
- synth_ai/core/tracing_v3/db_config.py +182 -0
- synth_ai/core/tracing_v3/decorators.py +401 -0
- synth_ai/core/tracing_v3/examples/basic_usage.py +194 -0
- synth_ai/core/tracing_v3/llm_call_record_helpers.py +437 -0
- synth_ai/core/tracing_v3/migration_helper.py +119 -0
- synth_ai/core/tracing_v3/replica_sync.py +262 -0
- synth_ai/core/tracing_v3/serialization.py +130 -0
- synth_ai/core/tracing_v3/session_tracer.py +542 -0
- synth_ai/core/tracing_v3/storage/base.py +211 -0
- synth_ai/core/tracing_v3/storage/config.py +109 -0
- synth_ai/core/tracing_v3/storage/factory.py +39 -0
- synth_ai/core/tracing_v3/storage/utils.py +206 -0
- synth_ai/core/tracing_v3/trace_utils.py +326 -0
- synth_ai/core/tracing_v3/turso/__init__.py +12 -0
- synth_ai/core/tracing_v3/turso/daemon.py +278 -0
- synth_ai/core/tracing_v3/turso/models.py +470 -0
- synth_ai/core/tracing_v3/turso/native_manager.py +1385 -0
- synth_ai/core/tracing_v3/utils.py +108 -0
- synth_ai/core/urls.py +18 -0
- synth_ai/core/user_config.py +137 -0
- synth_ai/core/uvicorn.py +222 -0
- synth_ai/data/__init__.py +110 -0
- synth_ai/data/enums.py +141 -0
- synth_ai/data/rewards.py +152 -0
- synth_ai/data/specs.py +36 -0
- synth_ai/data/traces.py +35 -0
- synth_ai/products/__init__.py +6 -0
- synth_ai/products/graph_evolve/__init__.py +46 -0
- synth_ai/products/graph_evolve/client.py +226 -0
- synth_ai/products/graph_evolve/config.py +591 -0
- synth_ai/products/graph_evolve/converters/__init__.py +42 -0
- synth_ai/products/graph_evolve/converters/openai_sft.py +484 -0
- synth_ai/products/graph_evolve/examples/hotpotqa/config.toml +109 -0
- synth_ai/products/graph_evolve/run.py +222 -0
- synth_ai/sdk/__init__.py +119 -0
- synth_ai/sdk/api/__init__.py +1 -0
- synth_ai/sdk/api/models/supported.py +514 -0
- synth_ai/sdk/api/research_agent/__init__.py +86 -0
- synth_ai/sdk/api/research_agent/cli.py +428 -0
- synth_ai/sdk/api/research_agent/config.py +357 -0
- synth_ai/sdk/api/research_agent/job.py +717 -0
- synth_ai/sdk/api/train/__init__.py +85 -0
- synth_ai/sdk/api/train/builders.py +895 -0
- synth_ai/sdk/api/train/cli.py +2188 -0
- synth_ai/sdk/api/train/config_finder.py +267 -0
- synth_ai/sdk/api/train/configs/__init__.py +65 -0
- synth_ai/sdk/api/train/configs/prompt_learning.py +1706 -0
- synth_ai/sdk/api/train/configs/rl.py +188 -0
- synth_ai/sdk/api/train/configs/sft.py +99 -0
- synth_ai/sdk/api/train/configs/shared.py +81 -0
- synth_ai/sdk/api/train/context_learning.py +312 -0
- synth_ai/sdk/api/train/env_resolver.py +418 -0
- synth_ai/sdk/api/train/graph_validators.py +216 -0
- synth_ai/sdk/api/train/graphgen.py +984 -0
- synth_ai/sdk/api/train/graphgen_models.py +823 -0
- synth_ai/sdk/api/train/graphgen_validators.py +109 -0
- synth_ai/sdk/api/train/pollers.py +124 -0
- synth_ai/sdk/api/train/progress/__init__.py +97 -0
- synth_ai/sdk/api/train/progress/dataclasses.py +569 -0
- synth_ai/sdk/api/train/progress/events.py +326 -0
- synth_ai/sdk/api/train/progress/results.py +428 -0
- synth_ai/sdk/api/train/progress/tracker.py +641 -0
- synth_ai/sdk/api/train/prompt_learning.py +470 -0
- synth_ai/sdk/api/train/rl.py +442 -0
- synth_ai/sdk/api/train/sft.py +396 -0
- synth_ai/sdk/api/train/summary.py +522 -0
- synth_ai/sdk/api/train/supported_algos.py +147 -0
- synth_ai/sdk/api/train/task_app.py +331 -0
- synth_ai/sdk/api/train/utils.py +279 -0
- synth_ai/sdk/api/train/validators.py +2424 -0
- synth_ai/sdk/baseline/__init__.py +25 -0
- synth_ai/sdk/baseline/config.py +209 -0
- synth_ai/sdk/baseline/discovery.py +216 -0
- synth_ai/sdk/baseline/execution.py +154 -0
- synth_ai/sdk/graphs/__init__.py +15 -0
- synth_ai/sdk/graphs/completions.py +570 -0
- synth_ai/sdk/inference/__init__.py +6 -0
- synth_ai/sdk/inference/client.py +128 -0
- synth_ai/sdk/jobs/__init__.py +16 -0
- synth_ai/sdk/jobs/client.py +371 -0
- synth_ai/sdk/judging/__init__.py +15 -0
- synth_ai/sdk/judging/base.py +24 -0
- synth_ai/sdk/judging/client.py +191 -0
- synth_ai/sdk/judging/schemas.py +222 -0
- synth_ai/sdk/judging/types.py +42 -0
- synth_ai/sdk/learning/__init__.py +69 -0
- synth_ai/sdk/learning/client.py +240 -0
- synth_ai/sdk/learning/ft_client.py +7 -0
- synth_ai/sdk/learning/health.py +49 -0
- synth_ai/sdk/learning/jobs.py +202 -0
- synth_ai/sdk/learning/prompt_extraction.py +334 -0
- synth_ai/sdk/learning/prompt_learning_client.py +455 -0
- synth_ai/sdk/learning/prompt_learning_types.py +185 -0
- synth_ai/sdk/learning/rl/client.py +268 -0
- synth_ai/sdk/learning/rl/contracts.py +27 -0
- synth_ai/sdk/learning/rl/env_keys.py +166 -0
- synth_ai/sdk/learning/rl/secrets.py +13 -0
- synth_ai/sdk/learning/sft/client.py +95 -0
- synth_ai/sdk/learning/sft/config.py +270 -0
- synth_ai/sdk/learning/sft/data.py +698 -0
- synth_ai/sdk/learning/validators.py +52 -0
- synth_ai/sdk/research_agent/__init__.py +34 -0
- synth_ai/sdk/research_agent/container_builder.py +328 -0
- synth_ai/sdk/research_agent/container_spec.py +198 -0
- synth_ai/sdk/research_agent/defaults.py +34 -0
- synth_ai/sdk/research_agent/results_collector.py +69 -0
- synth_ai/sdk/specs/__init__.py +46 -0
- synth_ai/sdk/specs/dataclasses.py +149 -0
- synth_ai/sdk/specs/loader.py +144 -0
- synth_ai/sdk/specs/serializer.py +199 -0
- synth_ai/sdk/specs/validation.py +250 -0
- synth_ai/sdk/streaming/__init__.py +35 -0
- synth_ai/sdk/streaming/config.py +94 -0
- synth_ai/sdk/streaming/handlers.py +1997 -0
- synth_ai/sdk/streaming/streamer.py +704 -0
- synth_ai/sdk/streaming/types.py +112 -0
- synth_ai/sdk/task/__init__.py +151 -0
- synth_ai/sdk/task/apps/__init__.py +133 -0
- synth_ai/sdk/task/config.py +261 -0
- synth_ai/sdk/task/contracts.py +298 -0
- synth_ai/sdk/task/datasets.py +108 -0
- synth_ai/sdk/task/in_process.py +1190 -0
- synth_ai/sdk/task/in_process_runner.py +309 -0
- synth_ai/sdk/task/inference_api.py +299 -0
- synth_ai/sdk/task/proxy.py +287 -0
- synth_ai/sdk/task/rubrics/__init__.py +55 -0
- synth_ai/sdk/task/rubrics/loaders.py +156 -0
- synth_ai/sdk/task/rubrics/models.py +57 -0
- synth_ai/sdk/task/rubrics/scoring.py +116 -0
- synth_ai/sdk/task/rubrics/strict.py +149 -0
- synth_ai/sdk/task/server.py +580 -0
- synth_ai/sdk/task/trace_correlation_helpers.py +506 -0
- synth_ai/sdk/task/tracing_utils.py +95 -0
- synth_ai/sdk/task/validators.py +456 -0
- synth_ai/sdk/tracing/__init__.py +39 -0
- synth_ai/sdk/training/__init__.py +102 -0
- synth_ai/sdk/usage/__init__.py +37 -0
- synth_ai/sdk/usage/client.py +171 -0
- synth_ai/sdk/usage/models.py +261 -0
- synth_ai/utils/__init__.py +213 -0
- synth_ai-0.4.1.dist-info/METADATA +195 -0
- synth_ai-0.4.1.dist-info/RECORD +379 -0
- synth_ai-0.4.1.dist-info/entry_points.txt +2 -0
- synth_ai-0.4.1.dist-info/top_level.txt +1 -0
- examples/__init__.py +0 -16
- examples/analyze_semantic_words.sh +0 -17
- examples/crafter_debug_render.py +0 -186
- examples/qwen_coder/README.md +0 -102
- examples/qwen_coder/_shared.py +0 -113
- examples/qwen_coder/configs/coder_lora_30b.toml +0 -61
- examples/qwen_coder/configs/coder_lora_4b.toml +0 -57
- examples/qwen_coder/configs/coder_lora_small.toml +0 -58
- examples/qwen_coder/generate_dataset.py +0 -98
- examples/qwen_coder/infer_ft_smoke.py +0 -64
- examples/qwen_coder/infer_prod_proxy.py +0 -73
- examples/qwen_coder/infer_via_synth.py +0 -87
- examples/qwen_coder/scripts/infer_coder.sh +0 -18
- examples/qwen_coder/scripts/train_coder_30b.sh +0 -21
- examples/qwen_coder/sft_full_17b.py +0 -103
- examples/qwen_coder/sft_lora_30b.py +0 -110
- examples/qwen_coder/subset_jsonl.py +0 -38
- examples/qwen_coder/validate_jsonl.py +0 -59
- examples/rl/README.md +0 -169
- examples/rl/configs/eval_base_qwen.toml +0 -15
- examples/rl/configs/eval_rl_qwen.toml +0 -11
- examples/rl/configs/rl_from_base_qwen.toml +0 -35
- examples/rl/configs/rl_from_base_qwen17.toml +0 -74
- examples/rl/configs/rl_from_ft_qwen.toml +0 -35
- examples/rl/download_dataset.py +0 -80
- examples/rl/run_eval.py +0 -436
- examples/rl/run_rl_and_save.py +0 -111
- examples/rl/task_app/README.md +0 -22
- examples/rl/task_app/math_single_step.py +0 -991
- examples/rl/task_app/math_task_app.py +0 -115
- examples/run_crafter_demo.sh +0 -10
- examples/sft/README.md +0 -139
- examples/sft/configs/crafter_fft_qwen0p6b.toml +0 -44
- examples/sft/configs/crafter_lora_qwen0p6b.toml +0 -45
- examples/sft/evaluate.py +0 -117
- examples/sft/export_dataset.py +0 -117
- examples/sft/generate_traces.py +0 -162
- examples/swe/__init__.py +0 -12
- examples/swe/task_app/README.md +0 -105
- examples/swe/task_app/__init__.py +0 -2
- examples/swe/task_app/grpo_swe_mini.py +0 -571
- examples/swe/task_app/grpo_swe_mini_task_app.py +0 -136
- examples/swe/task_app/hosted/README.md +0 -173
- examples/swe/task_app/hosted/__init__.py +0 -5
- examples/swe/task_app/hosted/branching.py +0 -143
- examples/swe/task_app/hosted/environment_routes.py +0 -1289
- examples/swe/task_app/hosted/envs/__init__.py +0 -1
- examples/swe/task_app/hosted/envs/crafter/__init__.py +0 -6
- examples/swe/task_app/hosted/envs/crafter/app.py +0 -1
- examples/swe/task_app/hosted/envs/crafter/environment.py +0 -522
- examples/swe/task_app/hosted/envs/crafter/policy.py +0 -478
- examples/swe/task_app/hosted/envs/crafter/react_agent.py +0 -108
- examples/swe/task_app/hosted/envs/crafter/shared.py +0 -305
- examples/swe/task_app/hosted/envs/crafter/tools.py +0 -47
- examples/swe/task_app/hosted/envs/mini_swe/__init__.py +0 -8
- examples/swe/task_app/hosted/envs/mini_swe/environment.py +0 -1164
- examples/swe/task_app/hosted/envs/mini_swe/policy.py +0 -355
- examples/swe/task_app/hosted/envs/mini_swe/shared.py +0 -83
- examples/swe/task_app/hosted/envs/mini_swe/tools.py +0 -96
- examples/swe/task_app/hosted/hosted_app.py +0 -204
- examples/swe/task_app/hosted/inference/__init__.py +0 -5
- examples/swe/task_app/hosted/inference/openai_client.py +0 -618
- examples/swe/task_app/hosted/main.py +0 -100
- examples/swe/task_app/hosted/policy_routes.py +0 -1079
- examples/swe/task_app/hosted/registry.py +0 -195
- examples/swe/task_app/hosted/rollout.py +0 -1869
- examples/swe/task_app/hosted/storage/__init__.py +0 -5
- examples/swe/task_app/hosted/storage/volume.py +0 -211
- examples/swe/task_app/hosted/test_agents.py +0 -161
- examples/swe/task_app/hosted/test_service.py +0 -137
- examples/swe/task_app/hosted/utils.py +0 -62
- examples/vlm/README.md +0 -68
- examples/vlm/configs/crafter_vlm_gpt4o.toml +0 -44
- examples/vlm/crafter_image_only_agent.py +0 -207
- examples/vlm/crafter_openai_vlm_agent.py +0 -277
- examples/vlm/filter_image_rows.py +0 -63
- examples/vlm/run_crafter_vlm_benchmark.py +0 -316
- examples/warming_up_to_rl/analyze_trace_db.py +0 -422
- examples/warming_up_to_rl/configs/crafter_fft.toml +0 -48
- examples/warming_up_to_rl/configs/crafter_fft_4b.toml +0 -54
- examples/warming_up_to_rl/configs/eval_fft_qwen4b.toml +0 -20
- examples/warming_up_to_rl/configs/eval_groq_qwen32b.toml +0 -13
- examples/warming_up_to_rl/configs/eval_modal_qwen4b.toml +0 -23
- examples/warming_up_to_rl/configs/rl_from_base_qwen4b.toml +0 -83
- examples/warming_up_to_rl/configs/rl_from_ft.toml +0 -56
- examples/warming_up_to_rl/export_trace_sft.py +0 -723
- examples/warming_up_to_rl/groq_test.py +0 -95
- examples/warming_up_to_rl/manage_secrets.py +0 -131
- examples/warming_up_to_rl/readme.md +0 -179
- examples/warming_up_to_rl/run_eval.py +0 -510
- examples/warming_up_to_rl/run_fft_and_save.py +0 -380
- examples/warming_up_to_rl/run_local_rollout.py +0 -237
- examples/warming_up_to_rl/run_local_rollout_modal.py +0 -246
- examples/warming_up_to_rl/run_local_rollout_parallel.py +0 -403
- examples/warming_up_to_rl/run_local_rollout_traced.py +0 -475
- examples/warming_up_to_rl/run_rl_and_save.py +0 -124
- examples/warming_up_to_rl/run_rollout_remote.py +0 -154
- examples/warming_up_to_rl/task_app/README.md +0 -42
- examples/warming_up_to_rl/task_app/grpo_crafter.py +0 -700
- examples/warming_up_to_rl/task_app/grpo_crafter_task_app.py +0 -146
- examples/warming_up_to_rl/task_app/synth_envs_hosted/README.md +0 -173
- examples/warming_up_to_rl/task_app/synth_envs_hosted/__init__.py +0 -5
- examples/warming_up_to_rl/task_app/synth_envs_hosted/branching.py +0 -143
- examples/warming_up_to_rl/task_app/synth_envs_hosted/environment_routes.py +0 -1226
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/__init__.py +0 -1
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/__init__.py +0 -6
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/app.py +0 -1
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/environment.py +0 -522
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/policy.py +0 -478
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/react_agent.py +0 -108
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/shared.py +0 -305
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/tools.py +0 -47
- examples/warming_up_to_rl/task_app/synth_envs_hosted/hosted_app.py +0 -204
- examples/warming_up_to_rl/task_app/synth_envs_hosted/inference/__init__.py +0 -5
- examples/warming_up_to_rl/task_app/synth_envs_hosted/inference/openai_client.py +0 -618
- examples/warming_up_to_rl/task_app/synth_envs_hosted/main.py +0 -100
- examples/warming_up_to_rl/task_app/synth_envs_hosted/policy_routes.py +0 -1083
- examples/warming_up_to_rl/task_app/synth_envs_hosted/registry.py +0 -195
- examples/warming_up_to_rl/task_app/synth_envs_hosted/rollout.py +0 -1869
- examples/warming_up_to_rl/task_app/synth_envs_hosted/storage/__init__.py +0 -5
- examples/warming_up_to_rl/task_app/synth_envs_hosted/storage/volume.py +0 -211
- examples/warming_up_to_rl/task_app/synth_envs_hosted/test_agents.py +0 -161
- examples/warming_up_to_rl/task_app/synth_envs_hosted/test_service.py +0 -137
- examples/warming_up_to_rl/task_app/synth_envs_hosted/utils.py +0 -62
- synth/__init__.py +0 -14
- synth_ai/api/models/supported.py +0 -376
- synth_ai/api/train/__init__.py +0 -5
- synth_ai/api/train/builders.py +0 -296
- synth_ai/api/train/cli.py +0 -606
- synth_ai/api/train/config_finder.py +0 -228
- synth_ai/api/train/env_resolver.py +0 -347
- synth_ai/api/train/pollers.py +0 -75
- synth_ai/api/train/supported_algos.py +0 -139
- synth_ai/api/train/task_app.py +0 -195
- synth_ai/api/train/utils.py +0 -217
- synth_ai/cli/_modal_wrapper.py +0 -28
- synth_ai/cli/_typer_patch.py +0 -49
- synth_ai/cli/balance.py +0 -203
- synth_ai/cli/calc.py +0 -69
- synth_ai/cli/demo.py +0 -159
- synth_ai/cli/legacy_root_backup.py +0 -470
- synth_ai/cli/man.py +0 -106
- synth_ai/cli/recent.py +0 -127
- synth_ai/cli/rl_demo.py +0 -274
- synth_ai/cli/status.py +0 -133
- synth_ai/cli/task_apps.py +0 -2782
- synth_ai/cli/traces.py +0 -163
- synth_ai/cli/watch.py +0 -505
- synth_ai/config/base_url.py +0 -107
- synth_ai/core/experiment.py +0 -13
- synth_ai/core/system.py +0 -15
- synth_ai/demo_registry.py +0 -295
- synth_ai/demos/core/__init__.py +0 -1
- synth_ai/demos/core/cli.py +0 -1756
- synth_ai/demos/demo_task_apps/core.py +0 -440
- synth_ai/demos/demo_task_apps/crafter/grpo_crafter_task_app.py +0 -172
- synth_ai/demos/demo_task_apps/math/deploy_task_app.sh +0 -22
- synth_ai/demos/demo_task_apps/math/modal_task_app.py +0 -739
- synth_ai/demos/demo_task_apps/math/task_app_entry.py +0 -37
- synth_ai/environments/__init__.py +0 -31
- synth_ai/environments/environment/__init__.py +0 -1
- synth_ai/environments/environment/artifacts/__init__.py +0 -1
- synth_ai/environments/environment/artifacts/base.py +0 -52
- synth_ai/environments/environment/core.py +0 -67
- synth_ai/environments/environment/db/__init__.py +0 -1
- synth_ai/environments/environment/db/sqlite.py +0 -45
- synth_ai/environments/environment/registry.py +0 -233
- synth_ai/environments/environment/resources/sqlite.py +0 -45
- synth_ai/environments/environment/results.py +0 -1
- synth_ai/environments/environment/rewards/__init__.py +0 -1
- synth_ai/environments/environment/rewards/core.py +0 -29
- synth_ai/environments/environment/shared_engine.py +0 -26
- synth_ai/environments/environment/tools/__init__.py +0 -200
- synth_ai/environments/examples/__init__.py +0 -1
- synth_ai/environments/examples/bandit/__init__.py +0 -33
- synth_ai/environments/examples/bandit/engine.py +0 -302
- synth_ai/environments/examples/bandit/environment.py +0 -194
- synth_ai/environments/examples/bandit/taskset.py +0 -200
- synth_ai/environments/examples/crafter_classic/__init__.py +0 -8
- synth_ai/environments/examples/crafter_classic/agent_demos/analyze_semantic_words_markdown.py +0 -250
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_comprehensive_evaluation.py +0 -59
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_evaluation_browser.py +0 -152
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_evaluation_config.toml +0 -24
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_evaluation_framework.py +0 -1194
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/crafter_synth_config.toml +0 -56
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/filter_config_modal.toml +0 -32
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/kick_off_ft_modal.py +0 -384
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_action_results.py +0 -53
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_agent_actions.py +0 -178
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_latest_run.py +0 -222
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_lm_traces.py +0 -183
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_no_rewards.py +0 -210
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/analyze_trace_issue.py +0 -206
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/check_db_schema.py +0 -49
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/check_latest_results.py +0 -64
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/debug_agent_responses.py +0 -88
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/old/quick_trace_check.py +0 -77
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/compare_experiments.py +0 -324
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/kick_off_ft_oai.py +0 -362
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/multi_model_config.toml +0 -49
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/analyze_enhanced_hooks.py +0 -332
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/analyze_hook_events.py +0 -97
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/analyze_hook_results.py +0 -217
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/check_hook_storage.py +0 -87
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/check_seeds.py +0 -88
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/compare_seed_performance.py +0 -195
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/custom_eval_pipelines.py +0 -400
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/plot_hook_frequency.py +0 -195
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/old/seed_analysis_summary.py +0 -56
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/run_rollouts_for_models_and_compare_v3.py +0 -858
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_quick_evaluation.py +0 -52
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_react_agent.py +0 -874
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_trace_evaluation.py +0 -1412
- synth_ai/environments/examples/crafter_classic/agent_demos/example_v3_usage.py +0 -216
- synth_ai/environments/examples/crafter_classic/agent_demos/old/compare_traces.py +0 -296
- synth_ai/environments/examples/crafter_classic/agent_demos/old/crafter_comprehensive_evaluation.py +0 -58
- synth_ai/environments/examples/crafter_classic/agent_demos/old/crafter_env_serialization.py +0 -464
- synth_ai/environments/examples/crafter_classic/agent_demos/old/crafter_evaluation_browser.py +0 -152
- synth_ai/environments/examples/crafter_classic/agent_demos/old/crafter_quick_evaluation.py +0 -51
- synth_ai/environments/examples/crafter_classic/agent_demos/old/crafter_trace_evaluation.py +0 -1412
- synth_ai/environments/examples/crafter_classic/agent_demos/old/debug_player_loss.py +0 -112
- synth_ai/environments/examples/crafter_classic/agent_demos/old/diagnose_service.py +0 -203
- synth_ai/environments/examples/crafter_classic/agent_demos/old/diagnose_slowness.py +0 -305
- synth_ai/environments/examples/crafter_classic/agent_demos/old/eval_by_difficulty.py +0 -126
- synth_ai/environments/examples/crafter_classic/agent_demos/old/eval_example.py +0 -94
- synth_ai/environments/examples/crafter_classic/agent_demos/old/explore_saved_states.py +0 -142
- synth_ai/environments/examples/crafter_classic/agent_demos/old/filter_traces_sft.py +0 -26
- synth_ai/environments/examples/crafter_classic/agent_demos/old/filter_traces_sft_OLD.py +0 -984
- synth_ai/environments/examples/crafter_classic/agent_demos/old/generate_ft_data_gemini.py +0 -724
- synth_ai/environments/examples/crafter_classic/agent_demos/old/generate_ft_data_modal.py +0 -386
- synth_ai/environments/examples/crafter_classic/agent_demos/old/generate_ft_metadata.py +0 -205
- synth_ai/environments/examples/crafter_classic/agent_demos/old/kick_off_ft_gemini.py +0 -150
- synth_ai/environments/examples/crafter_classic/agent_demos/old/kick_off_ft_modal.py +0 -283
- synth_ai/environments/examples/crafter_classic/agent_demos/old/prepare_vertex_ft.py +0 -280
- synth_ai/environments/examples/crafter_classic/agent_demos/old/profile_env_slowness.py +0 -456
- synth_ai/environments/examples/crafter_classic/agent_demos/old/replicate_issue.py +0 -166
- synth_ai/environments/examples/crafter_classic/agent_demos/old/run_and_eval.py +0 -102
- synth_ai/environments/examples/crafter_classic/agent_demos/old/run_comparison.py +0 -128
- synth_ai/environments/examples/crafter_classic/agent_demos/old/run_qwen_rollouts.py +0 -655
- synth_ai/environments/examples/crafter_classic/agent_demos/old/trace_eval_OLD.py +0 -202
- synth_ai/environments/examples/crafter_classic/agent_demos/old/validate_openai_format.py +0 -166
- synth_ai/environments/examples/crafter_classic/config_logging.py +0 -111
- synth_ai/environments/examples/crafter_classic/debug_translation.py +0 -0
- synth_ai/environments/examples/crafter_classic/engine.py +0 -579
- synth_ai/environments/examples/crafter_classic/engine_deterministic_patch.py +0 -64
- synth_ai/environments/examples/crafter_classic/engine_helpers/action_map.py +0 -6
- synth_ai/environments/examples/crafter_classic/engine_helpers/serialization.py +0 -75
- synth_ai/environments/examples/crafter_classic/engine_serialization_patch_v3.py +0 -267
- synth_ai/environments/examples/crafter_classic/environment.py +0 -479
- synth_ai/environments/examples/crafter_classic/taskset.py +0 -233
- synth_ai/environments/examples/crafter_classic/trace_hooks_v3.py +0 -228
- synth_ai/environments/examples/crafter_classic/world_config_patch_simple.py +0 -299
- synth_ai/environments/examples/crafter_custom/__init__.py +0 -4
- synth_ai/environments/examples/crafter_custom/agent_demos/__init__.py +0 -1
- synth_ai/environments/examples/crafter_custom/agent_demos/trace_eval.py +0 -202
- synth_ai/environments/examples/crafter_custom/crafter/__init__.py +0 -7
- synth_ai/environments/examples/crafter_custom/crafter/config.py +0 -182
- synth_ai/environments/examples/crafter_custom/crafter/constants.py +0 -8
- synth_ai/environments/examples/crafter_custom/crafter/engine.py +0 -269
- synth_ai/environments/examples/crafter_custom/crafter/env.py +0 -262
- synth_ai/environments/examples/crafter_custom/crafter/objects.py +0 -417
- synth_ai/environments/examples/crafter_custom/crafter/recorder.py +0 -187
- synth_ai/environments/examples/crafter_custom/crafter/worldgen.py +0 -118
- synth_ai/environments/examples/crafter_custom/dataset_builder.py +0 -373
- synth_ai/environments/examples/crafter_custom/environment.py +0 -312
- synth_ai/environments/examples/crafter_custom/old/analyze_diamond_issue.py +0 -159
- synth_ai/environments/examples/crafter_custom/old/analyze_diamond_spawning.py +0 -158
- synth_ai/environments/examples/crafter_custom/old/compare_worlds.py +0 -71
- synth_ai/environments/examples/crafter_custom/old/dataset_stats.py +0 -105
- synth_ai/environments/examples/crafter_custom/old/diamond_spawning_summary.py +0 -119
- synth_ai/environments/examples/crafter_custom/old/example_dataset_usage.py +0 -52
- synth_ai/environments/examples/crafter_custom/run_dataset.py +0 -305
- synth_ai/environments/examples/enron/art_helpers/email_search_tools.py +0 -156
- synth_ai/environments/examples/enron/art_helpers/local_email_db.py +0 -281
- synth_ai/environments/examples/enron/art_helpers/types_enron.py +0 -25
- synth_ai/environments/examples/enron/engine.py +0 -295
- synth_ai/environments/examples/enron/environment.py +0 -166
- synth_ai/environments/examples/enron/taskset.py +0 -112
- synth_ai/environments/examples/enron/units/keyword_stats.py +0 -112
- synth_ai/environments/examples/minigrid/__init__.py +0 -48
- synth_ai/environments/examples/minigrid/agent_demos/minigrid_evaluation_framework.py +0 -1188
- synth_ai/environments/examples/minigrid/agent_demos/minigrid_quick_evaluation.py +0 -48
- synth_ai/environments/examples/minigrid/agent_demos/minigrid_react_agent.py +0 -562
- synth_ai/environments/examples/minigrid/agent_demos/minigrid_trace_evaluation.py +0 -221
- synth_ai/environments/examples/minigrid/engine.py +0 -589
- synth_ai/environments/examples/minigrid/environment.py +0 -274
- synth_ai/environments/examples/minigrid/environment_mapping.py +0 -242
- synth_ai/environments/examples/minigrid/puzzle_loader.py +0 -417
- synth_ai/environments/examples/minigrid/taskset.py +0 -583
- synth_ai/environments/examples/nethack/__init__.py +0 -7
- synth_ai/environments/examples/nethack/achievements.py +0 -337
- synth_ai/environments/examples/nethack/agent_demos/nethack_evaluation_framework.py +0 -981
- synth_ai/environments/examples/nethack/agent_demos/nethack_quick_evaluation.py +0 -74
- synth_ai/environments/examples/nethack/agent_demos/nethack_react_agent.py +0 -831
- synth_ai/environments/examples/nethack/engine.py +0 -739
- synth_ai/environments/examples/nethack/environment.py +0 -256
- synth_ai/environments/examples/nethack/helpers/__init__.py +0 -41
- synth_ai/environments/examples/nethack/helpers/action_mapping.py +0 -301
- synth_ai/environments/examples/nethack/helpers/nle_wrapper.py +0 -402
- synth_ai/environments/examples/nethack/helpers/observation_utils.py +0 -433
- synth_ai/environments/examples/nethack/helpers/recording_wrapper.py +0 -200
- synth_ai/environments/examples/nethack/helpers/trajectory_recorder.py +0 -269
- synth_ai/environments/examples/nethack/helpers/visualization/replay_viewer.py +0 -308
- synth_ai/environments/examples/nethack/helpers/visualization/visualizer.py +0 -431
- synth_ai/environments/examples/nethack/taskset.py +0 -323
- synth_ai/environments/examples/red/__init__.py +0 -7
- synth_ai/environments/examples/red/agent_demos/__init__.py +0 -1
- synth_ai/environments/examples/red/config_logging.py +0 -110
- synth_ai/environments/examples/red/engine.py +0 -694
- synth_ai/environments/examples/red/engine_helpers/__init__.py +0 -1
- synth_ai/environments/examples/red/engine_helpers/memory_map.py +0 -28
- synth_ai/environments/examples/red/engine_helpers/reward_components.py +0 -276
- synth_ai/environments/examples/red/engine_helpers/reward_library/__init__.py +0 -142
- synth_ai/environments/examples/red/engine_helpers/reward_library/adaptive_rewards.py +0 -57
- synth_ai/environments/examples/red/engine_helpers/reward_library/battle_rewards.py +0 -284
- synth_ai/environments/examples/red/engine_helpers/reward_library/composite_rewards.py +0 -150
- synth_ai/environments/examples/red/engine_helpers/reward_library/economy_rewards.py +0 -138
- synth_ai/environments/examples/red/engine_helpers/reward_library/efficiency_rewards.py +0 -57
- synth_ai/environments/examples/red/engine_helpers/reward_library/exploration_rewards.py +0 -331
- synth_ai/environments/examples/red/engine_helpers/reward_library/novelty_rewards.py +0 -121
- synth_ai/environments/examples/red/engine_helpers/reward_library/pallet_town_rewards.py +0 -559
- synth_ai/environments/examples/red/engine_helpers/reward_library/pokemon_rewards.py +0 -313
- synth_ai/environments/examples/red/engine_helpers/reward_library/social_rewards.py +0 -148
- synth_ai/environments/examples/red/engine_helpers/reward_library/story_rewards.py +0 -247
- synth_ai/environments/examples/red/engine_helpers/screen_analysis.py +0 -368
- synth_ai/environments/examples/red/engine_helpers/state_extraction.py +0 -140
- synth_ai/environments/examples/red/environment.py +0 -238
- synth_ai/environments/examples/red/taskset.py +0 -79
- synth_ai/environments/examples/red/units/__init__.py +0 -1
- synth_ai/environments/examples/sokoban/__init__.py +0 -1
- synth_ai/environments/examples/sokoban/agent_demos/sokoban_full_eval.py +0 -899
- synth_ai/environments/examples/sokoban/engine.py +0 -678
- synth_ai/environments/examples/sokoban/engine_helpers/__init__.py +0 -1
- synth_ai/environments/examples/sokoban/engine_helpers/room_utils.py +0 -657
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/__init__.py +0 -18
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/__init__.py +0 -3
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/boxoban_env.py +0 -131
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/render_utils.py +0 -370
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/room_utils.py +0 -332
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env.py +0 -306
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_fixed_targets.py +0 -67
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_pull.py +0 -115
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_two_player.py +0 -123
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_variations.py +0 -394
- synth_ai/environments/examples/sokoban/environment.py +0 -229
- synth_ai/environments/examples/sokoban/generate_verified_puzzles.py +0 -440
- synth_ai/environments/examples/sokoban/puzzle_loader.py +0 -312
- synth_ai/environments/examples/sokoban/taskset.py +0 -428
- synth_ai/environments/examples/tictactoe/__init__.py +0 -1
- synth_ai/environments/examples/tictactoe/engine.py +0 -368
- synth_ai/environments/examples/tictactoe/environment.py +0 -240
- synth_ai/environments/examples/tictactoe/taskset.py +0 -215
- synth_ai/environments/examples/verilog/__init__.py +0 -10
- synth_ai/environments/examples/verilog/engine.py +0 -329
- synth_ai/environments/examples/verilog/environment.py +0 -350
- synth_ai/environments/examples/verilog/taskset.py +0 -420
- synth_ai/environments/examples/wordle/__init__.py +0 -29
- synth_ai/environments/examples/wordle/engine.py +0 -398
- synth_ai/environments/examples/wordle/environment.py +0 -159
- synth_ai/environments/examples/wordle/helpers/generate_instances_wordfreq.py +0 -75
- synth_ai/environments/examples/wordle/taskset.py +0 -230
- synth_ai/environments/reproducibility/core.py +0 -42
- synth_ai/environments/reproducibility/helpers.py +0 -0
- synth_ai/environments/reproducibility/tree.py +0 -363
- synth_ai/environments/service/app.py +0 -97
- synth_ai/environments/service/core_routes.py +0 -1021
- synth_ai/environments/service/external_registry.py +0 -56
- synth_ai/environments/service/registry.py +0 -9
- synth_ai/environments/stateful/__init__.py +0 -1
- synth_ai/environments/stateful/core.py +0 -163
- synth_ai/environments/stateful/engine.py +0 -21
- synth_ai/environments/stateful/state.py +0 -7
- synth_ai/environments/tasks/api.py +0 -19
- synth_ai/environments/tasks/core.py +0 -81
- synth_ai/environments/tasks/filters.py +0 -40
- synth_ai/environments/tasks/utils.py +0 -90
- synth_ai/environments/v0_observability/history.py +0 -3
- synth_ai/environments/v0_observability/log.py +0 -2
- synth_ai/evals/base.py +0 -13
- synth_ai/handshake.py +0 -109
- synth_ai/http.py +0 -26
- synth_ai/http_client.py +0 -136
- synth_ai/inference/__init__.py +0 -5
- synth_ai/inference/client.py +0 -34
- synth_ai/jobs/client.py +0 -271
- synth_ai/learning/__init__.py +0 -59
- synth_ai/learning/client.py +0 -241
- synth_ai/learning/ft_client.py +0 -7
- synth_ai/learning/health.py +0 -49
- synth_ai/learning/jobs.py +0 -201
- synth_ai/learning/rl/client.py +0 -267
- synth_ai/learning/rl/contracts.py +0 -27
- synth_ai/learning/rl/env_keys.py +0 -166
- synth_ai/learning/rl/secrets.py +0 -13
- synth_ai/learning/sft/client.py +0 -68
- synth_ai/learning/sft/config.py +0 -270
- synth_ai/learning/sft/data.py +0 -295
- synth_ai/learning/validators.py +0 -49
- synth_ai/lm/__init__.py +0 -25
- synth_ai/main.py +0 -6
- synth_ai/task/__init__.py +0 -102
- synth_ai/task/apps/__init__.py +0 -128
- synth_ai/task/contracts.py +0 -137
- synth_ai/task/datasets.py +0 -108
- synth_ai/task/proxy.py +0 -259
- synth_ai/task/server.py +0 -424
- synth_ai/task/tracing_utils.py +0 -84
- synth_ai/task/validators.py +0 -11
- synth_ai/tracing_v3/__init__.py +0 -97
- synth_ai/tracing_v3/config.py +0 -84
- synth_ai/tracing_v3/db_config.py +0 -194
- synth_ai/tracing_v3/decorators.py +0 -369
- synth_ai/tracing_v3/examples/basic_usage.py +0 -189
- synth_ai/tracing_v3/llm_call_record_helpers.py +0 -337
- synth_ai/tracing_v3/migration_helper.py +0 -120
- synth_ai/tracing_v3/replica_sync.py +0 -258
- synth_ai/tracing_v3/session_tracer.py +0 -530
- synth_ai/tracing_v3/storage/base.py +0 -210
- synth_ai/tracing_v3/storage/config.py +0 -75
- synth_ai/tracing_v3/storage/factory.py +0 -39
- synth_ai/tracing_v3/storage/utils.py +0 -204
- synth_ai/tracing_v3/turso/daemon.py +0 -149
- synth_ai/tracing_v3/turso/models.py +0 -469
- synth_ai/tracing_v3/turso/native_manager.py +0 -1173
- synth_ai/tracing_v3/utils.py +0 -108
- synth_ai/v0/api/__init__.py +0 -8
- synth_ai/v0/api/models/__init__.py +0 -8
- synth_ai/v0/api/models/supported.py +0 -8
- synth_ai/v0/config/__init__.py +0 -15
- synth_ai/v0/config/base_url.py +0 -12
- synth_ai/v0/lm/__init__.py +0 -51
- synth_ai/v0/lm/caching/constants.py +0 -6
- synth_ai/v0/lm/caching/dbs.py +0 -0
- synth_ai/v0/lm/caching/ephemeral.py +0 -100
- synth_ai/v0/lm/caching/handler.py +0 -137
- synth_ai/v0/lm/caching/initialize.py +0 -11
- synth_ai/v0/lm/caching/persistent.py +0 -114
- synth_ai/v0/lm/config.py +0 -115
- synth_ai/v0/lm/constants.py +0 -32
- synth_ai/v0/lm/core/__init__.py +0 -8
- synth_ai/v0/lm/core/all.py +0 -73
- synth_ai/v0/lm/core/exceptions.py +0 -5
- synth_ai/v0/lm/core/main.py +0 -331
- synth_ai/v0/lm/core/main_v3.py +0 -594
- synth_ai/v0/lm/core/synth_models.py +0 -35
- synth_ai/v0/lm/core/vendor_clients.py +0 -190
- synth_ai/v0/lm/cost/__init__.py +0 -0
- synth_ai/v0/lm/cost/monitor.py +0 -1
- synth_ai/v0/lm/cost/statefulness.py +0 -1
- synth_ai/v0/lm/injection.py +0 -80
- synth_ai/v0/lm/overrides.py +0 -206
- synth_ai/v0/lm/provider_support/__init__.py +0 -8
- synth_ai/v0/lm/provider_support/anthropic.py +0 -972
- synth_ai/v0/lm/provider_support/openai.py +0 -1139
- synth_ai/v0/lm/provider_support/suppress_logging.py +0 -31
- synth_ai/v0/lm/structured_outputs/__init__.py +0 -0
- synth_ai/v0/lm/structured_outputs/handler.py +0 -440
- synth_ai/v0/lm/structured_outputs/inject.py +0 -297
- synth_ai/v0/lm/structured_outputs/rehabilitate.py +0 -185
- synth_ai/v0/lm/tools/__init__.py +0 -3
- synth_ai/v0/lm/tools/base.py +0 -172
- synth_ai/v0/lm/unified_interface.py +0 -202
- synth_ai/v0/lm/vendors/__init__.py +0 -0
- synth_ai/v0/lm/vendors/base.py +0 -81
- synth_ai/v0/lm/vendors/core/__init__.py +0 -0
- synth_ai/v0/lm/vendors/core/anthropic_api.py +0 -387
- synth_ai/v0/lm/vendors/core/gemini_api.py +0 -292
- synth_ai/v0/lm/vendors/core/mistral_api.py +0 -322
- synth_ai/v0/lm/vendors/core/openai_api.py +0 -227
- synth_ai/v0/lm/vendors/core/synth_dev_api.py +0 -0
- synth_ai/v0/lm/vendors/local/__init__.py +0 -0
- synth_ai/v0/lm/vendors/local/ollama.py +0 -0
- synth_ai/v0/lm/vendors/openai_standard.py +0 -782
- synth_ai/v0/lm/vendors/openai_standard_responses.py +0 -259
- synth_ai/v0/lm/vendors/retries.py +0 -22
- synth_ai/v0/lm/vendors/supported/__init__.py +0 -0
- synth_ai/v0/lm/vendors/supported/custom_endpoint.py +0 -415
- synth_ai/v0/lm/vendors/supported/deepseek.py +0 -69
- synth_ai/v0/lm/vendors/supported/grok.py +0 -75
- synth_ai/v0/lm/vendors/supported/groq.py +0 -16
- synth_ai/v0/lm/vendors/supported/ollama.py +0 -15
- synth_ai/v0/lm/vendors/supported/openrouter.py +0 -74
- synth_ai/v0/lm/vendors/supported/together.py +0 -11
- synth_ai/v0/lm/vendors/synth_client.py +0 -835
- synth_ai/v0/lm/warmup.py +0 -186
- synth_ai/v0/tracing/__init__.py +0 -0
- synth_ai/v0/tracing/abstractions.py +0 -224
- synth_ai/v0/tracing/base_client.py +0 -91
- synth_ai/v0/tracing/client_manager.py +0 -131
- synth_ai/v0/tracing/config.py +0 -142
- synth_ai/v0/tracing/context.py +0 -146
- synth_ai/v0/tracing/decorators.py +0 -682
- synth_ai/v0/tracing/events/__init__.py +0 -0
- synth_ai/v0/tracing/events/manage.py +0 -147
- synth_ai/v0/tracing/events/scope.py +0 -86
- synth_ai/v0/tracing/events/store.py +0 -228
- synth_ai/v0/tracing/immediate_client.py +0 -151
- synth_ai/v0/tracing/local.py +0 -18
- synth_ai/v0/tracing/log_client_base.py +0 -73
- synth_ai/v0/tracing/retry_queue.py +0 -186
- synth_ai/v0/tracing/trackers.py +0 -515
- synth_ai/v0/tracing/upload.py +0 -409
- synth_ai/v0/tracing/utils.py +0 -9
- synth_ai/v0/tracing_v1/__init__.py +0 -16
- synth_ai/v0/tracing_v1/abstractions.py +0 -224
- synth_ai/v0/tracing_v1/base_client.py +0 -91
- synth_ai/v0/tracing_v1/client_manager.py +0 -131
- synth_ai/v0/tracing_v1/config.py +0 -142
- synth_ai/v0/tracing_v1/context.py +0 -146
- synth_ai/v0/tracing_v1/decorators.py +0 -703
- synth_ai/v0/tracing_v1/events/__init__.py +0 -0
- synth_ai/v0/tracing_v1/events/manage.py +0 -147
- synth_ai/v0/tracing_v1/events/scope.py +0 -86
- synth_ai/v0/tracing_v1/events/store.py +0 -228
- synth_ai/v0/tracing_v1/immediate_client.py +0 -151
- synth_ai/v0/tracing_v1/local.py +0 -18
- synth_ai/v0/tracing_v1/log_client_base.py +0 -73
- synth_ai/v0/tracing_v1/retry_queue.py +0 -186
- synth_ai/v0/tracing_v1/trackers.py +0 -515
- synth_ai/v0/tracing_v1/upload.py +0 -527
- synth_ai/v0/tracing_v1/utils.py +0 -9
- synth_ai/v0/tracing_v3/__init__.py +0 -10
- synth_ai/v0/tracing_v3/abstractions.py +0 -3
- synth_ai/v0/tracing_v3/decorators.py +0 -3
- synth_ai/v0/tracing_v3/llm_call_record_helpers.py +0 -3
- synth_ai/v0/tracing_v3/session_tracer.py +0 -3
- synth_ai-0.2.9.dev11.dist-info/METADATA +0 -191
- synth_ai-0.2.9.dev11.dist-info/RECORD +0 -571
- synth_ai-0.2.9.dev11.dist-info/entry_points.txt +0 -3
- synth_ai-0.2.9.dev11.dist-info/top_level.txt +0 -3
- /synth_ai/{demos/demo_task_apps → cli/demo_apps}/crafter/__init__.py +0 -0
- /synth_ai/{demos → cli/demo_apps}/demo_task_apps/__init__.py +0 -0
- /synth_ai/{demos → cli/demo_apps}/demo_task_apps/crafter/configs/crafter_fft_4b.toml +0 -0
- /synth_ai/{demos → cli/demo_apps}/demo_task_apps/crafter/configs/rl_from_base_qwen4b.toml +0 -0
- /synth_ai/{demos → cli/demo_apps}/demo_task_apps/math/__init__.py +0 -0
- /synth_ai/{demos → cli/demo_apps}/demo_task_apps/math/_common.py +0 -0
- /synth_ai/{demos → cli/demo_apps}/demo_task_apps/math/app.py +0 -0
- /synth_ai/{demos → cli/demo_apps}/demo_task_apps/math/config.toml +0 -0
- /synth_ai/{demos → cli/demo_apps}/demo_task_apps/math/deploy_modal.py +0 -0
- /synth_ai/{v0/lm/caching → core/apps}/__init__.py +0 -0
- /synth_ai/{tracing_v3 → core/tracing_v3}/abstractions.py +0 -0
- /synth_ai/{tracing_v3 → core/tracing_v3}/hooks.py +0 -0
- /synth_ai/{tracing_v3 → core/tracing_v3}/lm_call_record_abstractions.py +0 -0
- /synth_ai/{tracing_v3 → core/tracing_v3}/storage/__init__.py +0 -0
- /synth_ai/{tracing_v3 → core/tracing_v3}/storage/exceptions.py +0 -0
- /synth_ai/{tracing_v3 → core/tracing_v3}/storage/types.py +0 -0
- /synth_ai/{compound/cais.py → py.typed} +0 -0
- /synth_ai/{learning → sdk/learning}/algorithms.py +0 -0
- /synth_ai/{learning → sdk/learning}/config.py +0 -0
- /synth_ai/{learning → sdk/learning}/constants.py +0 -0
- /synth_ai/{learning → sdk/learning}/core.py +0 -0
- /synth_ai/{learning → sdk/learning}/gateway.py +0 -0
- /synth_ai/{learning → sdk/learning}/rl/__init__.py +0 -0
- /synth_ai/{learning → sdk/learning}/rl/config.py +0 -0
- /synth_ai/{learning → sdk/learning}/rl_client.py +0 -0
- /synth_ai/{learning → sdk/learning}/sft/__init__.py +0 -0
- /synth_ai/{learning → sdk/learning}/sse.py +0 -0
- /synth_ai/{task → sdk/task}/auth.py +0 -0
- /synth_ai/{task → sdk/task}/client.py +0 -0
- /synth_ai/{task → sdk/task}/errors.py +0 -0
- /synth_ai/{task → sdk/task}/health.py +0 -0
- /synth_ai/{task → sdk/task}/json.py +0 -0
- /synth_ai/{task → sdk/task}/rubrics.py +0 -0
- /synth_ai/{task → sdk/task}/vendors.py +0 -0
- {synth_ai-0.2.9.dev11.dist-info → synth_ai-0.4.1.dist-info}/WHEEL +0 -0
- {synth_ai-0.2.9.dev11.dist-info → synth_ai-0.4.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,643 @@
|
|
|
1
|
+
import contextlib
|
|
2
|
+
import inspect
|
|
3
|
+
import io
|
|
4
|
+
import os
|
|
5
|
+
import secrets
|
|
6
|
+
from collections.abc import Mapping, MutableMapping, Sequence
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any, Callable, Set, cast
|
|
9
|
+
from urllib.parse import parse_qs, urlparse
|
|
10
|
+
|
|
11
|
+
from fastapi.routing import APIRoute, APIRouter
|
|
12
|
+
from fastapi.testclient import TestClient
|
|
13
|
+
from starlette.middleware import Middleware
|
|
14
|
+
from starlette.types import ASGIApp
|
|
15
|
+
from synth_ai.cli.lib.prompts import ctx_print
|
|
16
|
+
from synth_ai.core.apps.common import (
|
|
17
|
+
build_fastapi_route_index,
|
|
18
|
+
extract_routes_from_app,
|
|
19
|
+
get_asgi_app,
|
|
20
|
+
load_module,
|
|
21
|
+
validate_py_file_compiles,
|
|
22
|
+
)
|
|
23
|
+
from synth_ai.core.paths import is_hidden_path, validate_file_type
|
|
24
|
+
from synth_ai.sdk.task.contracts import TaskInfo
|
|
25
|
+
from synth_ai.sdk.task.datasets import TaskDatasetRegistry
|
|
26
|
+
from synth_ai.sdk.task.server import ProxyConfig, RubricBundle, TaskAppConfig
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def validate_required_routes_exist(app: ASGIApp) -> None:
|
|
30
|
+
routes = set(extract_routes_from_app(app))
|
|
31
|
+
required_endpoints: Set[str] = {
|
|
32
|
+
'/',
|
|
33
|
+
"/health",
|
|
34
|
+
"/info",
|
|
35
|
+
"/task_info",
|
|
36
|
+
"/rollout"
|
|
37
|
+
}
|
|
38
|
+
missing = required_endpoints - routes
|
|
39
|
+
if missing:
|
|
40
|
+
raise ValueError(f"Missing required FastAPI endpoints: {', '.join(sorted(missing))}")
|
|
41
|
+
return None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
RouteContract = MutableMapping[str, Any]
|
|
45
|
+
ROUTE_CONTRACTS: dict[str, RouteContract] = {
|
|
46
|
+
"/": {
|
|
47
|
+
"method": "GET",
|
|
48
|
+
"require_auth": False,
|
|
49
|
+
"required_params": set(),
|
|
50
|
+
"response_validator": lambda payload, _: _validate_root_payload(payload),
|
|
51
|
+
},
|
|
52
|
+
"/health": {
|
|
53
|
+
"method": "GET",
|
|
54
|
+
"require_auth": True,
|
|
55
|
+
"required_params": {"request"},
|
|
56
|
+
"response_validator": lambda payload, _: _validate_health_payload(payload),
|
|
57
|
+
},
|
|
58
|
+
"/info": {
|
|
59
|
+
"method": "GET",
|
|
60
|
+
"require_auth": True,
|
|
61
|
+
"required_params": set(),
|
|
62
|
+
"response_validator": lambda payload, _: _validate_info_payload(payload),
|
|
63
|
+
},
|
|
64
|
+
"/task_info": {
|
|
65
|
+
"method": "GET",
|
|
66
|
+
"require_auth": True,
|
|
67
|
+
"required_params": {"seed", "seeds"},
|
|
68
|
+
"response_validator": lambda payload, _: _validate_task_info_payload(payload),
|
|
69
|
+
},
|
|
70
|
+
"/rollout": {
|
|
71
|
+
"method": "POST",
|
|
72
|
+
"require_auth": True,
|
|
73
|
+
"required_params": {"rollout_request"},
|
|
74
|
+
"response_validator": lambda payload, _: _validate_rollout_payload(payload),
|
|
75
|
+
},
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
def _ensure_mapping(payload: Any, path: str) -> Mapping[str, Any]:
|
|
79
|
+
if not isinstance(payload, Mapping):
|
|
80
|
+
raise ValueError(f"{path} must return a JSON object")
|
|
81
|
+
return payload
|
|
82
|
+
|
|
83
|
+
def _validate_root_payload(payload: Any) -> None:
|
|
84
|
+
data = _ensure_mapping(payload, "/")
|
|
85
|
+
status = data.get("status")
|
|
86
|
+
service = data.get("service")
|
|
87
|
+
if not isinstance(status, str) or not status:
|
|
88
|
+
raise ValueError("`/` must return a status string")
|
|
89
|
+
if not isinstance(service, str) or not service:
|
|
90
|
+
raise ValueError("`/` must return a service string")
|
|
91
|
+
|
|
92
|
+
def _validate_health_payload(payload: Any) -> None:
|
|
93
|
+
data = _ensure_mapping(payload, "/health")
|
|
94
|
+
healthy = data.get("healthy")
|
|
95
|
+
auth = data.get("auth")
|
|
96
|
+
if not isinstance(healthy, bool):
|
|
97
|
+
raise ValueError("`/health` must return a boolean `healthy` field")
|
|
98
|
+
if not isinstance(auth, Mapping):
|
|
99
|
+
raise ValueError("`/health` must include an `auth` object")
|
|
100
|
+
required_keys = {"required", "expected_prefix"}
|
|
101
|
+
missing = required_keys - set(auth)
|
|
102
|
+
if missing:
|
|
103
|
+
raise ValueError(f"`/health` auth payload missing keys: {', '.join(sorted(missing))}")
|
|
104
|
+
|
|
105
|
+
def _validate_info_payload(payload: Any) -> None:
|
|
106
|
+
data = _ensure_mapping(payload, "/info")
|
|
107
|
+
service = data.get("service")
|
|
108
|
+
dataset = data.get("dataset")
|
|
109
|
+
if not isinstance(service, Mapping):
|
|
110
|
+
raise ValueError("`/info` must include a service object")
|
|
111
|
+
if "task" not in service:
|
|
112
|
+
raise ValueError("`/info` service object must include a `task` field")
|
|
113
|
+
if dataset is None:
|
|
114
|
+
raise ValueError("`/info` must include dataset metadata")
|
|
115
|
+
|
|
116
|
+
def _validate_task_info_payload(payload: Any) -> None:
|
|
117
|
+
data = _ensure_mapping(payload, "/task_info")
|
|
118
|
+
if "taskset" not in data:
|
|
119
|
+
raise ValueError("`/task_info` without seeds must include a `taskset` field")
|
|
120
|
+
|
|
121
|
+
def _validate_rollout_payload(payload: Any) -> None:
|
|
122
|
+
"""Validate that /rollout returns a proper RolloutResponse schema.
|
|
123
|
+
|
|
124
|
+
This catches the common error: "Pattern validation failed: Failed to fetch
|
|
125
|
+
baseline messages: No trajectories in response" that occurs when manual
|
|
126
|
+
FastAPI implementations return simplified response formats instead of the
|
|
127
|
+
complete RolloutResponse schema required by training.
|
|
128
|
+
"""
|
|
129
|
+
data = _ensure_mapping(payload, "/rollout")
|
|
130
|
+
|
|
131
|
+
# Check required top-level fields
|
|
132
|
+
required_fields = ["run_id", "trajectories", "metrics"]
|
|
133
|
+
for field in required_fields:
|
|
134
|
+
if field not in data:
|
|
135
|
+
raise ValueError(
|
|
136
|
+
f"`/rollout` response missing required field '{field}'. "
|
|
137
|
+
f"The response must include: run_id, trajectories, and metrics. "
|
|
138
|
+
f"This error often occurs with manual FastAPI implementations. "
|
|
139
|
+
f"Use create_task_app(build_config()) instead."
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
# Validate trajectories is a list
|
|
143
|
+
trajectories = data.get("trajectories")
|
|
144
|
+
if not isinstance(trajectories, list):
|
|
145
|
+
raise ValueError(
|
|
146
|
+
f"`/rollout` response field 'trajectories' must be a list, got {type(trajectories).__name__}. "
|
|
147
|
+
f"Make sure your rollout executor returns a proper RolloutResponse with a list of RolloutTrajectory objects."
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
# Ensure trajectories list is not empty (training will fail if it's empty)
|
|
151
|
+
if len(trajectories) == 0:
|
|
152
|
+
raise ValueError(
|
|
153
|
+
"`/rollout` response field 'trajectories' is an empty list. "
|
|
154
|
+
"Training will fail with 'No trajectories in response'. "
|
|
155
|
+
"Your rollout executor must return at least one trajectory with steps."
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
# Validate first trajectory has required fields
|
|
159
|
+
first_traj = trajectories[0]
|
|
160
|
+
if not isinstance(first_traj, Mapping):
|
|
161
|
+
raise ValueError(
|
|
162
|
+
f"`/rollout` trajectories must contain objects, got {type(first_traj).__name__}"
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
required_traj_fields = ["env_id", "policy_id", "steps", "length", "inference_url"]
|
|
166
|
+
for field in required_traj_fields:
|
|
167
|
+
if field not in first_traj:
|
|
168
|
+
raise ValueError(
|
|
169
|
+
f"`/rollout` trajectory missing required field '{field}'. "
|
|
170
|
+
f"Each trajectory must include: env_id, policy_id, steps, length, and inference_url."
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
# STRICT: Validate inference_url contains correlation ID for trace correlation
|
|
174
|
+
# This catches: "Rollout response is missing trace correlation IDs in inference_url entries"
|
|
175
|
+
inference_url = first_traj.get("inference_url")
|
|
176
|
+
if inference_url is None:
|
|
177
|
+
raise ValueError(
|
|
178
|
+
"`/rollout` trajectory.inference_url is None. "
|
|
179
|
+
"Each trajectory must include a valid inference_url with ?cid=trace_xxxxx parameter for trace correlation. "
|
|
180
|
+
"Example: 'http://example.com/v1/chat/completions?cid=trace_abc123'"
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
if not isinstance(inference_url, str):
|
|
184
|
+
raise ValueError(
|
|
185
|
+
f"`/rollout` trajectory.inference_url must be a string, got {type(inference_url).__name__}"
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
parsed_inference_url = urlparse(inference_url)
|
|
189
|
+
if not parsed_inference_url.scheme or not parsed_inference_url.netloc:
|
|
190
|
+
raise ValueError(
|
|
191
|
+
"`/rollout` trajectory.inference_url must include a scheme and host. "
|
|
192
|
+
"Example: 'http://example.com?cid=trace_abc123'"
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
if parsed_inference_url.path not in ("", "/"):
|
|
196
|
+
raise ValueError(
|
|
197
|
+
f"`/rollout` trajectory.inference_url must be a base URL only (scheme + host). "
|
|
198
|
+
f"Found path: '{parsed_inference_url.path}'. "
|
|
199
|
+
f"Remove the path component - the backend will append it automatically. "
|
|
200
|
+
f"Expected format: 'http://example.com?cid=trace_xxx' (no '/v1' or '/v1/chat/completions')."
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
cid_values = parse_qs(parsed_inference_url.query).get("cid", [])
|
|
204
|
+
if not cid_values or not cid_values[0]:
|
|
205
|
+
raise ValueError(
|
|
206
|
+
"`/rollout` trajectory.inference_url missing correlation ID parameter. "
|
|
207
|
+
"URL must include ?cid=trace_xxxxx for trace correlation. "
|
|
208
|
+
"Example: 'http://example.com?cid=trace_abc123' (note: no path, just base URL + query)."
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
# Validate steps is a list and not empty
|
|
212
|
+
steps = first_traj.get("steps")
|
|
213
|
+
if not isinstance(steps, list):
|
|
214
|
+
raise ValueError(
|
|
215
|
+
f"`/rollout` trajectory 'steps' must be a list, got {type(steps).__name__}"
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
# For prompt learning (MIPRO, etc), we need messages in step.info
|
|
219
|
+
# This catches: "Could not extract messages from rollout response - ensure task app stores messages in step.info"
|
|
220
|
+
if len(steps) > 0:
|
|
221
|
+
first_step = steps[0]
|
|
222
|
+
if not isinstance(first_step, Mapping):
|
|
223
|
+
raise ValueError(
|
|
224
|
+
f"`/rollout` steps must contain objects, got {type(first_step).__name__}"
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
# STRICT: Require step.info with messages for prompt learning compatibility
|
|
228
|
+
step_info = first_step.get("info")
|
|
229
|
+
if step_info is None:
|
|
230
|
+
raise ValueError(
|
|
231
|
+
"`/rollout` step.info is missing. "
|
|
232
|
+
"For prompt learning (MIPRO), each step must include an 'info' field with 'messages'. "
|
|
233
|
+
"Use create_task_app(build_config()) with a proper rollout executor."
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
if not isinstance(step_info, Mapping):
|
|
237
|
+
raise ValueError(
|
|
238
|
+
f"`/rollout` step.info must be an object, got {type(step_info).__name__}"
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
messages = step_info.get("messages")
|
|
242
|
+
if messages is None:
|
|
243
|
+
raise ValueError(
|
|
244
|
+
"`/rollout` step.info['messages'] is missing. "
|
|
245
|
+
"Prompt learning requires conversation history in step.info['messages']. "
|
|
246
|
+
"The SDK's rollout executor handles this automatically."
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
if not isinstance(messages, list):
|
|
250
|
+
raise ValueError(
|
|
251
|
+
f"`/rollout` step.info['messages'] must be a list, got {type(messages).__name__}"
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
if len(messages) == 0:
|
|
255
|
+
raise ValueError(
|
|
256
|
+
"`/rollout` step.info['messages'] is an empty list. "
|
|
257
|
+
"Prompt learning requires at least one message in the conversation history."
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
# Validate metrics structure
|
|
261
|
+
metrics = data.get("metrics")
|
|
262
|
+
if not isinstance(metrics, Mapping):
|
|
263
|
+
raise ValueError(
|
|
264
|
+
f"`/rollout` response field 'metrics' must be an object, got {type(metrics).__name__}"
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
# Metrics can be either:
|
|
268
|
+
# 1. Full RolloutMetrics with episode_returns (list), mean_return, num_steps
|
|
269
|
+
# 2. Simple dict with scalar values (episode_returns as float, mean_return, num_steps)
|
|
270
|
+
required_metrics_fields = ["episode_returns", "mean_return", "num_steps"]
|
|
271
|
+
for field in required_metrics_fields:
|
|
272
|
+
if field not in metrics:
|
|
273
|
+
raise ValueError(
|
|
274
|
+
f"`/rollout` metrics missing required field '{field}'. "
|
|
275
|
+
f"Metrics must include: episode_returns, mean_return, and num_steps."
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
# Validate types - episode_returns can be either a list or a scalar
|
|
279
|
+
episode_returns = metrics.get("episode_returns")
|
|
280
|
+
if not isinstance(episode_returns, list | int | float):
|
|
281
|
+
raise ValueError(
|
|
282
|
+
f"`/rollout` metrics.episode_returns must be a list or number, got {type(episode_returns).__name__}"
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
mean_return = metrics.get("mean_return")
|
|
286
|
+
if not isinstance(mean_return, int | float):
|
|
287
|
+
raise ValueError(
|
|
288
|
+
f"`/rollout` metrics.mean_return must be a number, got {type(mean_return).__name__}"
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
num_steps = metrics.get("num_steps")
|
|
292
|
+
if not isinstance(num_steps, int):
|
|
293
|
+
raise ValueError(
|
|
294
|
+
f"`/rollout` metrics.num_steps must be an integer, got {type(num_steps).__name__}"
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def validate_route_contracts(app: ASGIApp) -> None:
|
|
299
|
+
route_index = build_fastapi_route_index(app)
|
|
300
|
+
|
|
301
|
+
def _get_route(path: str, method: str) -> APIRoute:
|
|
302
|
+
candidates = route_index.get(path, [])
|
|
303
|
+
if not candidates:
|
|
304
|
+
raise ValueError(f"No route registered for {path}")
|
|
305
|
+
method_upper = method.upper()
|
|
306
|
+
for route in candidates:
|
|
307
|
+
methods = {m.upper() for m in (route.methods or [])}
|
|
308
|
+
if method_upper in methods:
|
|
309
|
+
return route
|
|
310
|
+
raise ValueError(f"Route {path} missing required method {method_upper}")
|
|
311
|
+
|
|
312
|
+
def _ensure_params(route: APIRoute, expected: Set[str], path: str) -> None:
|
|
313
|
+
if not expected:
|
|
314
|
+
return
|
|
315
|
+
signature = inspect.signature(route.endpoint)
|
|
316
|
+
present = set(signature.parameters)
|
|
317
|
+
missing = expected - present
|
|
318
|
+
if missing:
|
|
319
|
+
raise ValueError(
|
|
320
|
+
f"{path} endpoint missing required parameters: {', '.join(sorted(missing))}"
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
for path, spec in ROUTE_CONTRACTS.items():
|
|
324
|
+
route = _get_route(path, spec["method"])
|
|
325
|
+
_ensure_params(route, set(spec.get("required_params", set())), path)
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def test_route_contracts(app: ASGIApp) -> None:
|
|
329
|
+
route_index = build_fastapi_route_index(app)
|
|
330
|
+
|
|
331
|
+
def _base_url(url: str) -> str:
|
|
332
|
+
parsed = urlparse(url)
|
|
333
|
+
if not parsed.scheme or not parsed.netloc:
|
|
334
|
+
raise ValueError("Inference URLs must include a scheme and host")
|
|
335
|
+
return f"{parsed.scheme}://{parsed.netloc}"
|
|
336
|
+
|
|
337
|
+
def _get_route(path: str, method: str) -> APIRoute:
|
|
338
|
+
candidates = route_index.get(path, [])
|
|
339
|
+
if not candidates:
|
|
340
|
+
raise ValueError(f"No route registered for {path}")
|
|
341
|
+
method_upper = method.upper()
|
|
342
|
+
for route in candidates:
|
|
343
|
+
methods = {m.upper() for m in (route.methods or [])}
|
|
344
|
+
if method_upper in methods:
|
|
345
|
+
return route
|
|
346
|
+
raise ValueError(f"Route {path} missing required method {method_upper}")
|
|
347
|
+
|
|
348
|
+
for path, spec in ROUTE_CONTRACTS.items():
|
|
349
|
+
_get_route(path, spec["method"])
|
|
350
|
+
|
|
351
|
+
# Ensure ENVIRONMENT_API_KEY present for auth-required routes
|
|
352
|
+
original_env_key = os.environ.get("ENVIRONMENT_API_KEY")
|
|
353
|
+
auth_key = original_env_key or secrets.token_hex(16)
|
|
354
|
+
if original_env_key is None:
|
|
355
|
+
os.environ["ENVIRONMENT_API_KEY"] = auth_key
|
|
356
|
+
auth_headers = {"X-API-Key": auth_key}
|
|
357
|
+
rollout_interceptor_url = "http://0.0.0.0:54444/v1/chat/completions"
|
|
358
|
+
rollout_interceptor_base = _base_url(rollout_interceptor_url)
|
|
359
|
+
rollout_trace_id = "trace_contract_validation"
|
|
360
|
+
|
|
361
|
+
try:
|
|
362
|
+
with TestClient(cast(ASGIApp, app)) as client: # type: ignore[redundant-cast]
|
|
363
|
+
for path, spec in ROUTE_CONTRACTS.items():
|
|
364
|
+
headers = auth_headers if spec.get("require_auth") else {}
|
|
365
|
+
params = None
|
|
366
|
+
json_payload = None
|
|
367
|
+
if path == "/task_info":
|
|
368
|
+
params = {"seed": 0}
|
|
369
|
+
if path == "/rollout":
|
|
370
|
+
# Send the actual RolloutRequest format used by prompt learning backend
|
|
371
|
+
# This matches the payload from evaluation.py:_execute_rollout_request()
|
|
372
|
+
json_payload = {
|
|
373
|
+
"run_id": "validate",
|
|
374
|
+
"env": {
|
|
375
|
+
"env_name": "validation",
|
|
376
|
+
"config": {"index": 0},
|
|
377
|
+
"seed": 0,
|
|
378
|
+
},
|
|
379
|
+
"policy": {
|
|
380
|
+
"policy_name": "validation",
|
|
381
|
+
"config": {
|
|
382
|
+
"model": "gpt-4o-mini",
|
|
383
|
+
"provider": "openai",
|
|
384
|
+
"temperature": 0.7,
|
|
385
|
+
"inference_url": rollout_interceptor_url,
|
|
386
|
+
"trace_correlation_id": rollout_trace_id,
|
|
387
|
+
},
|
|
388
|
+
"assert_proxy": True, # Backend always sets this for prompt learning
|
|
389
|
+
"proxy_only": True, # Backend always sets this for prompt learning
|
|
390
|
+
},
|
|
391
|
+
"ops": ["agent", "env"], # Critical: training sends this
|
|
392
|
+
"record": {"trajectories": True},
|
|
393
|
+
"mode": "eval",
|
|
394
|
+
}
|
|
395
|
+
validator = spec.get("response_validator")
|
|
396
|
+
response = client.request(
|
|
397
|
+
spec["method"],
|
|
398
|
+
path,
|
|
399
|
+
headers=headers,
|
|
400
|
+
params=params,
|
|
401
|
+
json=json_payload,
|
|
402
|
+
)
|
|
403
|
+
if response.status_code >= 400:
|
|
404
|
+
raise ValueError(
|
|
405
|
+
f"{path} responded with HTTP {response.status_code} during validation"
|
|
406
|
+
)
|
|
407
|
+
if validator is None:
|
|
408
|
+
continue
|
|
409
|
+
try:
|
|
410
|
+
payload = response.json()
|
|
411
|
+
except ValueError as exc:
|
|
412
|
+
raise ValueError(f"{path} did not return JSON during validation") from exc
|
|
413
|
+
validator(payload, path)
|
|
414
|
+
|
|
415
|
+
if path == "/rollout":
|
|
416
|
+
trajectories = payload.get("trajectories") or []
|
|
417
|
+
first_traj = trajectories[0]
|
|
418
|
+
inference_url = first_traj.get("inference_url")
|
|
419
|
+
parsed_inference_url = urlparse(inference_url)
|
|
420
|
+
returned_base = f"{parsed_inference_url.scheme}://{parsed_inference_url.netloc}"
|
|
421
|
+
if returned_base != rollout_interceptor_base:
|
|
422
|
+
raise ValueError(
|
|
423
|
+
"`/rollout` trajectory.inference_url must use the interceptor base URL "
|
|
424
|
+
"provided in policy.config.inference_url."
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
cid_values = parse_qs(parsed_inference_url.query).get("cid", [])
|
|
428
|
+
if rollout_trace_id not in cid_values:
|
|
429
|
+
raise ValueError(
|
|
430
|
+
"`/rollout` trajectory.inference_url must include the trace correlation "
|
|
431
|
+
"ID from policy.config.trace_correlation_id."
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
# Ensure auth dependency rejects missing key for protected endpoints
|
|
435
|
+
protected_paths = [p for p, spec in ROUTE_CONTRACTS.items() if spec["require_auth"]]
|
|
436
|
+
if protected_paths:
|
|
437
|
+
resp = client.get(protected_paths[0])
|
|
438
|
+
if resp.status_code == 200:
|
|
439
|
+
raise ValueError(
|
|
440
|
+
f"{protected_paths[0]} accepted requests without ENVIRONMENT_API_KEY"
|
|
441
|
+
)
|
|
442
|
+
finally:
|
|
443
|
+
if original_env_key is None:
|
|
444
|
+
os.environ.pop("ENVIRONMENT_API_KEY", None)
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
def validate_config_structure(cfg: TaskAppConfig) -> TaskAppConfig:
|
|
448
|
+
def callable_signature(name: str, fn: Callable[..., Any]) -> inspect.Signature:
|
|
449
|
+
try:
|
|
450
|
+
return inspect.signature(fn)
|
|
451
|
+
except (TypeError, ValueError) as exc:
|
|
452
|
+
raise ValueError(f"{name} must be a callable with an inspectable signature") from exc
|
|
453
|
+
|
|
454
|
+
def required_parameters(sig: inspect.Signature) -> list[inspect.Parameter]:
|
|
455
|
+
positional_kinds = {
|
|
456
|
+
inspect.Parameter.POSITIONAL_ONLY,
|
|
457
|
+
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
458
|
+
}
|
|
459
|
+
return [
|
|
460
|
+
param
|
|
461
|
+
for param in sig.parameters.values()
|
|
462
|
+
if param.kind in positional_kinds and param.default is inspect._empty
|
|
463
|
+
]
|
|
464
|
+
|
|
465
|
+
for field_name in ("app_id", "name", "description"):
|
|
466
|
+
value = getattr(cfg, field_name, None)
|
|
467
|
+
if not isinstance(value, str) or not value.strip():
|
|
468
|
+
raise ValueError(f"TaskAppConfig.{field_name} must be a non-empty string")
|
|
469
|
+
|
|
470
|
+
try:
|
|
471
|
+
TaskInfo.model_validate(cfg.base_task_info)
|
|
472
|
+
except Exception as exc:
|
|
473
|
+
raise ValueError("TaskAppConfig.base_task_info must be a valid TaskInfo") from exc
|
|
474
|
+
|
|
475
|
+
if not callable(cfg.describe_taskset):
|
|
476
|
+
raise ValueError("TaskAppConfig.describe_taskset must be callable")
|
|
477
|
+
describe_sig = callable_signature("describe_taskset", cfg.describe_taskset)
|
|
478
|
+
describe_required = required_parameters(describe_sig)
|
|
479
|
+
if describe_required:
|
|
480
|
+
required_names = ", ".join(param.name for param in describe_required)
|
|
481
|
+
raise ValueError(
|
|
482
|
+
f"describe_taskset must not require positional arguments (found: {required_names})"
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
if not callable(cfg.provide_task_instances):
|
|
486
|
+
raise ValueError("TaskAppConfig.provide_task_instances must be callable")
|
|
487
|
+
provide_sig = callable_signature("provide_task_instances", cfg.provide_task_instances)
|
|
488
|
+
provide_required = required_parameters(provide_sig)
|
|
489
|
+
if not provide_required:
|
|
490
|
+
raise ValueError("provide_task_instances must accept at least one positional argument")
|
|
491
|
+
|
|
492
|
+
if not callable(cfg.rollout):
|
|
493
|
+
raise ValueError("TaskAppConfig.rollout must be callable")
|
|
494
|
+
rollout_sig = callable_signature("rollout", cfg.rollout)
|
|
495
|
+
rollout_required = required_parameters(rollout_sig)
|
|
496
|
+
if len(rollout_required) < 2:
|
|
497
|
+
raise ValueError(
|
|
498
|
+
"rollout must accept at least two positional parameters (rollout_request, request)"
|
|
499
|
+
)
|
|
500
|
+
|
|
501
|
+
if cfg.dataset_registry is not None and not isinstance(
|
|
502
|
+
cfg.dataset_registry, TaskDatasetRegistry
|
|
503
|
+
):
|
|
504
|
+
raise TypeError("dataset_registry must be a TaskDatasetRegistry or None")
|
|
505
|
+
|
|
506
|
+
if cfg.rubrics is not None and not isinstance(cfg.rubrics, RubricBundle):
|
|
507
|
+
raise TypeError("rubrics must be a RubricBundle or None")
|
|
508
|
+
|
|
509
|
+
if cfg.proxy is not None and not isinstance(cfg.proxy, ProxyConfig):
|
|
510
|
+
raise TypeError("proxy must be a ProxyConfig or None")
|
|
511
|
+
|
|
512
|
+
if not isinstance(cfg.routers, Sequence):
|
|
513
|
+
raise TypeError("routers must be a sequence of fastapi.APIRouter instances")
|
|
514
|
+
for router in cfg.routers:
|
|
515
|
+
if not isinstance(router, APIRouter):
|
|
516
|
+
raise TypeError("routers must contain only fastapi.APIRouter instances")
|
|
517
|
+
|
|
518
|
+
if not isinstance(cfg.middleware, Sequence):
|
|
519
|
+
raise TypeError("middleware must be a sequence of starlette.middleware.Middleware")
|
|
520
|
+
for middleware in cfg.middleware:
|
|
521
|
+
if not isinstance(middleware, Middleware):
|
|
522
|
+
raise TypeError("middleware entries must be starlette.middleware.Middleware instances")
|
|
523
|
+
|
|
524
|
+
if not isinstance(cfg.app_state, MutableMapping):
|
|
525
|
+
raise TypeError("app_state must be a mutable mapping")
|
|
526
|
+
|
|
527
|
+
if not isinstance(cfg.require_api_key, bool):
|
|
528
|
+
raise TypeError("require_api_key must be a boolean")
|
|
529
|
+
if not isinstance(cfg.expose_debug_env, bool):
|
|
530
|
+
raise TypeError("expose_debug_env must be a boolean")
|
|
531
|
+
|
|
532
|
+
if cfg.cors_origins is not None:
|
|
533
|
+
if not isinstance(cfg.cors_origins, Sequence):
|
|
534
|
+
raise TypeError("cors_origins must be a sequence of strings")
|
|
535
|
+
for origin in cfg.cors_origins:
|
|
536
|
+
if not isinstance(origin, str):
|
|
537
|
+
raise TypeError("cors_origins must contain only strings")
|
|
538
|
+
|
|
539
|
+
for hook_name, hooks in (
|
|
540
|
+
("startup_hooks", cfg.startup_hooks),
|
|
541
|
+
("shutdown_hooks", cfg.shutdown_hooks),
|
|
542
|
+
):
|
|
543
|
+
if not isinstance(hooks, Sequence):
|
|
544
|
+
raise TypeError(f"{hook_name} must be a sequence of callables")
|
|
545
|
+
for hook in hooks:
|
|
546
|
+
if not callable(hook):
|
|
547
|
+
raise TypeError(f"Each entry in {hook_name} must be callable")
|
|
548
|
+
|
|
549
|
+
return cfg
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
def validate_task_app(
|
|
553
|
+
path: Path,
|
|
554
|
+
discovery: bool = False
|
|
555
|
+
) -> Path:
|
|
556
|
+
|
|
557
|
+
def print_pass():
|
|
558
|
+
ctx_print("Check passed", not discovery)
|
|
559
|
+
|
|
560
|
+
ctx_print("\nChecking if .py file", not discovery)
|
|
561
|
+
validate_file_type(path, ".py")
|
|
562
|
+
print_pass()
|
|
563
|
+
|
|
564
|
+
ctx_print("\nChecking if compiles", not discovery)
|
|
565
|
+
validate_py_file_compiles(path)
|
|
566
|
+
print_pass()
|
|
567
|
+
|
|
568
|
+
ctx_print("\nChecking if loads to module", not discovery)
|
|
569
|
+
with contextlib.redirect_stdout(io.StringIO()):
|
|
570
|
+
module = load_module(path)
|
|
571
|
+
print_pass()
|
|
572
|
+
|
|
573
|
+
ctx_print("\nChecking if is ASGI app", not discovery)
|
|
574
|
+
with contextlib.redirect_stdout(io.StringIO()):
|
|
575
|
+
app = get_asgi_app(module)
|
|
576
|
+
print_pass()
|
|
577
|
+
|
|
578
|
+
ctx_print("\nChecking if config structure is valid", not discovery)
|
|
579
|
+
with contextlib.redirect_stdout(io.StringIO()):
|
|
580
|
+
config_factory = getattr(module, "build_config", None)
|
|
581
|
+
if callable(config_factory):
|
|
582
|
+
config = config_factory()
|
|
583
|
+
if not isinstance(config, TaskAppConfig):
|
|
584
|
+
raise TypeError("build_config must return a TaskAppConfig instance")
|
|
585
|
+
validate_config_structure(config)
|
|
586
|
+
print_pass()
|
|
587
|
+
|
|
588
|
+
ctx_print("\nChecking if required routes exist", not discovery)
|
|
589
|
+
validate_required_routes_exist(app)
|
|
590
|
+
print_pass()
|
|
591
|
+
|
|
592
|
+
ctx_print("\nChecking if required route contracts exist", not discovery)
|
|
593
|
+
validate_route_contracts(app)
|
|
594
|
+
print_pass()
|
|
595
|
+
|
|
596
|
+
if discovery:
|
|
597
|
+
return path
|
|
598
|
+
|
|
599
|
+
ctx_print("Testing route contracts", not discovery)
|
|
600
|
+
test_route_contracts(app)
|
|
601
|
+
print_pass()
|
|
602
|
+
print('\n')
|
|
603
|
+
return path
|
|
604
|
+
|
|
605
|
+
|
|
606
|
+
def is_valid_task_app(
|
|
607
|
+
path: Path,
|
|
608
|
+
discovery: bool = False
|
|
609
|
+
) -> bool:
|
|
610
|
+
try:
|
|
611
|
+
validate_task_app(path, discovery)
|
|
612
|
+
except Exception:
|
|
613
|
+
return False
|
|
614
|
+
return True
|
|
615
|
+
|
|
616
|
+
|
|
617
|
+
def find_task_apps_in_cwd() -> list[tuple[Path, str]]:
|
|
618
|
+
from datetime import datetime
|
|
619
|
+
|
|
620
|
+
cwd = Path.cwd().resolve()
|
|
621
|
+
entries: list[tuple[Path, str, float]] = []
|
|
622
|
+
for path in cwd.rglob("*.py"):
|
|
623
|
+
if is_hidden_path(path, cwd):
|
|
624
|
+
continue
|
|
625
|
+
if not path.is_file():
|
|
626
|
+
continue
|
|
627
|
+
try:
|
|
628
|
+
validate_task_app(path, True)
|
|
629
|
+
except Exception:
|
|
630
|
+
continue
|
|
631
|
+
try:
|
|
632
|
+
rel_path = path.relative_to(cwd)
|
|
633
|
+
except ValueError:
|
|
634
|
+
rel_path = path
|
|
635
|
+
try:
|
|
636
|
+
mtime = path.stat().st_mtime
|
|
637
|
+
mtime_str = datetime.fromtimestamp(mtime).isoformat(sep=" ", timespec="seconds")
|
|
638
|
+
except OSError:
|
|
639
|
+
mtime = 0.0
|
|
640
|
+
mtime_str = ""
|
|
641
|
+
entries.append((rel_path, mtime_str, mtime))
|
|
642
|
+
entries.sort(key=lambda item: item[2], reverse=True)
|
|
643
|
+
return [(rel_path, mtime_str) for rel_path, mtime_str, _ in entries]
|
synth_ai/cli/lib/bin.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import shlex
|
|
2
|
+
import subprocess
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from .prompts import prompt_choice
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def install_bin(name: str, install_options: list[str]) -> bool:
|
|
9
|
+
cmd = prompt_choice(
|
|
10
|
+
f"How would you like to install {name}?",
|
|
11
|
+
install_options
|
|
12
|
+
)
|
|
13
|
+
div_start = f"{'-' * 29} INSTALL START {'-' * 29}"
|
|
14
|
+
div_end = f"{'-' * 30} INSTALL END {'-' * 30}"
|
|
15
|
+
try:
|
|
16
|
+
print(f"Installing {name} via `{cmd}`")
|
|
17
|
+
print('\n' + div_start)
|
|
18
|
+
subprocess.run(shlex.split(cmd), check=True)
|
|
19
|
+
print(div_end + '\n')
|
|
20
|
+
return True
|
|
21
|
+
except subprocess.CalledProcessError as e:
|
|
22
|
+
print(f"Failed to install {name}: {e}")
|
|
23
|
+
print(div_end + '\n')
|
|
24
|
+
return False
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def verify_bin(bin_path: Path) -> bool:
|
|
28
|
+
try:
|
|
29
|
+
result = subprocess.run(
|
|
30
|
+
[str(bin_path), "--version"],
|
|
31
|
+
capture_output=True,
|
|
32
|
+
text=True,
|
|
33
|
+
timeout=3,
|
|
34
|
+
check=False
|
|
35
|
+
)
|
|
36
|
+
return result.returncode == 0
|
|
37
|
+
except (OSError, subprocess.SubprocessError) as e:
|
|
38
|
+
print(e)
|
|
39
|
+
return False
|