synth-ai 0.2.8.dev2__py3-none-any.whl → 0.4.3__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.
- synth_ai/__init__.py +44 -24
- synth_ai/__main__.py +30 -3
- synth_ai/cli/__init__.py +103 -48
- 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/demo/__init__.py +3 -0
- synth_ai/cli/commands/demo/core.py +153 -0
- synth_ai/cli/commands/eval/__init__.py +10 -0
- synth_ai/cli/commands/eval/config.py +338 -0
- synth_ai/cli/commands/eval/core.py +256 -0
- synth_ai/cli/commands/eval/runner.py +704 -0
- synth_ai/cli/commands/eval/validation.py +60 -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 +1428 -0
- synth_ai/cli/commands/status/__init__.py +3 -0
- synth_ai/cli/commands/status/client.py +91 -0
- synth_ai/cli/commands/status/config.py +12 -0
- synth_ai/cli/commands/status/errors.py +11 -0
- synth_ai/cli/commands/status/subcommands/__init__.py +3 -0
- synth_ai/cli/commands/status/subcommands/config.py +13 -0
- synth_ai/cli/commands/status/subcommands/files.py +34 -0
- synth_ai/cli/commands/status/subcommands/jobs.py +51 -0
- synth_ai/cli/commands/status/subcommands/models.py +35 -0
- synth_ai/cli/commands/status/subcommands/runs.py +34 -0
- synth_ai/cli/commands/status/subcommands/session.py +77 -0
- synth_ai/cli/commands/status/subcommands/summary.py +39 -0
- synth_ai/cli/commands/status/subcommands/utils.py +41 -0
- synth_ai/cli/commands/status/utils.py +23 -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/{demos → cli/demo_apps}/core/cli.py +783 -441
- synth_ai/cli/demo_apps/crafter/__init__.py +1 -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/__init__.py +7 -0
- synth_ai/{demos → cli/demo_apps}/demo_task_apps/core.py +75 -37
- synth_ai/cli/demo_apps/demo_task_apps/crafter/__init__.py +1 -0
- synth_ai/cli/demo_apps/demo_task_apps/crafter/configs/crafter_fft_4b.toml +53 -0
- synth_ai/cli/demo_apps/demo_task_apps/crafter/configs/rl_from_base_qwen4b.toml +73 -0
- synth_ai/cli/demo_apps/demo_task_apps/crafter/grpo_crafter_task_app.py +185 -0
- synth_ai/{demos → cli/demo_apps}/demo_task_apps/math/_common.py +1 -2
- synth_ai/{demos → cli/demo_apps}/demo_task_apps/math/app.py +2 -1
- synth_ai/cli/demo_apps/demo_task_apps/math/config.toml +73 -0
- synth_ai/{demos → cli/demo_apps}/demo_task_apps/math/deploy_modal.py +3 -6
- synth_ai/cli/demo_apps/demo_task_apps/math/modal_task_app.py +738 -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 +75 -0
- synth_ai/cli/demo_apps/math/deploy_modal.py +54 -0
- synth_ai/cli/demo_apps/math/modal_task_app.py +698 -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 +922 -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/{balance.py → infra/balance.py} +16 -4
- 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 +642 -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 +150 -108
- synth_ai/cli/task_apps/__init__.py +37 -0
- synth_ai/cli/task_apps/commands.py +3145 -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/{watch.py → training/watch.py} +13 -18
- synth_ai/cli/turso.py +52 -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/{recent.py → utils/recent.py} +13 -7
- synth_ai/cli/{traces.py → utils/traces.py} +9 -5
- 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 +231 -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/{tracing_v3 → core/tracing_v3}/__init__.py +5 -1
- synth_ai/{tracing_v3 → core/tracing_v3}/abstractions.py +21 -4
- synth_ai/core/tracing_v3/config.py +229 -0
- synth_ai/core/tracing_v3/constants.py +21 -0
- synth_ai/{tracing_v3 → core/tracing_v3}/db_config.py +42 -29
- synth_ai/{tracing_v3 → core/tracing_v3}/decorators.py +80 -45
- synth_ai/{tracing_v3 → core/tracing_v3}/examples/basic_usage.py +15 -9
- synth_ai/{tracing_v3 → core/tracing_v3}/hooks.py +6 -4
- synth_ai/{tracing_v3 → core/tracing_v3}/llm_call_record_helpers.py +161 -61
- synth_ai/{tracing_v3 → core/tracing_v3}/migration_helper.py +1 -2
- synth_ai/{tracing_v3 → core/tracing_v3}/replica_sync.py +12 -7
- synth_ai/core/tracing_v3/serialization.py +130 -0
- synth_ai/{tracing_v3 → core/tracing_v3}/session_tracer.py +88 -21
- synth_ai/{tracing_v3 → core/tracing_v3}/storage/base.py +99 -12
- synth_ai/core/tracing_v3/storage/config.py +109 -0
- synth_ai/{tracing_v3 → core/tracing_v3}/storage/factory.py +11 -9
- synth_ai/{tracing_v3 → core/tracing_v3}/storage/utils.py +15 -11
- 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/{tracing_v3 → core/tracing_v3}/turso/models.py +7 -3
- synth_ai/core/tracing_v3/turso/native_manager.py +1385 -0
- synth_ai/{tracing_v3 → core/tracing_v3}/utils.py +5 -4
- 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 +83 -0
- synth_ai/data/enums.py +123 -0
- synth_ai/data/rewards.py +152 -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/products/graph_gepa/__init__.py +23 -0
- synth_ai/products/graph_gepa/converters/__init__.py +19 -0
- synth_ai/products/graph_gepa/converters/openai_sft.py +29 -0
- synth_ai/sdk/__init__.py +123 -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 +296 -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 +2199 -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 +187 -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/local_api.py +10 -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 +469 -0
- synth_ai/sdk/api/train/rl.py +441 -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 +351 -0
- synth_ai/sdk/api/train/utils.py +279 -0
- synth_ai/sdk/api/train/validators.py +2424 -0
- synth_ai/sdk/graphs/__init__.py +15 -0
- synth_ai/sdk/graphs/completions.py +570 -0
- synth_ai/{inference → sdk/inference}/__init__.py +0 -1
- 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 +14 -0
- synth_ai/sdk/judging/base.py +24 -0
- synth_ai/sdk/judging/client.py +40 -0
- synth_ai/sdk/judging/schemas.py +222 -0
- synth_ai/sdk/judging/types.py +42 -0
- synth_ai/sdk/learning/__init__.py +99 -0
- synth_ai/sdk/learning/algorithms.py +14 -0
- synth_ai/{learning → sdk/learning}/client.py +121 -30
- synth_ai/sdk/learning/config.py +5 -0
- synth_ai/{learning → sdk/learning}/constants.py +0 -2
- synth_ai/sdk/learning/context_learning_client.py +531 -0
- synth_ai/sdk/learning/context_learning_types.py +292 -0
- synth_ai/sdk/learning/ft_client.py +7 -0
- synth_ai/{learning → sdk/learning}/health.py +15 -9
- synth_ai/{learning → sdk/learning}/jobs.py +44 -47
- 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 +186 -0
- synth_ai/{rl → sdk/learning/rl}/__init__.py +13 -8
- synth_ai/{learning/rl_client.py → sdk/learning/rl/client.py} +89 -77
- synth_ai/sdk/learning/rl/config.py +31 -0
- synth_ai/{rl → sdk/learning/rl}/contracts.py +5 -14
- synth_ai/{rl → sdk/learning/rl}/env_keys.py +45 -16
- synth_ai/sdk/learning/rl/secrets.py +13 -0
- synth_ai/sdk/learning/rl_client.py +5 -0
- synth_ai/sdk/learning/sft/__init__.py +29 -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/sse.py +57 -0
- synth_ai/sdk/learning/validators.py +52 -0
- synth_ai/sdk/localapi/__init__.py +40 -0
- synth_ai/sdk/localapi/apps/__init__.py +28 -0
- synth_ai/sdk/localapi/client.py +10 -0
- synth_ai/sdk/localapi/contracts.py +10 -0
- synth_ai/sdk/localapi/helpers.py +519 -0
- synth_ai/sdk/localapi/rollouts.py +87 -0
- synth_ai/sdk/localapi/server.py +29 -0
- synth_ai/sdk/localapi/template.py +70 -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 +713 -0
- synth_ai/sdk/streaming/types.py +112 -0
- synth_ai/sdk/task/__init__.py +164 -0
- synth_ai/sdk/task/apps/__init__.py +169 -0
- synth_ai/sdk/task/auth.py +165 -0
- synth_ai/sdk/task/client.py +175 -0
- synth_ai/sdk/task/config.py +257 -0
- synth_ai/sdk/task/contracts.py +219 -0
- synth_ai/sdk/task/datasets.py +108 -0
- synth_ai/sdk/task/errors.py +50 -0
- synth_ai/sdk/task/health.py +34 -0
- synth_ai/sdk/task/in_process.py +1190 -0
- synth_ai/sdk/task/in_process_runner.py +314 -0
- synth_ai/sdk/task/inference_api.py +299 -0
- synth_ai/sdk/task/json.py +111 -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/rubrics.py +219 -0
- synth_ai/sdk/task/server.py +631 -0
- synth_ai/sdk/task/trace_correlation_helpers.py +539 -0
- synth_ai/sdk/task/tracing_utils.py +95 -0
- synth_ai/sdk/task/validators.py +441 -0
- synth_ai/sdk/task/vendors.py +59 -0
- synth_ai/sdk/training/__init__.py +102 -0
- synth_ai/sdk/tunnels/__init__.py +83 -0
- synth_ai/sdk/tunnels/cleanup.py +83 -0
- synth_ai/sdk/tunnels/ports.py +120 -0
- synth_ai/utils/__init__.py +213 -0
- synth_ai-0.4.3.dist-info/METADATA +262 -0
- synth_ai-0.4.3.dist-info/RECORD +370 -0
- {synth_ai-0.2.8.dev2.dist-info → synth_ai-0.4.3.dist-info}/entry_points.txt +0 -1
- synth_ai/cli/calc.py +0 -69
- synth_ai/cli/demo.py +0 -144
- synth_ai/cli/legacy_root_backup.py +0 -470
- synth_ai/cli/man.py +0 -106
- synth_ai/cli/rl_demo.py +0 -202
- synth_ai/cli/status.py +0 -133
- synth_ai/config/base_url.py +0 -107
- synth_ai/core/experiment.py +0 -15
- synth_ai/core/system.py +0 -15
- synth_ai/demos/core/__init__.py +0 -1
- synth_ai/demos/demo_task_apps/__init__.py +0 -1
- synth_ai/demos/demo_task_apps/math/config.toml +0 -129
- 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 -415
- 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 -294
- 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/filter_traces_sft_turso.py +0 -738
- 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/filter_traces_sft_turso.py +0 -580
- 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 -404
- 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/sokoban/units/astar_common.py +0 -95
- 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 -364
- synth_ai/environments/service/app.py +0 -98
- synth_ai/environments/service/core_routes.py +0 -1020
- 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 -80
- synth_ai/environments/tasks/filters.py +0 -41
- synth_ai/environments/tasks/utils.py +0 -91
- synth_ai/environments/v0_observability/history.py +0 -3
- synth_ai/environments/v0_observability/log.py +0 -2
- synth_ai/evals/base.py +0 -15
- synth_ai/experimental/synth_oss.py +0 -446
- synth_ai/handshake.py +0 -63
- synth_ai/http.py +0 -26
- synth_ai/http_client.py +0 -104
- synth_ai/inference/client.py +0 -20
- synth_ai/install_sqld.sh +0 -40
- synth_ai/jobs/client.py +0 -246
- synth_ai/learning/__init__.py +0 -24
- synth_ai/learning/config.py +0 -43
- synth_ai/learning/filtering.py +0 -0
- synth_ai/learning/ft_client.py +0 -59
- 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 -213
- synth_ai/learning/prompts/mipro.py +0 -289
- synth_ai/learning/prompts/random_search.py +0 -246
- synth_ai/learning/prompts/run_mipro_banking77.py +0 -172
- synth_ai/learning/prompts/run_random_search_banking77.py +0 -324
- synth_ai/learning/sse.py +0 -58
- synth_ai/learning/validators.py +0 -48
- synth_ai/lm/__init__.py +0 -51
- synth_ai/lm/caching/constants.py +0 -6
- synth_ai/lm/caching/dbs.py +0 -0
- synth_ai/lm/caching/ephemeral.py +0 -102
- synth_ai/lm/caching/handler.py +0 -137
- synth_ai/lm/caching/initialize.py +0 -11
- synth_ai/lm/caching/persistent.py +0 -114
- synth_ai/lm/config.py +0 -110
- synth_ai/lm/constants.py +0 -32
- synth_ai/lm/core/__init__.py +0 -8
- synth_ai/lm/core/all.py +0 -73
- synth_ai/lm/core/exceptions.py +0 -7
- synth_ai/lm/core/main.py +0 -319
- synth_ai/lm/core/main_v3.py +0 -594
- synth_ai/lm/core/synth_models.py +0 -48
- synth_ai/lm/core/vendor_clients.py +0 -188
- synth_ai/lm/cost/__init__.py +0 -0
- synth_ai/lm/cost/monitor.py +0 -1
- synth_ai/lm/cost/statefulness.py +0 -1
- synth_ai/lm/injection.py +0 -80
- synth_ai/lm/overrides.py +0 -206
- synth_ai/lm/provider_support/__init__.py +0 -8
- synth_ai/lm/provider_support/anthropic.py +0 -972
- synth_ai/lm/provider_support/openai.py +0 -1139
- synth_ai/lm/provider_support/suppress_logging.py +0 -31
- synth_ai/lm/structured_outputs/__init__.py +0 -0
- synth_ai/lm/structured_outputs/handler.py +0 -440
- synth_ai/lm/structured_outputs/inject.py +0 -297
- synth_ai/lm/structured_outputs/rehabilitate.py +0 -185
- synth_ai/lm/tools/__init__.py +0 -3
- synth_ai/lm/tools/base.py +0 -172
- synth_ai/lm/unified_interface.py +0 -202
- synth_ai/lm/vendors/__init__.py +0 -0
- synth_ai/lm/vendors/base.py +0 -81
- synth_ai/lm/vendors/core/__init__.py +0 -0
- synth_ai/lm/vendors/core/anthropic_api.py +0 -387
- synth_ai/lm/vendors/core/gemini_api.py +0 -292
- synth_ai/lm/vendors/core/mistral_api.py +0 -322
- synth_ai/lm/vendors/core/openai_api.py +0 -225
- synth_ai/lm/vendors/core/synth_dev_api.py +0 -0
- synth_ai/lm/vendors/local/__init__.py +0 -0
- synth_ai/lm/vendors/local/ollama.py +0 -0
- synth_ai/lm/vendors/openai_standard.py +0 -780
- synth_ai/lm/vendors/openai_standard_responses.py +0 -256
- synth_ai/lm/vendors/retries.py +0 -22
- synth_ai/lm/vendors/supported/__init__.py +0 -0
- synth_ai/lm/vendors/supported/custom_endpoint.py +0 -417
- synth_ai/lm/vendors/supported/deepseek.py +0 -69
- synth_ai/lm/vendors/supported/grok.py +0 -75
- synth_ai/lm/vendors/supported/groq.py +0 -16
- synth_ai/lm/vendors/supported/ollama.py +0 -15
- synth_ai/lm/vendors/supported/openrouter.py +0 -74
- synth_ai/lm/vendors/supported/together.py +0 -11
- synth_ai/lm/vendors/synth_client.py +0 -808
- synth_ai/lm/warmup.py +0 -186
- synth_ai/rl/secrets.py +0 -19
- synth_ai/scripts/verify_rewards.py +0 -100
- synth_ai/task/__init__.py +0 -10
- synth_ai/task/contracts.py +0 -120
- synth_ai/task/health.py +0 -28
- synth_ai/task/validators.py +0 -12
- synth_ai/tracing/__init__.py +0 -30
- synth_ai/tracing_v1/__init__.py +0 -33
- synth_ai/tracing_v3/config.py +0 -84
- synth_ai/tracing_v3/storage/config.py +0 -62
- synth_ai/tracing_v3/turso/__init__.py +0 -25
- synth_ai/tracing_v3/turso/daemon.py +0 -144
- synth_ai/tracing_v3/turso/manager.py +0 -760
- 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 -512
- 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/zyk/__init__.py +0 -30
- synth_ai-0.2.8.dev2.dist-info/METADATA +0 -129
- synth_ai-0.2.8.dev2.dist-info/RECORD +0 -420
- /synth_ai/{demos → cli/demo_apps}/demo_task_apps/math/__init__.py +0 -0
- /synth_ai/{lm/caching → core/apps}/__init__.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}/core.py +0 -0
- /synth_ai/{learning → sdk/learning}/gateway.py +0 -0
- {synth_ai-0.2.8.dev2.dist-info → synth_ai-0.4.3.dist-info}/WHEEL +0 -0
- {synth_ai-0.2.8.dev2.dist-info → synth_ai-0.4.3.dist-info}/licenses/LICENSE +0 -0
- {synth_ai-0.2.8.dev2.dist-info → synth_ai-0.4.3.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
"""Main scan command implementation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
import click
|
|
11
|
+
|
|
12
|
+
from synth_ai.cli.commands.scan.cloudflare_scanner import scan_cloudflare_apps
|
|
13
|
+
from synth_ai.cli.commands.scan.local_scanner import scan_local_ports, scan_registry
|
|
14
|
+
from synth_ai.cli.commands.scan.models import ScannedApp
|
|
15
|
+
from synth_ai.cli.lib.env import resolve_env_var
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def format_app_table(apps: list[ScannedApp]) -> str:
|
|
19
|
+
"""Format apps as a human-readable table."""
|
|
20
|
+
if not apps:
|
|
21
|
+
return "No active task apps found."
|
|
22
|
+
|
|
23
|
+
# Calculate column widths
|
|
24
|
+
name_width = max(len(app.name) for app in apps if app.name) if apps else 4
|
|
25
|
+
port_width = 5 # "Port" header
|
|
26
|
+
status_width = 10 # Status with icon
|
|
27
|
+
type_width = max(len(app.type) for app in apps) if apps else 4
|
|
28
|
+
via_width = max(len(app.discovered_via) for app in apps) if apps else 13
|
|
29
|
+
|
|
30
|
+
# Additional metadata columns
|
|
31
|
+
app_id_width = max(len(app.app_id or "") for app in apps) if apps else 0
|
|
32
|
+
app_id_width = max(app_id_width, 7) # "App ID" header
|
|
33
|
+
|
|
34
|
+
version_width = max(len(app.version or "") for app in apps) if apps else 0
|
|
35
|
+
version_width = max(version_width, 7) # "Version" header
|
|
36
|
+
|
|
37
|
+
# Ensure minimum widths
|
|
38
|
+
name_width = max(name_width, 4)
|
|
39
|
+
type_width = max(type_width, 4)
|
|
40
|
+
via_width = max(via_width, 13)
|
|
41
|
+
|
|
42
|
+
lines = [f"Found {len(apps)} active task app{'s' if len(apps) != 1 else ''}:\n"]
|
|
43
|
+
|
|
44
|
+
# Header row
|
|
45
|
+
header_parts = [
|
|
46
|
+
f"{'Name':<{name_width}}",
|
|
47
|
+
f"{'Port':<{port_width}}",
|
|
48
|
+
f"{'Status':<{status_width}}",
|
|
49
|
+
f"{'Type':<{type_width}}",
|
|
50
|
+
]
|
|
51
|
+
if app_id_width > 0:
|
|
52
|
+
header_parts.append(f"{'App ID':<{app_id_width}}")
|
|
53
|
+
if version_width > 0:
|
|
54
|
+
header_parts.append(f"{'Version':<{version_width}}")
|
|
55
|
+
header_parts.append(f"{'Discovered Via':<{via_width}}")
|
|
56
|
+
|
|
57
|
+
lines.append(" ".join(header_parts))
|
|
58
|
+
|
|
59
|
+
# Separator
|
|
60
|
+
total_width = sum(len(p) for p in header_parts) + len(header_parts) - 1
|
|
61
|
+
lines.append("─" * total_width)
|
|
62
|
+
|
|
63
|
+
for app in apps:
|
|
64
|
+
status_icon = "✅" if app.health_status == "healthy" else "⚠️ " if app.health_status == "unhealthy" else "❓"
|
|
65
|
+
status_display = f"{status_icon} {app.health_status}"
|
|
66
|
+
|
|
67
|
+
port_str = str(app.port) if app.port else "-"
|
|
68
|
+
|
|
69
|
+
row_parts = [
|
|
70
|
+
f"{app.name:<{name_width}}",
|
|
71
|
+
f"{port_str:<{port_width}}",
|
|
72
|
+
f"{status_display:<{status_width}}",
|
|
73
|
+
f"{app.type:<{type_width}}",
|
|
74
|
+
]
|
|
75
|
+
if app_id_width > 0:
|
|
76
|
+
row_parts.append(f"{(app.app_id or '-'):<{app_id_width}}")
|
|
77
|
+
if version_width > 0:
|
|
78
|
+
row_parts.append(f"{(app.version or '-'):<{version_width}}")
|
|
79
|
+
row_parts.append(f"{app.discovered_via:<{via_width}}")
|
|
80
|
+
|
|
81
|
+
lines.append(" ".join(row_parts))
|
|
82
|
+
|
|
83
|
+
return "\n".join(lines)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def format_app_json(apps: list[ScannedApp]) -> str:
|
|
87
|
+
"""Format apps as JSON."""
|
|
88
|
+
apps_data = []
|
|
89
|
+
for app in apps:
|
|
90
|
+
apps_data.append(
|
|
91
|
+
{
|
|
92
|
+
"name": app.name,
|
|
93
|
+
"url": app.url,
|
|
94
|
+
"type": app.type,
|
|
95
|
+
"health_status": app.health_status,
|
|
96
|
+
"port": app.port,
|
|
97
|
+
"tunnel_mode": app.tunnel_mode,
|
|
98
|
+
"tunnel_hostname": app.tunnel_hostname,
|
|
99
|
+
"app_id": app.app_id,
|
|
100
|
+
"task_name": app.task_name,
|
|
101
|
+
"dataset_id": app.dataset_id,
|
|
102
|
+
"version": app.version,
|
|
103
|
+
"metadata": app.metadata,
|
|
104
|
+
"discovered_via": app.discovered_via,
|
|
105
|
+
}
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
summary = {
|
|
109
|
+
"total_found": len(apps),
|
|
110
|
+
"healthy": sum(1 for app in apps if app.health_status == "healthy"),
|
|
111
|
+
"unhealthy": sum(1 for app in apps if app.health_status == "unhealthy"),
|
|
112
|
+
"local_count": sum(1 for app in apps if app.type == "local"),
|
|
113
|
+
"cloudflare_count": sum(1 for app in apps if app.type == "cloudflare"),
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return json.dumps({"apps": apps_data, "scan_summary": summary}, indent=2)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
async def run_scan(
|
|
120
|
+
port_range: tuple[int, int],
|
|
121
|
+
timeout: float,
|
|
122
|
+
api_key: str | None,
|
|
123
|
+
env_file: Path | None,
|
|
124
|
+
verbose: bool,
|
|
125
|
+
) -> list[ScannedApp]:
|
|
126
|
+
"""Run the scan operation.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
port_range: Tuple of (start_port, end_port)
|
|
130
|
+
timeout: Health check timeout
|
|
131
|
+
api_key: API key for health checks
|
|
132
|
+
env_file: Specific .env file to check
|
|
133
|
+
verbose: Show detailed progress
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
List of discovered apps
|
|
137
|
+
"""
|
|
138
|
+
start_port, end_port = port_range
|
|
139
|
+
all_apps: list[ScannedApp] = []
|
|
140
|
+
|
|
141
|
+
# Resolve API key
|
|
142
|
+
env_api_key = api_key
|
|
143
|
+
if not env_api_key:
|
|
144
|
+
try:
|
|
145
|
+
env_api_key = resolve_env_var("ENVIRONMENT_API_KEY")
|
|
146
|
+
except Exception:
|
|
147
|
+
env_api_key = os.getenv("ENVIRONMENT_API_KEY")
|
|
148
|
+
|
|
149
|
+
if verbose:
|
|
150
|
+
click.echo(f"Scanning ports {start_port}-{end_port}...", err=True)
|
|
151
|
+
|
|
152
|
+
# Scan local ports
|
|
153
|
+
local_apps = await scan_local_ports(start_port, end_port, env_api_key, timeout)
|
|
154
|
+
all_apps.extend(local_apps)
|
|
155
|
+
|
|
156
|
+
if verbose:
|
|
157
|
+
click.echo(f"Found {len(local_apps)} local app(s)", err=True)
|
|
158
|
+
|
|
159
|
+
# Scan Cloudflare apps
|
|
160
|
+
synth_api_key = os.getenv("SYNTH_API_KEY")
|
|
161
|
+
cloudflare_apps = await scan_cloudflare_apps(synth_api_key, env_api_key, env_file, timeout)
|
|
162
|
+
all_apps.extend(cloudflare_apps)
|
|
163
|
+
|
|
164
|
+
if verbose:
|
|
165
|
+
click.echo(f"Found {len(cloudflare_apps)} Cloudflare app(s)", err=True)
|
|
166
|
+
|
|
167
|
+
# Scan service records (local services deployed via synth-ai)
|
|
168
|
+
from synth_ai.cli.commands.scan.local_scanner import scan_service_records
|
|
169
|
+
service_record_apps = await scan_service_records(env_api_key, timeout)
|
|
170
|
+
all_apps.extend(service_record_apps)
|
|
171
|
+
|
|
172
|
+
if verbose:
|
|
173
|
+
click.echo(f"Found {len(service_record_apps)} service record(s)", err=True)
|
|
174
|
+
|
|
175
|
+
# Scan registry (for reference, but these don't have URLs)
|
|
176
|
+
registry_apps = scan_registry()
|
|
177
|
+
# Only add registry apps that weren't already discovered
|
|
178
|
+
discovered_app_ids = {app.app_id for app in all_apps if app.app_id}
|
|
179
|
+
new_registry_apps = [app for app in registry_apps if app.app_id and app.app_id not in discovered_app_ids]
|
|
180
|
+
all_apps.extend(new_registry_apps)
|
|
181
|
+
|
|
182
|
+
if verbose:
|
|
183
|
+
click.echo(f"Found {len(new_registry_apps)} registry app(s) not yet running", err=True)
|
|
184
|
+
|
|
185
|
+
# Deduplicate apps by URL (prefer better sources and metadata)
|
|
186
|
+
seen_urls: dict[str, ScannedApp] = {}
|
|
187
|
+
for app in all_apps:
|
|
188
|
+
if app.url and app.url in seen_urls:
|
|
189
|
+
# Prefer apps with better discovery method and metadata
|
|
190
|
+
existing = seen_urls[app.url]
|
|
191
|
+
|
|
192
|
+
# Priority order for discovery methods:
|
|
193
|
+
# 1. service_records / tunnel_records (have metadata like app_id, task_app_path)
|
|
194
|
+
# 2. cloudflared_process / backend_api (tunnels)
|
|
195
|
+
# 3. port_scan (basic discovery)
|
|
196
|
+
# 4. registry (no URL)
|
|
197
|
+
|
|
198
|
+
priority_order = {
|
|
199
|
+
"service_records": 1,
|
|
200
|
+
"tunnel_records": 1,
|
|
201
|
+
"backend_api": 2,
|
|
202
|
+
"cloudflared_process": 2,
|
|
203
|
+
"port_scan": 3,
|
|
204
|
+
"registry": 4,
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
app_priority = priority_order.get(app.discovered_via, 99)
|
|
208
|
+
existing_priority = priority_order.get(existing.discovered_via, 99)
|
|
209
|
+
|
|
210
|
+
# Prefer higher priority discovery method
|
|
211
|
+
if app_priority < existing_priority:
|
|
212
|
+
seen_urls[app.url] = app
|
|
213
|
+
elif app_priority == existing_priority:
|
|
214
|
+
# Same priority - prefer better health status or better metadata
|
|
215
|
+
if app.health_status != "unknown" and existing.health_status == "unknown":
|
|
216
|
+
seen_urls[app.url] = app
|
|
217
|
+
elif app.app_id and not existing.app_id:
|
|
218
|
+
# Prefer app with app_id
|
|
219
|
+
seen_urls[app.url] = app
|
|
220
|
+
elif app.discovered_via != "registry" and existing.discovered_via == "registry":
|
|
221
|
+
seen_urls[app.url] = app
|
|
222
|
+
elif app.url:
|
|
223
|
+
seen_urls[app.url] = app
|
|
224
|
+
elif app.discovered_via == "registry":
|
|
225
|
+
# Registry apps without URLs - add them separately
|
|
226
|
+
seen_urls[f"registry:{app.app_id}"] = app
|
|
227
|
+
|
|
228
|
+
return list(seen_urls.values())
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
@click.command("scan")
|
|
232
|
+
@click.option(
|
|
233
|
+
"--port-range",
|
|
234
|
+
type=str,
|
|
235
|
+
default="8000:8100",
|
|
236
|
+
help="Port range to scan for local apps (format: START:END)",
|
|
237
|
+
)
|
|
238
|
+
@click.option(
|
|
239
|
+
"--timeout",
|
|
240
|
+
type=float,
|
|
241
|
+
default=2.0,
|
|
242
|
+
show_default=True,
|
|
243
|
+
help="Health check timeout in seconds",
|
|
244
|
+
)
|
|
245
|
+
@click.option(
|
|
246
|
+
"--api-key",
|
|
247
|
+
type=str,
|
|
248
|
+
default=None,
|
|
249
|
+
envvar="ENVIRONMENT_API_KEY",
|
|
250
|
+
help="API key for health checks (default: from ENVIRONMENT_API_KEY env var)",
|
|
251
|
+
)
|
|
252
|
+
@click.option("--json", "output_json", is_flag=True, help="Output results as JSON")
|
|
253
|
+
@click.option("--verbose", is_flag=True, help="Show detailed scanning progress")
|
|
254
|
+
@click.option(
|
|
255
|
+
"--env-file",
|
|
256
|
+
type=click.Path(exists=True, dir_okay=False, path_type=Path),
|
|
257
|
+
default=None,
|
|
258
|
+
help="(Deprecated) Not used - tunnels are discovered from running processes",
|
|
259
|
+
hidden=True,
|
|
260
|
+
)
|
|
261
|
+
def scan_command(
|
|
262
|
+
port_range: str,
|
|
263
|
+
timeout: float,
|
|
264
|
+
api_key: str | None,
|
|
265
|
+
output_json: bool,
|
|
266
|
+
verbose: bool,
|
|
267
|
+
env_file: Path | None,
|
|
268
|
+
) -> None:
|
|
269
|
+
"""Scan for active Cloudflare and local task apps.
|
|
270
|
+
|
|
271
|
+
Discovers and performs health checks on running task applications deployed
|
|
272
|
+
locally or via Cloudflare tunnels. Returns structured information in table
|
|
273
|
+
or JSON format, suitable for terminal use or programmatic consumption by
|
|
274
|
+
CLI agents and automation tools.
|
|
275
|
+
|
|
276
|
+
Discovery Methods:
|
|
277
|
+
- Port scanning: Scans specified port range for local HTTP servers
|
|
278
|
+
- Service records: Reads deployed local services from persistent records
|
|
279
|
+
- Tunnel records: Reads deployed Cloudflare tunnels from persistent records
|
|
280
|
+
- Process scanning: Inspects running cloudflared processes for tunnel URLs
|
|
281
|
+
- Backend API: Queries backend for managed tunnel information
|
|
282
|
+
- Registry: Checks task app registry for registered apps
|
|
283
|
+
|
|
284
|
+
Health Checks:
|
|
285
|
+
- Performs HTTP GET requests to /health endpoints
|
|
286
|
+
- Extracts metadata from /info endpoints (app_id, version, task_name, etc.)
|
|
287
|
+
- Supports API key authentication via X-API-Key header
|
|
288
|
+
|
|
289
|
+
Output Formats:
|
|
290
|
+
- Table (default): Human-readable table with columns for name, port, status,
|
|
291
|
+
type, app ID, version, and discovery method
|
|
292
|
+
- JSON (--json): Machine-readable JSON with full metadata and scan summary
|
|
293
|
+
|
|
294
|
+
Examples:
|
|
295
|
+
# Scan default port range (8000-8100) and show table
|
|
296
|
+
$ synth-ai scan
|
|
297
|
+
|
|
298
|
+
# Scan specific port range with verbose output
|
|
299
|
+
$ synth-ai scan --port-range 8000:9000 --verbose
|
|
300
|
+
|
|
301
|
+
# Get JSON output for programmatic use
|
|
302
|
+
$ synth-ai scan --json
|
|
303
|
+
|
|
304
|
+
# Use custom API key and timeout
|
|
305
|
+
$ synth-ai scan --api-key YOUR_KEY --timeout 5.0
|
|
306
|
+
|
|
307
|
+
Args:
|
|
308
|
+
port_range: Port range to scan (format: START:END, e.g., "8000:8100")
|
|
309
|
+
timeout: Health check timeout in seconds (default: 2.0)
|
|
310
|
+
api_key: API key for health checks (default: from ENVIRONMENT_API_KEY env var)
|
|
311
|
+
output_json: Output results as JSON instead of table
|
|
312
|
+
verbose: Show detailed scanning progress
|
|
313
|
+
env_file: (Deprecated) Not used - tunnels are discovered from processes
|
|
314
|
+
|
|
315
|
+
Returns:
|
|
316
|
+
None (outputs to stdout)
|
|
317
|
+
|
|
318
|
+
Raises:
|
|
319
|
+
click.BadParameter: If port range format is invalid or out of valid range
|
|
320
|
+
"""
|
|
321
|
+
# Parse port range
|
|
322
|
+
try:
|
|
323
|
+
if ":" in port_range:
|
|
324
|
+
start_str, end_str = port_range.split(":", 1)
|
|
325
|
+
start_port = int(start_str.strip())
|
|
326
|
+
end_port = int(end_str.strip())
|
|
327
|
+
else:
|
|
328
|
+
start_port = int(port_range.strip())
|
|
329
|
+
end_port = start_port
|
|
330
|
+
except ValueError as e:
|
|
331
|
+
raise click.BadParameter(f"Invalid port range format: {port_range}. Use START:END (e.g., 8000:8100)") from e
|
|
332
|
+
|
|
333
|
+
if start_port < 1 or end_port > 65535 or start_port > end_port:
|
|
334
|
+
raise click.BadParameter(f"Invalid port range: {start_port}-{end_port}")
|
|
335
|
+
|
|
336
|
+
# Run scan
|
|
337
|
+
apps = asyncio.run(run_scan((start_port, end_port), timeout, api_key, env_file, verbose))
|
|
338
|
+
|
|
339
|
+
# Output results
|
|
340
|
+
if output_json:
|
|
341
|
+
click.echo(format_app_json(apps))
|
|
342
|
+
else:
|
|
343
|
+
click.echo(format_app_table(apps))
|
|
344
|
+
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"""Health check utilities for scan command."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from typing import Any, Literal
|
|
7
|
+
|
|
8
|
+
import httpx
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
async def check_app_health(
|
|
12
|
+
url: str,
|
|
13
|
+
api_key: str | None,
|
|
14
|
+
timeout: float = 2.0,
|
|
15
|
+
) -> tuple[Literal["healthy", "unhealthy", "unknown"], dict[str, Any]]:
|
|
16
|
+
"""Check health and fetch metadata from a task app.
|
|
17
|
+
|
|
18
|
+
Performs HTTP health checks on a task app by:
|
|
19
|
+
1. Checking the /health endpoint for health status
|
|
20
|
+
2. Fetching the /info endpoint for metadata (app_id, version, etc.)
|
|
21
|
+
|
|
22
|
+
Health Status Determination:
|
|
23
|
+
- "healthy": HTTP 200 with valid JSON response containing "status": "healthy"
|
|
24
|
+
or "healthy": true, or HTTP 200 with any valid JSON
|
|
25
|
+
- "unhealthy": HTTP error status (4xx, 5xx), Cloudflare tunnel errors (530, 502),
|
|
26
|
+
or HTML responses (Cloudflare error pages)
|
|
27
|
+
- "unknown": Request timeout, connection errors, or other exceptions
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
url: Base URL of the task app (e.g., "http://localhost:8000" or
|
|
31
|
+
"https://abc123.trycloudflare.com"). Trailing slashes are handled.
|
|
32
|
+
api_key: API key for authentication via X-API-Key header. If None, requests
|
|
33
|
+
are made without authentication (may fail for apps requiring auth).
|
|
34
|
+
timeout: Request timeout in seconds for both /health and /info endpoints.
|
|
35
|
+
Default is 2.0 seconds.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Tuple of (health_status, metadata_dict) where:
|
|
39
|
+
- health_status: One of "healthy", "unhealthy", or "unknown"
|
|
40
|
+
- metadata_dict: Dictionary containing:
|
|
41
|
+
- Full /info endpoint response (if available)
|
|
42
|
+
- Error information (if health check failed)
|
|
43
|
+
- HTTP status codes and error messages
|
|
44
|
+
|
|
45
|
+
Examples:
|
|
46
|
+
>>> import asyncio
|
|
47
|
+
>>> status, metadata = asyncio.run(check_app_health("http://localhost:8000", "key123"))
|
|
48
|
+
>>> print(status) # "healthy", "unhealthy", or "unknown"
|
|
49
|
+
>>> print(metadata.get("service", {}).get("task", {}).get("id")) # app_id
|
|
50
|
+
|
|
51
|
+
Raises:
|
|
52
|
+
No exceptions are raised - all errors are captured and returned as "unknown"
|
|
53
|
+
status with error details in metadata.
|
|
54
|
+
"""
|
|
55
|
+
headers: dict[str, str] = {}
|
|
56
|
+
if api_key:
|
|
57
|
+
headers["X-API-Key"] = api_key
|
|
58
|
+
|
|
59
|
+
metadata: dict[str, Any] = {}
|
|
60
|
+
|
|
61
|
+
# Check /health endpoint
|
|
62
|
+
health_status: Literal["healthy", "unhealthy", "unknown"] = "unknown"
|
|
63
|
+
try:
|
|
64
|
+
async with httpx.AsyncClient(timeout=timeout, follow_redirects=True) as client:
|
|
65
|
+
health_url = f"{url.rstrip('/')}/health"
|
|
66
|
+
health_resp = await client.get(health_url, headers=headers)
|
|
67
|
+
|
|
68
|
+
# Check for Cloudflare tunnel errors (530, 502, etc.) or HTML responses
|
|
69
|
+
if health_resp.status_code in (530, 502, 503, 504):
|
|
70
|
+
# Cloudflare error - tunnel pointing to dead server
|
|
71
|
+
health_status = "unhealthy"
|
|
72
|
+
metadata["tunnel_error"] = f"HTTP {health_resp.status_code}"
|
|
73
|
+
elif "text/html" in health_resp.headers.get("content-type", "").lower():
|
|
74
|
+
# HTML response (Cloudflare error page) - tunnel not working
|
|
75
|
+
health_status = "unhealthy"
|
|
76
|
+
metadata["tunnel_error"] = "HTML response (tunnel error)"
|
|
77
|
+
elif health_resp.status_code == 200:
|
|
78
|
+
try:
|
|
79
|
+
health_data = health_resp.json()
|
|
80
|
+
if isinstance(health_data, dict):
|
|
81
|
+
# Check for "status": "healthy" or "healthy": true
|
|
82
|
+
status = health_data.get("status", "").lower()
|
|
83
|
+
healthy_flag = health_data.get("healthy")
|
|
84
|
+
if status == "healthy" or healthy_flag is True:
|
|
85
|
+
health_status = "healthy"
|
|
86
|
+
elif status == "unhealthy" or healthy_flag is False:
|
|
87
|
+
health_status = "unhealthy"
|
|
88
|
+
else:
|
|
89
|
+
# If status is 200 but no clear health indicator, consider healthy
|
|
90
|
+
health_status = "healthy"
|
|
91
|
+
else:
|
|
92
|
+
# If JSON parsing fails but status is 200, consider it healthy
|
|
93
|
+
health_status = "healthy"
|
|
94
|
+
except Exception:
|
|
95
|
+
# If JSON parsing fails but status is 200, consider it healthy
|
|
96
|
+
health_status = "healthy"
|
|
97
|
+
else:
|
|
98
|
+
health_status = "unhealthy"
|
|
99
|
+
metadata["http_status"] = health_resp.status_code
|
|
100
|
+
except httpx.TimeoutException:
|
|
101
|
+
health_status = "unknown"
|
|
102
|
+
metadata["error"] = "timeout"
|
|
103
|
+
except Exception as exc:
|
|
104
|
+
health_status = "unknown"
|
|
105
|
+
metadata["error"] = str(exc)
|
|
106
|
+
|
|
107
|
+
# Fetch /info endpoint for metadata
|
|
108
|
+
try:
|
|
109
|
+
async with httpx.AsyncClient(timeout=timeout) as client:
|
|
110
|
+
info_url = f"{url.rstrip('/')}/info"
|
|
111
|
+
info_resp = await client.get(info_url, headers=headers)
|
|
112
|
+
if info_resp.status_code == 200:
|
|
113
|
+
try:
|
|
114
|
+
info_data = info_resp.json()
|
|
115
|
+
if isinstance(info_data, dict):
|
|
116
|
+
metadata.update(info_data)
|
|
117
|
+
except Exception:
|
|
118
|
+
pass
|
|
119
|
+
except Exception:
|
|
120
|
+
pass
|
|
121
|
+
|
|
122
|
+
return health_status, metadata
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def extract_app_info(metadata: dict[str, Any]) -> tuple[str | None, str | None, str | None, str | None]:
|
|
126
|
+
"""Extract app information from /info endpoint metadata.
|
|
127
|
+
|
|
128
|
+
Parses the metadata dictionary (typically from /info endpoint response) to
|
|
129
|
+
extract key application identifiers and version information. Handles missing
|
|
130
|
+
or malformed data gracefully by returning None for unavailable fields.
|
|
131
|
+
|
|
132
|
+
Metadata Structure Expected:
|
|
133
|
+
{
|
|
134
|
+
"service": {
|
|
135
|
+
"task": {
|
|
136
|
+
"id": "app_id",
|
|
137
|
+
"name": "Task Name",
|
|
138
|
+
"version": "1.0.0"
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
"dataset": {
|
|
142
|
+
"id": "dataset_id"
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
metadata: Dictionary containing /info endpoint response or service records.
|
|
148
|
+
May contain nested "service" and "dataset" dictionaries.
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
Tuple of (app_id, task_name, dataset_id, version) where:
|
|
152
|
+
- app_id: Task app identifier from service.task.id
|
|
153
|
+
- task_name: Human-readable task name from service.task.name
|
|
154
|
+
- dataset_id: Dataset identifier from dataset.id (if available)
|
|
155
|
+
- version: App version from service.task.version
|
|
156
|
+
|
|
157
|
+
Examples:
|
|
158
|
+
>>> metadata = {
|
|
159
|
+
... "service": {"task": {"id": "banking77", "name": "Banking77", "version": "1.0.0"}},
|
|
160
|
+
... "dataset": {"id": "banking77_dataset"}
|
|
161
|
+
... }
|
|
162
|
+
>>> app_id, task_name, dataset_id, version = extract_app_info(metadata)
|
|
163
|
+
>>> print(app_id) # "banking77"
|
|
164
|
+
>>> print(version) # "1.0.0"
|
|
165
|
+
"""
|
|
166
|
+
app_id: str | None = None
|
|
167
|
+
task_name: str | None = None
|
|
168
|
+
dataset_id: str | None = None
|
|
169
|
+
version: str | None = None
|
|
170
|
+
|
|
171
|
+
try:
|
|
172
|
+
service = metadata.get("service", {})
|
|
173
|
+
if isinstance(service, dict):
|
|
174
|
+
task = service.get("task", {})
|
|
175
|
+
if isinstance(task, dict):
|
|
176
|
+
app_id = task.get("id")
|
|
177
|
+
task_name = task.get("name")
|
|
178
|
+
version = task.get("version")
|
|
179
|
+
|
|
180
|
+
dataset = metadata.get("dataset", {})
|
|
181
|
+
if isinstance(dataset, dict):
|
|
182
|
+
dataset_id = dataset.get("id")
|
|
183
|
+
except Exception:
|
|
184
|
+
pass
|
|
185
|
+
|
|
186
|
+
return app_id, task_name, dataset_id, version
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
async def check_multiple_apps_health(
|
|
190
|
+
urls: list[str],
|
|
191
|
+
api_key: str | None,
|
|
192
|
+
timeout: float = 2.0,
|
|
193
|
+
max_concurrent: int = 10,
|
|
194
|
+
) -> dict[str, tuple[Literal["healthy", "unhealthy", "unknown"], dict[str, Any]]]:
|
|
195
|
+
"""Check health for multiple apps concurrently.
|
|
196
|
+
|
|
197
|
+
Performs health checks on multiple task apps in parallel using asyncio
|
|
198
|
+
semaphores to limit concurrent requests. This is more efficient than
|
|
199
|
+
checking apps sequentially, especially when scanning many ports or services.
|
|
200
|
+
|
|
201
|
+
Concurrency Control:
|
|
202
|
+
Uses asyncio.Semaphore to limit the number of concurrent HTTP requests,
|
|
203
|
+
preventing resource exhaustion when checking many apps simultaneously.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
urls: List of app URLs to check (e.g., ["http://localhost:8000", ...]).
|
|
207
|
+
Each URL will be checked independently.
|
|
208
|
+
api_key: API key for authentication via X-API-Key header. Applied to
|
|
209
|
+
all requests. If None, requests are made without authentication.
|
|
210
|
+
timeout: Request timeout in seconds per app. Default is 2.0 seconds.
|
|
211
|
+
max_concurrent: Maximum number of concurrent HTTP requests. Default is 10.
|
|
212
|
+
Increase for faster scanning, decrease to reduce resource usage.
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
Dictionary mapping URL -> (health_status, metadata) where:
|
|
216
|
+
- Keys are the input URLs
|
|
217
|
+
- Values are tuples of (health_status, metadata_dict) as returned
|
|
218
|
+
by check_app_health()
|
|
219
|
+
|
|
220
|
+
Examples:
|
|
221
|
+
>>> import asyncio
|
|
222
|
+
>>> urls = ["http://localhost:8000", "http://localhost:8001"]
|
|
223
|
+
>>> results = asyncio.run(check_multiple_apps_health(urls, "key123"))
|
|
224
|
+
>>> for url, (status, metadata) in results.items():
|
|
225
|
+
... print(f"{url}: {status}")
|
|
226
|
+
|
|
227
|
+
Note:
|
|
228
|
+
Exceptions during individual health checks are caught and result in
|
|
229
|
+
"unknown" status. The function never raises exceptions.
|
|
230
|
+
"""
|
|
231
|
+
semaphore = asyncio.Semaphore(max_concurrent)
|
|
232
|
+
results: dict[str, tuple[Literal["healthy", "unhealthy", "unknown"], dict[str, Any]]] = {}
|
|
233
|
+
|
|
234
|
+
async def check_one(url: str) -> None:
|
|
235
|
+
async with semaphore:
|
|
236
|
+
status, metadata = await check_app_health(url, api_key, timeout)
|
|
237
|
+
results[url] = (status, metadata)
|
|
238
|
+
|
|
239
|
+
await asyncio.gather(*[check_one(url) for url in urls], return_exceptions=True)
|
|
240
|
+
|
|
241
|
+
return results
|
|
242
|
+
|