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,713 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import contextlib
|
|
5
|
+
import json
|
|
6
|
+
import logging
|
|
7
|
+
import random
|
|
8
|
+
import sys
|
|
9
|
+
import time
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from typing import Any, AsyncIterator, Iterable, Sequence
|
|
12
|
+
|
|
13
|
+
import aiohttp
|
|
14
|
+
|
|
15
|
+
from synth_ai.core.http import AsyncHttpClient, sleep
|
|
16
|
+
from synth_ai.core.telemetry import log_info
|
|
17
|
+
|
|
18
|
+
from .config import StreamConfig
|
|
19
|
+
from .handlers import StreamHandler
|
|
20
|
+
from .types import StreamMessage, StreamType
|
|
21
|
+
|
|
22
|
+
TERMINAL_STATUSES = {"succeeded", "failed", "cancelled", "canceled", "completed"}
|
|
23
|
+
TERMINAL_EVENT_SUCCESS = {
|
|
24
|
+
"sft.job.completed",
|
|
25
|
+
"rl.train.completed",
|
|
26
|
+
"rl.job.completed",
|
|
27
|
+
"context.learning.job.completed",
|
|
28
|
+
"workflow.completed",
|
|
29
|
+
"training.completed",
|
|
30
|
+
# GraphGen / Graph Evolve events
|
|
31
|
+
"optimization_completed",
|
|
32
|
+
"graphgen.completed",
|
|
33
|
+
"graph_evolve.completed",
|
|
34
|
+
# GEPA / Prompt Learning events
|
|
35
|
+
"prompt.learning.gepa.complete",
|
|
36
|
+
"prompt.learning.mipro.complete",
|
|
37
|
+
"gepa.complete",
|
|
38
|
+
"mipro.complete",
|
|
39
|
+
}
|
|
40
|
+
TERMINAL_EVENT_FAILURE = {
|
|
41
|
+
"sft.job.failed",
|
|
42
|
+
"rl.train.failed",
|
|
43
|
+
"rl.job.failed",
|
|
44
|
+
"context.learning.job.failed",
|
|
45
|
+
"workflow.failed",
|
|
46
|
+
"training.failed",
|
|
47
|
+
# GraphGen / Graph Evolve events
|
|
48
|
+
"optimization_failed",
|
|
49
|
+
"graphgen.failed",
|
|
50
|
+
"graph_evolve.failed",
|
|
51
|
+
# GEPA / Prompt Learning events
|
|
52
|
+
"prompt.learning.gepa.failed",
|
|
53
|
+
"prompt.learning.mipro.failed",
|
|
54
|
+
"gepa.failed",
|
|
55
|
+
"mipro.failed",
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@dataclass(slots=True)
|
|
60
|
+
class StreamEndpoints:
|
|
61
|
+
"""Collection of endpoint paths (with optional fallbacks) to poll for a job."""
|
|
62
|
+
|
|
63
|
+
status: str | None
|
|
64
|
+
events: str | None = None
|
|
65
|
+
metrics: str | None = None
|
|
66
|
+
timeline: str | None = None
|
|
67
|
+
status_fallbacks: tuple[str, ...] = ()
|
|
68
|
+
event_fallbacks: tuple[str, ...] = ()
|
|
69
|
+
metric_fallbacks: tuple[str, ...] = ()
|
|
70
|
+
timeline_fallbacks: tuple[str, ...] = ()
|
|
71
|
+
|
|
72
|
+
@classmethod
|
|
73
|
+
def learning(cls, job_id: str) -> StreamEndpoints:
|
|
74
|
+
base = f"/learning/jobs/{job_id}"
|
|
75
|
+
return cls(
|
|
76
|
+
status=base,
|
|
77
|
+
events=f"{base}/events",
|
|
78
|
+
metrics=f"{base}/metrics",
|
|
79
|
+
timeline=f"{base}/timeline",
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
@classmethod
|
|
83
|
+
def prompt_learning(cls, job_id: str) -> StreamEndpoints:
|
|
84
|
+
"""Endpoints for prompt learning jobs (MIPRO/GEPA)."""
|
|
85
|
+
base = f"/prompt-learning/online/jobs/{job_id}"
|
|
86
|
+
return cls(
|
|
87
|
+
status=base,
|
|
88
|
+
events=f"{base}/events",
|
|
89
|
+
metrics=f"{base}/metrics",
|
|
90
|
+
timeline=None,
|
|
91
|
+
status_fallbacks=(
|
|
92
|
+
f"/learning/jobs/{job_id}",
|
|
93
|
+
f"/orchestration/jobs/{job_id}",
|
|
94
|
+
),
|
|
95
|
+
event_fallbacks=(
|
|
96
|
+
f"/learning/jobs/{job_id}/events",
|
|
97
|
+
f"/orchestration/jobs/{job_id}/events",
|
|
98
|
+
),
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
@classmethod
|
|
102
|
+
def context_learning(cls, job_id: str) -> StreamEndpoints:
|
|
103
|
+
"""Endpoints for context learning jobs."""
|
|
104
|
+
base = f"/context-learning/jobs/{job_id}"
|
|
105
|
+
return cls(
|
|
106
|
+
status=base,
|
|
107
|
+
events=f"{base}/events",
|
|
108
|
+
metrics=f"{base}/metrics",
|
|
109
|
+
timeline=None,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def events_stream_url(self) -> str | None:
|
|
114
|
+
"""Get the SSE streaming URL for events if available."""
|
|
115
|
+
if self.events:
|
|
116
|
+
if self.events.endswith("/events"):
|
|
117
|
+
return f"{self.events}/stream"
|
|
118
|
+
return None
|
|
119
|
+
|
|
120
|
+
@classmethod
|
|
121
|
+
def rl(cls, job_id: str) -> StreamEndpoints:
|
|
122
|
+
base = f"/rl/jobs/{job_id}"
|
|
123
|
+
return cls(
|
|
124
|
+
status=base,
|
|
125
|
+
events=f"{base}/events",
|
|
126
|
+
metrics=f"{base}/metrics",
|
|
127
|
+
timeline=f"{base}/timeline",
|
|
128
|
+
status_fallbacks=(
|
|
129
|
+
f"/learning/jobs/{job_id}",
|
|
130
|
+
f"/orchestration/jobs/{job_id}",
|
|
131
|
+
),
|
|
132
|
+
event_fallbacks=(
|
|
133
|
+
f"/learning/jobs/{job_id}/events",
|
|
134
|
+
f"/orchestration/jobs/{job_id}/events",
|
|
135
|
+
),
|
|
136
|
+
metric_fallbacks=(
|
|
137
|
+
f"/learning/jobs/{job_id}/metrics",
|
|
138
|
+
),
|
|
139
|
+
timeline_fallbacks=(
|
|
140
|
+
f"/learning/jobs/{job_id}/timeline",
|
|
141
|
+
),
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
@classmethod
|
|
145
|
+
def graphgen(cls, job_id: str) -> StreamEndpoints:
|
|
146
|
+
"""Endpoints for GraphGen workflow optimization jobs.
|
|
147
|
+
|
|
148
|
+
GraphGen jobs use /api/graphgen/jobs/{job_id} endpoints.
|
|
149
|
+
The backend handles GraphGen -> graph_evolve job ID resolution internally using job_relationships.
|
|
150
|
+
No fallbacks needed - GraphGen endpoints resolve everything.
|
|
151
|
+
"""
|
|
152
|
+
base = f"/graphgen/jobs/{job_id}"
|
|
153
|
+
return cls(
|
|
154
|
+
status=base,
|
|
155
|
+
events=f"{base}/events",
|
|
156
|
+
metrics=f"{base}/metrics",
|
|
157
|
+
timeline=None,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
@classmethod
|
|
161
|
+
def adas(cls, job_id: str) -> StreamEndpoints:
|
|
162
|
+
"""Legacy alias for GraphGen workflow optimization jobs."""
|
|
163
|
+
return cls.graphgen(job_id)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class JobStreamer:
|
|
167
|
+
"""Poll job endpoints and dispatch messages to configured handlers."""
|
|
168
|
+
|
|
169
|
+
def __init__(
|
|
170
|
+
self,
|
|
171
|
+
*,
|
|
172
|
+
base_url: str,
|
|
173
|
+
api_key: str,
|
|
174
|
+
job_id: str,
|
|
175
|
+
endpoints: StreamEndpoints | None = None,
|
|
176
|
+
config: StreamConfig | None = None,
|
|
177
|
+
handlers: Sequence[StreamHandler] | None = None,
|
|
178
|
+
interval_seconds: float = 2.0,
|
|
179
|
+
timeout_seconds: float | None = None,
|
|
180
|
+
http_timeout: float = 60.0,
|
|
181
|
+
http_client: AsyncHttpClient | None = None,
|
|
182
|
+
sleep_fn= sleep,
|
|
183
|
+
) -> None:
|
|
184
|
+
self.base_url = base_url.rstrip("/")
|
|
185
|
+
self.api_key = api_key
|
|
186
|
+
self.job_id = job_id
|
|
187
|
+
self.endpoints = endpoints or StreamEndpoints.learning(job_id)
|
|
188
|
+
self.config = config or StreamConfig.default()
|
|
189
|
+
self.handlers: list[StreamHandler] = list(handlers or [])
|
|
190
|
+
self.interval_seconds = interval_seconds
|
|
191
|
+
self.timeout_seconds = timeout_seconds
|
|
192
|
+
self.http_timeout = http_timeout
|
|
193
|
+
self._http = http_client
|
|
194
|
+
self._sleep = sleep_fn
|
|
195
|
+
|
|
196
|
+
status_sources: list[str | None] = [self.endpoints.status]
|
|
197
|
+
status_sources.extend(self.endpoints.status_fallbacks)
|
|
198
|
+
self._status_paths = [p for p in status_sources if p]
|
|
199
|
+
|
|
200
|
+
event_sources: list[str | None] = [self.endpoints.events]
|
|
201
|
+
event_sources.extend(self.endpoints.event_fallbacks)
|
|
202
|
+
self._event_paths = [p for p in event_sources if p]
|
|
203
|
+
|
|
204
|
+
metric_sources: list[str | None] = [self.endpoints.metrics]
|
|
205
|
+
metric_sources.extend(self.endpoints.metric_fallbacks)
|
|
206
|
+
self._metric_paths = [p for p in metric_sources if p]
|
|
207
|
+
|
|
208
|
+
timeline_sources: list[str | None] = [self.endpoints.timeline]
|
|
209
|
+
timeline_sources.extend(self.endpoints.timeline_fallbacks)
|
|
210
|
+
self._timeline_paths = [p for p in timeline_sources if p]
|
|
211
|
+
|
|
212
|
+
self._last_seq_by_stream: dict[str, int] = {}
|
|
213
|
+
self._metric_cursors: dict[str, tuple[int | None, str]] = {}
|
|
214
|
+
self._seen_messages: set[str] = set()
|
|
215
|
+
self._last_status_payload: dict[str, Any] | None = None
|
|
216
|
+
self._last_status_value: str | None = None
|
|
217
|
+
self._terminal_seen = False
|
|
218
|
+
self._terminal_event_status: str | None = None
|
|
219
|
+
|
|
220
|
+
if not self.handlers:
|
|
221
|
+
from .handlers import CLIHandler
|
|
222
|
+
|
|
223
|
+
self.handlers = [CLIHandler()]
|
|
224
|
+
|
|
225
|
+
async def stream_until_terminal(self) -> dict[str, Any]:
|
|
226
|
+
"""Stream configured endpoints until the job reaches a terminal state."""
|
|
227
|
+
ctx: dict[str, Any] = {"job_id": self.job_id, "base_url": self.base_url}
|
|
228
|
+
log_info("JobStreamer.stream_until_terminal invoked", ctx=ctx)
|
|
229
|
+
http_cm = self._http or AsyncHttpClient(self.base_url, self.api_key, timeout=self.http_timeout)
|
|
230
|
+
async with http_cm as http:
|
|
231
|
+
# Use SSE streaming exclusively for events (prompt learning jobs)
|
|
232
|
+
# SSE provides real-time event delivery from Redis, avoiding empty polling responses
|
|
233
|
+
sse_url = self.endpoints.events_stream_url
|
|
234
|
+
if sse_url and StreamType.EVENTS in self.config.enabled_streams:
|
|
235
|
+
# SSE streaming - real-time event delivery, with concurrent status polling
|
|
236
|
+
|
|
237
|
+
# Create a queue to receive events from SSE stream
|
|
238
|
+
event_queue: asyncio.Queue = asyncio.Queue()
|
|
239
|
+
sse_done = asyncio.Event()
|
|
240
|
+
|
|
241
|
+
async def sse_reader():
|
|
242
|
+
"""Read SSE events and put them in the queue."""
|
|
243
|
+
try:
|
|
244
|
+
async for event_msg in self._stream_events_sse(sse_url):
|
|
245
|
+
await event_queue.put(event_msg)
|
|
246
|
+
if self._terminal_seen:
|
|
247
|
+
break
|
|
248
|
+
except Exception as e:
|
|
249
|
+
await event_queue.put(e) # Put exception in queue
|
|
250
|
+
finally:
|
|
251
|
+
sse_done.set()
|
|
252
|
+
|
|
253
|
+
async def status_poller():
|
|
254
|
+
"""Periodically poll status while SSE stream is active."""
|
|
255
|
+
while not sse_done.is_set() and not self._terminal_seen:
|
|
256
|
+
await asyncio.sleep(2.0) # Check every 2 seconds
|
|
257
|
+
if self._terminal_seen or sse_done.is_set():
|
|
258
|
+
break
|
|
259
|
+
|
|
260
|
+
status = await self._refresh_status(http)
|
|
261
|
+
|
|
262
|
+
metric_messages = await self._poll_metrics(http)
|
|
263
|
+
timeline_messages = await self._poll_timeline(http)
|
|
264
|
+
self._dispatch(metric_messages + timeline_messages)
|
|
265
|
+
|
|
266
|
+
# Check for terminal status
|
|
267
|
+
if status and status.lower() in TERMINAL_STATUSES:
|
|
268
|
+
self._terminal_seen = True
|
|
269
|
+
break
|
|
270
|
+
|
|
271
|
+
# Start both tasks concurrently
|
|
272
|
+
sse_task = asyncio.create_task(sse_reader())
|
|
273
|
+
status_task = asyncio.create_task(status_poller())
|
|
274
|
+
|
|
275
|
+
try:
|
|
276
|
+
# Process events from queue
|
|
277
|
+
while not self._terminal_seen:
|
|
278
|
+
# Wait for event or timeout
|
|
279
|
+
try:
|
|
280
|
+
item = await asyncio.wait_for(event_queue.get(), timeout=1.0)
|
|
281
|
+
except asyncio.TimeoutError:
|
|
282
|
+
# No event received, check if SSE is done or terminal
|
|
283
|
+
if sse_done.is_set() or self._terminal_seen:
|
|
284
|
+
break
|
|
285
|
+
continue
|
|
286
|
+
|
|
287
|
+
# Handle exception from SSE reader
|
|
288
|
+
if isinstance(item, Exception):
|
|
289
|
+
raise item
|
|
290
|
+
|
|
291
|
+
# Process event
|
|
292
|
+
self._dispatch([item])
|
|
293
|
+
|
|
294
|
+
# Poll metrics/timeline after each event
|
|
295
|
+
metric_messages = await self._poll_metrics(http)
|
|
296
|
+
timeline_messages = await self._poll_timeline(http)
|
|
297
|
+
self._dispatch(metric_messages + timeline_messages)
|
|
298
|
+
|
|
299
|
+
# Check for terminal status
|
|
300
|
+
if self._terminal_seen:
|
|
301
|
+
break
|
|
302
|
+
finally:
|
|
303
|
+
# Cancel tasks
|
|
304
|
+
sse_task.cancel()
|
|
305
|
+
status_task.cancel()
|
|
306
|
+
try:
|
|
307
|
+
await asyncio.gather(sse_task, status_task, return_exceptions=True)
|
|
308
|
+
except Exception:
|
|
309
|
+
pass
|
|
310
|
+
else:
|
|
311
|
+
# No SSE endpoint available - use polling for events
|
|
312
|
+
while True:
|
|
313
|
+
status = await self._refresh_status(http)
|
|
314
|
+
|
|
315
|
+
# Check status FIRST before polling events/metrics
|
|
316
|
+
if status and status.lower() in TERMINAL_STATUSES:
|
|
317
|
+
self._terminal_seen = True
|
|
318
|
+
break
|
|
319
|
+
if self._terminal_seen:
|
|
320
|
+
break
|
|
321
|
+
|
|
322
|
+
event_messages = await self._poll_events(http)
|
|
323
|
+
metric_messages = await self._poll_metrics(http)
|
|
324
|
+
timeline_messages = await self._poll_timeline(http)
|
|
325
|
+
|
|
326
|
+
self._dispatch(event_messages + metric_messages + timeline_messages)
|
|
327
|
+
|
|
328
|
+
# Check again after polling (terminal events might have been received)
|
|
329
|
+
if self._terminal_seen:
|
|
330
|
+
break
|
|
331
|
+
if status and status.lower() in TERMINAL_STATUSES:
|
|
332
|
+
self._terminal_seen = True
|
|
333
|
+
break
|
|
334
|
+
|
|
335
|
+
await self._sleep(self.interval_seconds)
|
|
336
|
+
|
|
337
|
+
for handler in self.handlers:
|
|
338
|
+
with contextlib.suppress(Exception):
|
|
339
|
+
handler.flush()
|
|
340
|
+
|
|
341
|
+
final_status = self._terminal_event_status or self._last_status_value or "unknown"
|
|
342
|
+
if self._last_status_payload:
|
|
343
|
+
self._last_status_payload["status"] = final_status
|
|
344
|
+
return self._last_status_payload
|
|
345
|
+
return {"job_id": self.job_id, "status": final_status}
|
|
346
|
+
|
|
347
|
+
async def _stream_events_sse(self, sse_url: str) -> AsyncIterator[StreamMessage]:
|
|
348
|
+
"""Stream events via Server-Sent Events (SSE)."""
|
|
349
|
+
url = f"{self.base_url.rstrip('/')}/{sse_url.lstrip('/')}"
|
|
350
|
+
headers = {
|
|
351
|
+
"Accept": "text/event-stream",
|
|
352
|
+
"Cache-Control": "no-cache",
|
|
353
|
+
"authorization": f"Bearer {self.api_key}",
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
# Create a separate session for SSE (long-lived connection)
|
|
357
|
+
timeout = aiohttp.ClientTimeout(total=None) # No timeout for SSE
|
|
358
|
+
async with aiohttp.ClientSession(headers=headers, timeout=timeout) as session:
|
|
359
|
+
async with session.get(url) as resp:
|
|
360
|
+
if resp.status != 200:
|
|
361
|
+
raise Exception(f"SSE endpoint returned {resp.status}: {await resp.text()}")
|
|
362
|
+
|
|
363
|
+
print(f"[DEBUG] SSE stream connected to {url}, status={resp.status}", file=sys.stderr)
|
|
364
|
+
buffer = ""
|
|
365
|
+
event_count = 0
|
|
366
|
+
last_event_time = time.time()
|
|
367
|
+
no_events_warning_printed = False
|
|
368
|
+
|
|
369
|
+
# Read SSE stream in chunks and parse events
|
|
370
|
+
async for chunk in resp.content.iter_chunked(8192):
|
|
371
|
+
current_time = time.time()
|
|
372
|
+
|
|
373
|
+
# Warn if no events received for 10 seconds (events should be streaming)
|
|
374
|
+
if event_count == 1 and current_time - last_event_time > 10 and not no_events_warning_printed:
|
|
375
|
+
print(f"[DEBUG] WARNING: No events received via SSE for 10s after connection. Backend may not be publishing to Redis (check SSE_USE_REDIS env var).", file=sys.stderr)
|
|
376
|
+
no_events_warning_printed = True
|
|
377
|
+
|
|
378
|
+
buffer += chunk.decode("utf-8", errors="ignore")
|
|
379
|
+
|
|
380
|
+
# SSE events are separated by double newlines
|
|
381
|
+
while "\n\n" in buffer:
|
|
382
|
+
event_block, buffer = buffer.split("\n\n", 1)
|
|
383
|
+
event_block = event_block.strip()
|
|
384
|
+
|
|
385
|
+
if not event_block:
|
|
386
|
+
continue
|
|
387
|
+
|
|
388
|
+
event_data = {}
|
|
389
|
+
event_id = None
|
|
390
|
+
event_type_line = None
|
|
391
|
+
|
|
392
|
+
# Parse SSE event block line by line
|
|
393
|
+
for event_line in event_block.split("\n"):
|
|
394
|
+
event_line = event_line.strip()
|
|
395
|
+
if not event_line or event_line.startswith(":"):
|
|
396
|
+
continue # Skip comments/empty lines
|
|
397
|
+
if event_line.startswith("id:"):
|
|
398
|
+
event_id = event_line[3:].strip()
|
|
399
|
+
elif event_line.startswith("event:"):
|
|
400
|
+
event_type_line = event_line[6:].strip()
|
|
401
|
+
elif event_line.startswith("data:"):
|
|
402
|
+
data_str = event_line[5:].strip()
|
|
403
|
+
try:
|
|
404
|
+
event_data = json.loads(data_str)
|
|
405
|
+
except json.JSONDecodeError as e:
|
|
406
|
+
print(f"[DEBUG] Failed to parse SSE data: {e}, data={data_str[:200]}", file=sys.stderr)
|
|
407
|
+
continue
|
|
408
|
+
|
|
409
|
+
# Debug: log what we parsed
|
|
410
|
+
if event_data:
|
|
411
|
+
event_count += 1
|
|
412
|
+
last_event_time = time.time()
|
|
413
|
+
print(f"[DEBUG] Parsed SSE event #{event_count}: type={event_data.get('type')}, seq={event_data.get('seq')}", file=sys.stderr)
|
|
414
|
+
|
|
415
|
+
if event_data and "type" in event_data:
|
|
416
|
+
# Convert SSE event to StreamMessage
|
|
417
|
+
event_job_id = event_data.get("job_id") or self.job_id
|
|
418
|
+
msg = StreamMessage.from_event(event_job_id, event_data)
|
|
419
|
+
|
|
420
|
+
# Update sequence tracking
|
|
421
|
+
seq = event_data.get("seq")
|
|
422
|
+
if seq is not None:
|
|
423
|
+
try:
|
|
424
|
+
seq_int = int(seq)
|
|
425
|
+
if sse_url not in self._last_seq_by_stream or seq_int > self._last_seq_by_stream[sse_url]:
|
|
426
|
+
self._last_seq_by_stream[sse_url] = seq_int
|
|
427
|
+
except (TypeError, ValueError):
|
|
428
|
+
pass
|
|
429
|
+
|
|
430
|
+
# Check for terminal events
|
|
431
|
+
event_type = str(event_data.get("type", "")).lower()
|
|
432
|
+
if event_type in TERMINAL_EVENT_SUCCESS:
|
|
433
|
+
self._terminal_seen = True
|
|
434
|
+
self._terminal_event_status = "succeeded"
|
|
435
|
+
elif event_type in TERMINAL_EVENT_FAILURE:
|
|
436
|
+
self._terminal_seen = True
|
|
437
|
+
self._terminal_event_status = "failed"
|
|
438
|
+
|
|
439
|
+
yield msg
|
|
440
|
+
|
|
441
|
+
async def _refresh_status(self, http: AsyncHttpClient) -> str:
|
|
442
|
+
status_payload = await self._poll_status(http)
|
|
443
|
+
if status_payload:
|
|
444
|
+
self._last_status_payload = status_payload
|
|
445
|
+
status = str(status_payload.get("status") or status_payload.get("state") or "").lower()
|
|
446
|
+
if status:
|
|
447
|
+
self._last_status_value = status
|
|
448
|
+
if status in TERMINAL_STATUSES:
|
|
449
|
+
self._terminal_seen = True
|
|
450
|
+
print(f"[SDK] Terminal status detected: {status}", flush=True)
|
|
451
|
+
return status
|
|
452
|
+
return self._last_status_value or ""
|
|
453
|
+
|
|
454
|
+
async def _poll_status(self, http: AsyncHttpClient) -> dict[str, Any] | None:
|
|
455
|
+
if StreamType.STATUS not in self.config.enabled_streams or not self._status_paths:
|
|
456
|
+
return None
|
|
457
|
+
|
|
458
|
+
last_error: Exception | None = None
|
|
459
|
+
for path in self._status_paths:
|
|
460
|
+
try:
|
|
461
|
+
# Add cache-busting query param to ensure fresh status
|
|
462
|
+
# Use a timestamp to prevent any caching
|
|
463
|
+
params = {"_t": int(time.time() * 1000)}
|
|
464
|
+
data = await http.get(path, params=params)
|
|
465
|
+
except Exception as exc:
|
|
466
|
+
last_error = exc
|
|
467
|
+
# Try next fallback path
|
|
468
|
+
continue
|
|
469
|
+
if isinstance(data, dict):
|
|
470
|
+
message = StreamMessage.from_status(self.job_id, data)
|
|
471
|
+
self._dispatch([message])
|
|
472
|
+
return data
|
|
473
|
+
|
|
474
|
+
# If all paths failed, log the error for debugging
|
|
475
|
+
if last_error is not None:
|
|
476
|
+
logger = logging.getLogger(__name__)
|
|
477
|
+
logger.debug(f"Status polling failed for all paths: {last_error}")
|
|
478
|
+
return None
|
|
479
|
+
|
|
480
|
+
async def _poll_events(self, http: AsyncHttpClient) -> list[StreamMessage]:
|
|
481
|
+
if StreamType.EVENTS not in self.config.enabled_streams or not self._event_paths:
|
|
482
|
+
return []
|
|
483
|
+
messages: list[StreamMessage] = []
|
|
484
|
+
total = 0
|
|
485
|
+
for path in self._event_paths:
|
|
486
|
+
since = self._last_seq_by_stream.get(path, 0)
|
|
487
|
+
# Increase limit to capture more events per poll
|
|
488
|
+
limit = 1000 if self.config.max_events_per_poll and self.config.max_events_per_poll > 200 else 200
|
|
489
|
+
params = {"since_seq": since, "limit": limit}
|
|
490
|
+
try:
|
|
491
|
+
data = await http.get(path, params=params)
|
|
492
|
+
# Debug: Always log what we got from API
|
|
493
|
+
print(f"[DEBUG] Polling {path} with since_seq={since}, limit={limit}", file=sys.stderr)
|
|
494
|
+
print(f"[DEBUG] Got response from {path}, type={type(data).__name__}, keys={list(data.keys()) if isinstance(data, dict) else 'not dict'}", file=sys.stderr)
|
|
495
|
+
if isinstance(data, dict):
|
|
496
|
+
# Check for next_seq to see if we should update our tracking
|
|
497
|
+
if "next_seq" in data:
|
|
498
|
+
print(f"[DEBUG] Response has next_seq={data.get('next_seq')}, current since={since}", file=sys.stderr)
|
|
499
|
+
# Show what keys are in the response
|
|
500
|
+
for key in data.keys():
|
|
501
|
+
val = data[key]
|
|
502
|
+
if isinstance(val, list):
|
|
503
|
+
print(f"[DEBUG] Response[{key}] is list with {len(val)} items", file=sys.stderr)
|
|
504
|
+
if len(val) > 0:
|
|
505
|
+
print(f"[DEBUG] First item in {key}: {list(val[0].keys()) if isinstance(val[0], dict) else type(val[0])}", file=sys.stderr)
|
|
506
|
+
elif isinstance(val, dict):
|
|
507
|
+
print(f"[DEBUG] Response[{key}] is dict with keys: {list(val.keys())[:5]}", file=sys.stderr)
|
|
508
|
+
except Exception as e:
|
|
509
|
+
error_str = str(e)
|
|
510
|
+
print(f"[DEBUG] Error polling {path}: {e}", file=sys.stderr)
|
|
511
|
+
# Fail fast if we get 404 on both ADAS and fallback endpoints (indicates job ID mapping issue)
|
|
512
|
+
if "404" in error_str and (
|
|
513
|
+
"graphgen" in path.lower()
|
|
514
|
+
or "adas" in path.lower()
|
|
515
|
+
or "prompt-learning" in path.lower()
|
|
516
|
+
):
|
|
517
|
+
# Check if this is the last fallback path - if so, raise to fail fast
|
|
518
|
+
if path == self._event_paths[-1]: # Last fallback path
|
|
519
|
+
raise RuntimeError(
|
|
520
|
+
f"Failed to poll events: All endpoints returned 404. "
|
|
521
|
+
f"This likely indicates a job ID mapping issue. "
|
|
522
|
+
f"GraphGen endpoints need the GraphGen job ID; GEPA fallback endpoints need the GEPA job ID. "
|
|
523
|
+
f"Last error: {error_str}"
|
|
524
|
+
)
|
|
525
|
+
continue
|
|
526
|
+
raw_events = _extract_list(data, "events")
|
|
527
|
+
# Debug: Always log what we extracted
|
|
528
|
+
print(f"[DEBUG] Extracted {len(raw_events)} events from {path} using _extract_list", file=sys.stderr)
|
|
529
|
+
# Update last_seq using next_seq if available
|
|
530
|
+
if isinstance(data, dict) and "next_seq" in data:
|
|
531
|
+
next_seq = data.get("next_seq")
|
|
532
|
+
if next_seq is not None:
|
|
533
|
+
try:
|
|
534
|
+
next_seq_int = int(next_seq)
|
|
535
|
+
if next_seq_int > since:
|
|
536
|
+
self._last_seq_by_stream[path] = next_seq_int
|
|
537
|
+
print(f"[DEBUG] Updated last_seq for {path} to {next_seq_int}", file=sys.stderr)
|
|
538
|
+
except (TypeError, ValueError):
|
|
539
|
+
pass
|
|
540
|
+
if raw_events and len(raw_events) > 0:
|
|
541
|
+
# Log first event type for debugging
|
|
542
|
+
first_event_type = raw_events[0].get("type", "unknown")
|
|
543
|
+
print(f"[DEBUG] First event type: {first_event_type}", file=sys.stderr)
|
|
544
|
+
for event in raw_events:
|
|
545
|
+
seq_raw = event.get("seq")
|
|
546
|
+
try:
|
|
547
|
+
seq_value = int(seq_raw) # type: ignore[arg-type]
|
|
548
|
+
except (TypeError, ValueError):
|
|
549
|
+
seq_value = None
|
|
550
|
+
last_seq = self._last_seq_by_stream.get(path, 0)
|
|
551
|
+
seq = seq_value if seq_value is not None else last_seq + 1
|
|
552
|
+
if seq <= last_seq:
|
|
553
|
+
continue
|
|
554
|
+
if seq_value is None:
|
|
555
|
+
event["seq"] = seq
|
|
556
|
+
self._last_seq_by_stream[path] = seq
|
|
557
|
+
# Bypass filtering - show ALL events
|
|
558
|
+
# if not self.config.should_include_event(event):
|
|
559
|
+
# continue
|
|
560
|
+
event_job_id = event.get("job_id") or self.job_id
|
|
561
|
+
event_message = StreamMessage.from_event(event_job_id, event)
|
|
562
|
+
event_type = str(event.get("type") or "").lower()
|
|
563
|
+
if event_type in TERMINAL_EVENT_SUCCESS:
|
|
564
|
+
self._terminal_seen = True
|
|
565
|
+
self._terminal_event_status = "succeeded"
|
|
566
|
+
elif event_type in TERMINAL_EVENT_FAILURE:
|
|
567
|
+
self._terminal_seen = True
|
|
568
|
+
self._terminal_event_status = "failed"
|
|
569
|
+
messages.append(event_message)
|
|
570
|
+
total += 1
|
|
571
|
+
if self.config.max_events_per_poll and total >= self.config.max_events_per_poll:
|
|
572
|
+
return messages
|
|
573
|
+
return messages
|
|
574
|
+
|
|
575
|
+
async def _poll_metrics(self, http: AsyncHttpClient) -> list[StreamMessage]:
|
|
576
|
+
if StreamType.METRICS not in self.config.enabled_streams or not self._metric_paths:
|
|
577
|
+
return []
|
|
578
|
+
messages: list[StreamMessage] = []
|
|
579
|
+
for path in self._metric_paths:
|
|
580
|
+
params = {"limit": 200}
|
|
581
|
+
try:
|
|
582
|
+
data = await http.get(path, params=params)
|
|
583
|
+
except Exception:
|
|
584
|
+
continue
|
|
585
|
+
points = _extract_list(data, "points")
|
|
586
|
+
for point in points:
|
|
587
|
+
name = str(point.get("name") or "")
|
|
588
|
+
if not name:
|
|
589
|
+
continue
|
|
590
|
+
step, fingerprint = _metric_cursor(point)
|
|
591
|
+
last_step, last_fingerprint = self._metric_cursors.get(name, (None, ""))
|
|
592
|
+
if step is not None:
|
|
593
|
+
if last_step is not None and step <= last_step:
|
|
594
|
+
continue
|
|
595
|
+
elif fingerprint and fingerprint == last_fingerprint:
|
|
596
|
+
continue
|
|
597
|
+
self._metric_cursors[name] = (step, fingerprint)
|
|
598
|
+
if not self.config.should_include_metric(point):
|
|
599
|
+
continue
|
|
600
|
+
metric_job_id = point.get("job_id") or self.job_id
|
|
601
|
+
messages.append(StreamMessage.from_metric(metric_job_id, point))
|
|
602
|
+
return messages
|
|
603
|
+
|
|
604
|
+
async def _poll_timeline(self, http: AsyncHttpClient) -> list[StreamMessage]:
|
|
605
|
+
if StreamType.TIMELINE not in self.config.enabled_streams or not self._timeline_paths:
|
|
606
|
+
return []
|
|
607
|
+
messages: list[StreamMessage] = []
|
|
608
|
+
for path in self._timeline_paths:
|
|
609
|
+
try:
|
|
610
|
+
data = await http.get(path)
|
|
611
|
+
except Exception:
|
|
612
|
+
continue
|
|
613
|
+
|
|
614
|
+
timeline_entries = _extract_list(data, "events")
|
|
615
|
+
for entry in timeline_entries:
|
|
616
|
+
if not self.config.should_include_timeline(entry):
|
|
617
|
+
continue
|
|
618
|
+
timeline_job_id = entry.get("job_id") or self.job_id
|
|
619
|
+
phase = str(entry.get("phase") or "").lower()
|
|
620
|
+
if phase in TERMINAL_STATUSES:
|
|
621
|
+
self._terminal_seen = True
|
|
622
|
+
if phase in {"failed", "cancelled", "canceled"}:
|
|
623
|
+
self._terminal_event_status = "failed"
|
|
624
|
+
elif phase:
|
|
625
|
+
self._terminal_event_status = "succeeded"
|
|
626
|
+
messages.append(StreamMessage.from_timeline(timeline_job_id, entry))
|
|
627
|
+
return messages
|
|
628
|
+
|
|
629
|
+
def _dispatch(self, messages: Iterable[StreamMessage]) -> None:
|
|
630
|
+
message_list = list(messages)
|
|
631
|
+
for message in message_list:
|
|
632
|
+
if self.config.deduplicate and message.key in self._seen_messages:
|
|
633
|
+
continue
|
|
634
|
+
if self.config.sample_rate < 1.0 and random.random() > self.config.sample_rate:
|
|
635
|
+
continue
|
|
636
|
+
if self.config.deduplicate:
|
|
637
|
+
self._seen_messages.add(message.key)
|
|
638
|
+
|
|
639
|
+
# Check for terminal events in dispatch (belt-and-suspenders)
|
|
640
|
+
if message.stream_type == StreamType.EVENTS and message.data:
|
|
641
|
+
event_type = str(message.data.get("type", "")).lower()
|
|
642
|
+
if event_type in TERMINAL_EVENT_SUCCESS:
|
|
643
|
+
self._terminal_seen = True
|
|
644
|
+
self._terminal_event_status = "succeeded"
|
|
645
|
+
elif event_type in TERMINAL_EVENT_FAILURE:
|
|
646
|
+
self._terminal_seen = True
|
|
647
|
+
self._terminal_event_status = "failed"
|
|
648
|
+
|
|
649
|
+
for handler in self.handlers:
|
|
650
|
+
try:
|
|
651
|
+
if handler.should_handle(message):
|
|
652
|
+
handler.handle(message)
|
|
653
|
+
except Exception:
|
|
654
|
+
pass
|
|
655
|
+
|
|
656
|
+
|
|
657
|
+
def _metric_cursor(point: dict[str, Any]) -> tuple[int | None, str]:
|
|
658
|
+
raw_step = point.get("step")
|
|
659
|
+
step_value: int | None = None
|
|
660
|
+
if raw_step is not None:
|
|
661
|
+
try:
|
|
662
|
+
step_value = int(raw_step) # type: ignore[arg-type]
|
|
663
|
+
except (TypeError, ValueError):
|
|
664
|
+
step_value = None
|
|
665
|
+
|
|
666
|
+
fingerprint = ""
|
|
667
|
+
for key in ("created_at", "updated_at", "timestamp"):
|
|
668
|
+
ts_val = point.get(key)
|
|
669
|
+
if ts_val is not None and ts_val != "":
|
|
670
|
+
fingerprint = str(ts_val)
|
|
671
|
+
break
|
|
672
|
+
if not fingerprint:
|
|
673
|
+
try:
|
|
674
|
+
fingerprint = json.dumps(point, sort_keys=True, default=str)
|
|
675
|
+
except Exception:
|
|
676
|
+
fingerprint = repr(point)
|
|
677
|
+
return step_value, fingerprint
|
|
678
|
+
|
|
679
|
+
|
|
680
|
+
def _extract_list(data: Any, field: str) -> list[dict[str, Any]]:
|
|
681
|
+
results: list[dict[str, Any]] = []
|
|
682
|
+
seen_items: set[int] = set()
|
|
683
|
+
stack: list[Any] = [data]
|
|
684
|
+
seen_containers: set[int] = set()
|
|
685
|
+
|
|
686
|
+
fallback_keys = {"data", "result", "results", "items", "payload", "records", "entries", "values"}
|
|
687
|
+
|
|
688
|
+
while stack:
|
|
689
|
+
current = stack.pop()
|
|
690
|
+
if current is None:
|
|
691
|
+
continue
|
|
692
|
+
current_id = id(current)
|
|
693
|
+
if current_id in seen_containers:
|
|
694
|
+
continue
|
|
695
|
+
seen_containers.add(current_id)
|
|
696
|
+
|
|
697
|
+
if isinstance(current, list):
|
|
698
|
+
for item in current:
|
|
699
|
+
if isinstance(item, dict):
|
|
700
|
+
item_id = id(item)
|
|
701
|
+
if item_id not in seen_items:
|
|
702
|
+
seen_items.add(item_id)
|
|
703
|
+
results.append(item)
|
|
704
|
+
elif isinstance(current, dict):
|
|
705
|
+
if field in current:
|
|
706
|
+
stack.append(current[field])
|
|
707
|
+
for key in fallback_keys:
|
|
708
|
+
if key in current:
|
|
709
|
+
stack.append(current[key])
|
|
710
|
+
return results
|
|
711
|
+
|
|
712
|
+
|
|
713
|
+
__all__ = ["JobStreamer", "StreamEndpoints"]
|