synth-ai 0.2.9.dev7__py3-none-any.whl → 0.2.10__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of synth-ai might be problematic. Click here for more details.
- examples/__init__.py +16 -0
- examples/crafter_debug_render.py +8 -11
- examples/dev/qwen3_32b_qlora_4xh100.toml +40 -0
- examples/multi_step/crafter_rl_lora.md +29 -0
- examples/qwen_coder/README.md +102 -0
- examples/qwen_coder/_shared.py +113 -0
- examples/qwen_coder/configs/coder_lora_30b.toml +61 -0
- examples/qwen_coder/configs/coder_lora_4b.toml +57 -0
- examples/qwen_coder/configs/coder_lora_small.toml +58 -0
- examples/qwen_coder/generate_dataset.py +98 -0
- examples/qwen_coder/infer_ft_smoke.py +65 -0
- examples/qwen_coder/infer_prod_proxy.py +73 -0
- examples/qwen_coder/infer_via_synth.py +87 -0
- examples/qwen_coder/scripts/infer_coder.sh +19 -0
- examples/qwen_coder/scripts/train_coder_30b.sh +22 -0
- examples/qwen_coder/sft_full_17b.py +103 -0
- examples/qwen_coder/sft_lora_30b.py +110 -0
- examples/qwen_coder/subset_jsonl.py +39 -0
- examples/qwen_coder/todos.md +38 -0
- examples/qwen_coder/validate_jsonl.py +60 -0
- examples/rl/run_eval.py +36 -37
- examples/rl/run_rl_and_save.py +5 -5
- examples/rl/task_app/math_single_step.py +65 -43
- examples/rl/task_app/math_task_app.py +3 -3
- examples/sft/README.md +139 -0
- examples/sft/configs/crafter_fft_qwen0p6b.toml +44 -0
- examples/sft/configs/crafter_lora_qwen0p6b.toml +45 -0
- examples/sft/evaluate.py +117 -0
- examples/sft/export_dataset.py +117 -0
- examples/sft/generate_traces.py +162 -0
- examples/swe/__init__.py +12 -0
- examples/swe/task_app/README.md +105 -0
- examples/swe/task_app/__init__.py +2 -0
- examples/swe/task_app/grpo_swe_mini.py +571 -0
- examples/swe/task_app/grpo_swe_mini_task_app.py +136 -0
- examples/swe/task_app/hosted/README.md +173 -0
- examples/swe/task_app/hosted/__init__.py +5 -0
- examples/swe/task_app/hosted/branching.py +143 -0
- examples/swe/task_app/hosted/environment_routes.py +1289 -0
- examples/swe/task_app/hosted/envs/__init__.py +1 -0
- examples/swe/task_app/hosted/envs/crafter/__init__.py +6 -0
- examples/swe/task_app/hosted/envs/crafter/app.py +1 -0
- examples/swe/task_app/hosted/envs/crafter/environment.py +522 -0
- examples/swe/task_app/hosted/envs/crafter/policy.py +478 -0
- examples/swe/task_app/hosted/envs/crafter/react_agent.py +108 -0
- examples/swe/task_app/hosted/envs/crafter/shared.py +305 -0
- examples/swe/task_app/hosted/envs/crafter/tools.py +47 -0
- examples/swe/task_app/hosted/envs/mini_swe/__init__.py +8 -0
- examples/swe/task_app/hosted/envs/mini_swe/environment.py +1164 -0
- examples/swe/task_app/hosted/envs/mini_swe/policy.py +355 -0
- examples/swe/task_app/hosted/envs/mini_swe/shared.py +83 -0
- examples/swe/task_app/hosted/envs/mini_swe/tools.py +96 -0
- examples/swe/task_app/hosted/hosted_app.py +204 -0
- examples/swe/task_app/hosted/inference/__init__.py +5 -0
- examples/swe/task_app/hosted/inference/openai_client.py +618 -0
- examples/swe/task_app/hosted/main.py +100 -0
- examples/swe/task_app/hosted/policy_routes.py +1079 -0
- examples/swe/task_app/hosted/registry.py +195 -0
- examples/swe/task_app/hosted/rollout.py +1869 -0
- examples/swe/task_app/hosted/storage/__init__.py +5 -0
- examples/swe/task_app/hosted/storage/volume.py +211 -0
- examples/swe/task_app/hosted/test_agents.py +161 -0
- examples/swe/task_app/hosted/test_service.py +137 -0
- examples/swe/task_app/hosted/utils.py +62 -0
- examples/vlm/PROPOSAL.md +53 -0
- examples/vlm/README.md +68 -0
- examples/vlm/configs/crafter_vlm_gpt4o.toml +44 -0
- examples/vlm/crafter_image_only_agent.py +207 -0
- examples/vlm/crafter_openai_vlm_agent.py +277 -0
- examples/vlm/filter_image_rows.py +63 -0
- examples/vlm/run_crafter_vlm_benchmark.py +316 -0
- examples/warming_up_to_rl/analyze_trace_db.py +5 -5
- examples/warming_up_to_rl/configs/rl_from_base_qwen4b.toml +11 -1
- examples/warming_up_to_rl/export_trace_sft.py +78 -21
- examples/warming_up_to_rl/groq_test.py +4 -4
- examples/warming_up_to_rl/manage_secrets.py +13 -18
- examples/warming_up_to_rl/run_eval.py +42 -44
- examples/warming_up_to_rl/run_fft_and_save.py +11 -16
- examples/warming_up_to_rl/run_local_rollout.py +1 -3
- examples/warming_up_to_rl/run_local_rollout_modal.py +2 -4
- examples/warming_up_to_rl/run_local_rollout_parallel.py +1 -4
- examples/warming_up_to_rl/run_local_rollout_traced.py +3 -5
- examples/warming_up_to_rl/run_rl_and_save.py +5 -6
- examples/warming_up_to_rl/run_rollout_remote.py +8 -10
- examples/warming_up_to_rl/task_app/README.md +6 -2
- examples/warming_up_to_rl/task_app/grpo_crafter.py +234 -35
- examples/warming_up_to_rl/task_app/grpo_crafter_task_app.py +2 -3
- examples/warming_up_to_rl/task_app/synth_envs_hosted/__init__.py +1 -1
- examples/warming_up_to_rl/task_app/synth_envs_hosted/branching.py +9 -11
- examples/warming_up_to_rl/task_app/synth_envs_hosted/environment_routes.py +131 -114
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/environment.py +101 -41
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/policy.py +73 -51
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/react_agent.py +14 -6
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/shared.py +16 -16
- examples/warming_up_to_rl/task_app/synth_envs_hosted/hosted_app.py +32 -34
- examples/warming_up_to_rl/task_app/synth_envs_hosted/inference/openai_client.py +94 -31
- examples/warming_up_to_rl/task_app/synth_envs_hosted/main.py +0 -2
- examples/warming_up_to_rl/task_app/synth_envs_hosted/policy_routes.py +303 -203
- examples/warming_up_to_rl/task_app/synth_envs_hosted/registry.py +21 -23
- examples/warming_up_to_rl/task_app/synth_envs_hosted/rollout.py +328 -225
- examples/warming_up_to_rl/task_app/synth_envs_hosted/storage/volume.py +13 -13
- examples/warming_up_to_rl/task_app/synth_envs_hosted/test_agents.py +1 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/test_service.py +1 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/utils.py +4 -3
- synth_ai/api/models/supported.py +376 -0
- synth_ai/api/train/builders.py +128 -21
- synth_ai/api/train/cli.py +80 -64
- synth_ai/api/train/config_finder.py +7 -2
- synth_ai/api/train/env_resolver.py +1 -1
- synth_ai/api/train/pollers.py +2 -1
- synth_ai/api/train/supported_algos.py +139 -0
- synth_ai/api/train/task_app.py +1 -2
- synth_ai/api/train/utils.py +13 -44
- synth_ai/cli/__init__.py +8 -0
- synth_ai/cli/_modal_wrapper.py +28 -0
- synth_ai/cli/_typer_patch.py +49 -0
- synth_ai/cli/balance.py +1 -2
- synth_ai/cli/calc.py +1 -1
- synth_ai/cli/demo.py +2 -1
- synth_ai/cli/recent.py +2 -2
- synth_ai/cli/rl_demo.py +2 -1
- synth_ai/cli/root.py +11 -13
- synth_ai/cli/status.py +2 -2
- synth_ai/cli/task_apps.py +529 -179
- synth_ai/cli/traces.py +6 -4
- synth_ai/cli/watch.py +12 -18
- synth_ai/demo_registry.py +1 -1
- synth_ai/demos/core/cli.py +36 -43
- synth_ai/demos/demo_task_apps/__init__.py +3 -3
- synth_ai/demos/demo_task_apps/core.py +17 -25
- synth_ai/demos/demo_task_apps/crafter/grpo_crafter_task_app.py +3 -4
- synth_ai/demos/demo_task_apps/math/app.py +2 -1
- synth_ai/demos/demo_task_apps/math/deploy_modal.py +3 -4
- synth_ai/demos/demo_task_apps/math/modal_task_app.py +16 -18
- synth_ai/demos/demo_task_apps/math/task_app_entry.py +0 -1
- synth_ai/environments/examples/crafter_classic/environment.py +76 -1
- synth_ai/environments/reproducibility/tree.py +2 -5
- synth_ai/environments/service/app.py +11 -12
- synth_ai/environments/service/core_routes.py +4 -7
- synth_ai/environments/stateful/engine.py +1 -1
- synth_ai/environments/tasks/core.py +1 -0
- synth_ai/environments/tasks/filters.py +5 -6
- synth_ai/environments/tasks/utils.py +4 -5
- synth_ai/handshake.py +9 -9
- synth_ai/http.py +1 -1
- synth_ai/http_client.py +18 -10
- synth_ai/inference/client.py +15 -5
- synth_ai/jobs/client.py +78 -83
- synth_ai/learning/__init__.py +41 -6
- synth_ai/learning/algorithms.py +14 -0
- synth_ai/learning/client.py +91 -24
- synth_ai/learning/config.py +2 -38
- synth_ai/learning/ft_client.py +4 -59
- synth_ai/learning/health.py +5 -6
- synth_ai/learning/jobs.py +31 -47
- synth_ai/{rl → learning/rl}/__init__.py +14 -4
- synth_ai/learning/rl/client.py +267 -0
- synth_ai/learning/rl/config.py +31 -0
- synth_ai/{rl → learning/rl}/contracts.py +5 -8
- synth_ai/{rl → learning/rl}/env_keys.py +39 -15
- synth_ai/learning/rl/secrets.py +13 -0
- synth_ai/learning/rl_client.py +2 -281
- synth_ai/learning/sft/__init__.py +29 -0
- synth_ai/learning/sft/client.py +68 -0
- synth_ai/learning/sft/config.py +270 -0
- synth_ai/learning/sft/data.py +295 -0
- synth_ai/learning/sse.py +25 -24
- synth_ai/learning/validators.py +25 -28
- synth_ai/lm/__init__.py +21 -47
- synth_ai/task/__init__.py +25 -27
- synth_ai/task/apps/__init__.py +7 -8
- synth_ai/task/auth.py +8 -8
- synth_ai/task/client.py +14 -14
- synth_ai/task/contracts.py +36 -35
- synth_ai/task/datasets.py +6 -5
- synth_ai/task/errors.py +10 -10
- synth_ai/task/health.py +17 -9
- synth_ai/task/json.py +58 -23
- synth_ai/task/proxy.py +13 -9
- synth_ai/task/rubrics.py +16 -15
- synth_ai/task/server.py +12 -12
- synth_ai/task/tracing_utils.py +4 -4
- synth_ai/task/vendors.py +5 -6
- synth_ai/tracing_v3/__init__.py +2 -0
- synth_ai/tracing_v3/abstractions.py +21 -4
- synth_ai/tracing_v3/decorators.py +18 -16
- synth_ai/tracing_v3/hooks.py +5 -5
- synth_ai/tracing_v3/llm_call_record_helpers.py +6 -6
- synth_ai/tracing_v3/session_tracer.py +40 -14
- synth_ai/tracing_v3/storage/base.py +85 -0
- synth_ai/tracing_v3/storage/config.py +21 -8
- synth_ai/tracing_v3/storage/factory.py +10 -7
- synth_ai/tracing_v3/storage/utils.py +4 -2
- synth_ai/tracing_v3/turso/daemon.py +7 -2
- synth_ai/tracing_v3/turso/models.py +2 -2
- synth_ai/tracing_v3/turso/native_manager.py +1173 -0
- synth_ai/tracing_v3/utils.py +4 -4
- synth_ai/v0/api/__init__.py +8 -0
- synth_ai/v0/api/models/__init__.py +8 -0
- synth_ai/v0/api/models/supported.py +8 -0
- synth_ai/v0/config/__init__.py +15 -0
- synth_ai/v0/config/base_url.py +12 -0
- synth_ai/v0/lm/__init__.py +51 -0
- synth_ai/{lm → v0/lm}/caching/ephemeral.py +2 -2
- synth_ai/{lm → v0/lm}/caching/handler.py +4 -4
- synth_ai/{lm → v0/lm}/caching/initialize.py +1 -1
- synth_ai/{lm → v0/lm}/caching/persistent.py +1 -1
- synth_ai/{lm → v0/lm}/config.py +6 -1
- synth_ai/{lm → v0/lm}/core/all.py +9 -9
- synth_ai/{lm → v0/lm}/core/main.py +6 -6
- synth_ai/{lm → v0/lm}/core/main_v3.py +10 -10
- synth_ai/{lm → v0/lm}/core/synth_models.py +2 -14
- synth_ai/{lm → v0/lm}/core/vendor_clients.py +2 -2
- synth_ai/{lm → v0/lm}/overrides.py +2 -2
- synth_ai/{lm → v0/lm}/provider_support/anthropic.py +4 -4
- synth_ai/{lm → v0/lm}/provider_support/openai.py +5 -5
- synth_ai/{lm → v0/lm}/structured_outputs/handler.py +5 -5
- synth_ai/{lm → v0/lm}/structured_outputs/rehabilitate.py +1 -1
- synth_ai/{lm → v0/lm}/vendors/core/anthropic_api.py +9 -9
- synth_ai/{lm → v0/lm}/vendors/core/gemini_api.py +5 -5
- synth_ai/{lm → v0/lm}/vendors/core/mistral_api.py +5 -5
- synth_ai/{lm → v0/lm}/vendors/core/openai_api.py +10 -10
- synth_ai/{lm → v0/lm}/vendors/openai_standard.py +8 -8
- synth_ai/{lm → v0/lm}/vendors/openai_standard_responses.py +2 -2
- synth_ai/{lm → v0/lm}/vendors/supported/custom_endpoint.py +3 -3
- synth_ai/{lm → v0/lm}/vendors/supported/deepseek.py +2 -2
- synth_ai/{lm → v0/lm}/vendors/supported/grok.py +2 -2
- synth_ai/{lm → v0/lm}/vendors/supported/groq.py +1 -1
- synth_ai/{lm → v0/lm}/vendors/supported/ollama.py +1 -1
- synth_ai/{lm → v0/lm}/vendors/supported/openrouter.py +3 -3
- synth_ai/{lm → v0/lm}/vendors/supported/together.py +1 -1
- synth_ai/{lm → v0/lm}/vendors/synth_client.py +1 -1
- synth_ai/v0/tracing_v3/__init__.py +10 -0
- synth_ai/v0/tracing_v3/abstractions.py +3 -0
- synth_ai/v0/tracing_v3/decorators.py +3 -0
- synth_ai/v0/tracing_v3/llm_call_record_helpers.py +3 -0
- synth_ai/v0/tracing_v3/session_tracer.py +3 -0
- {synth_ai-0.2.9.dev7.dist-info → synth_ai-0.2.10.dist-info}/METADATA +10 -7
- {synth_ai-0.2.9.dev7.dist-info → synth_ai-0.2.10.dist-info}/RECORD +269 -233
- examples/common_old/backend.py +0 -20
- examples/evals_old/README.md +0 -98
- examples/evals_old/__init__.py +0 -6
- examples/evals_old/compare_models.py +0 -1038
- examples/evals_old/example_log.md +0 -145
- examples/evals_old/run_demo.sh +0 -126
- examples/evals_old/trace_analysis.py +0 -270
- examples/finetuning_old/_backup_synth_qwen/config.toml +0 -29
- examples/finetuning_old/_backup_synth_qwen/example_log.md +0 -324
- examples/finetuning_old/_backup_synth_qwen/filter_traces.py +0 -60
- examples/finetuning_old/_backup_synth_qwen/filter_traces_achievements.py +0 -243
- examples/finetuning_old/_backup_synth_qwen/purge_v3_traces.py +0 -109
- examples/finetuning_old/_backup_synth_qwen/react_agent_lm.py +0 -1924
- examples/finetuning_old/_backup_synth_qwen/readme.md +0 -49
- examples/finetuning_old/_backup_synth_qwen/run_crafter_qwen4b.py +0 -114
- examples/finetuning_old/_backup_synth_qwen/run_demo.sh +0 -195
- examples/finetuning_old/_backup_synth_qwen/sft_kickoff.py +0 -119
- examples/finetuning_old/synth_qwen_v1/README.md +0 -68
- examples/finetuning_old/synth_qwen_v1/filter_traces.py +0 -60
- examples/finetuning_old/synth_qwen_v1/filter_traces_achievements.py +0 -243
- examples/finetuning_old/synth_qwen_v1/finetune.py +0 -46
- examples/finetuning_old/synth_qwen_v1/hello_ft_model.py +0 -71
- examples/finetuning_old/synth_qwen_v1/infer.py +0 -36
- examples/finetuning_old/synth_qwen_v1/poll.py +0 -46
- examples/finetuning_old/synth_qwen_v1/prepare_data.py +0 -35
- examples/finetuning_old/synth_qwen_v1/purge_v3_traces.py +0 -109
- examples/finetuning_old/synth_qwen_v1/react_agent_lm.py +0 -1933
- examples/finetuning_old/synth_qwen_v1/run_crafter_sft_job.py +0 -210
- examples/finetuning_old/synth_qwen_v1/run_ft_job.py +0 -237
- examples/finetuning_old/synth_qwen_v1/upload_data.py +0 -34
- examples/finetuning_old/synth_qwen_v1/util.py +0 -152
- examples/rl_old/task_app.py +0 -1131
- synth_ai/experimental/synth_oss.py +0 -445
- synth_ai/learning/filtering.py +0 -0
- synth_ai/learning/offline/dpo.py +0 -0
- synth_ai/learning/offline/providers.py +0 -7
- synth_ai/learning/offline/sft.py +0 -0
- synth_ai/learning/offline/shared.py +0 -0
- synth_ai/learning/online/grpo.py +0 -0
- synth_ai/learning/online/irft.py +0 -0
- synth_ai/learning/prompts/banking77_injection_eval.py +0 -168
- synth_ai/learning/prompts/gepa.py +0 -0
- synth_ai/learning/prompts/hello_world_in_context_injection_ex.py +0 -211
- synth_ai/learning/prompts/mipro.py +0 -289
- synth_ai/learning/prompts/random_search.py +0 -249
- synth_ai/learning/prompts/run_mipro_banking77.py +0 -172
- synth_ai/learning/prompts/run_random_search_banking77.py +0 -329
- synth_ai/rl/secrets.py +0 -19
- synth_ai/scripts/verify_rewards.py +0 -100
- synth_ai/tracing/__init__.py +0 -30
- synth_ai/tracing_v1/__init__.py +0 -33
- synth_ai/tracing_v3/turso/__init__.py +0 -25
- synth_ai/tracing_v3/turso/manager.py +0 -838
- synth_ai/zyk/__init__.py +0 -30
- /synth_ai/{lm → v0/lm}/caching/__init__.py +0 -0
- /synth_ai/{lm → v0/lm}/caching/constants.py +0 -0
- /synth_ai/{lm → v0/lm}/caching/dbs.py +0 -0
- /synth_ai/{lm → v0/lm}/constants.py +0 -0
- /synth_ai/{lm → v0/lm}/core/__init__.py +0 -0
- /synth_ai/{lm → v0/lm}/core/exceptions.py +0 -0
- /synth_ai/{lm → v0/lm}/cost/__init__.py +0 -0
- /synth_ai/{lm → v0/lm}/cost/monitor.py +0 -0
- /synth_ai/{lm → v0/lm}/cost/statefulness.py +0 -0
- /synth_ai/{lm → v0/lm}/injection.py +0 -0
- /synth_ai/{lm → v0/lm}/provider_support/__init__.py +0 -0
- /synth_ai/{lm → v0/lm}/provider_support/suppress_logging.py +0 -0
- /synth_ai/{lm → v0/lm}/structured_outputs/__init__.py +0 -0
- /synth_ai/{lm → v0/lm}/structured_outputs/inject.py +0 -0
- /synth_ai/{lm → v0/lm}/tools/__init__.py +0 -0
- /synth_ai/{lm → v0/lm}/tools/base.py +0 -0
- /synth_ai/{lm → v0/lm}/unified_interface.py +0 -0
- /synth_ai/{lm → v0/lm}/vendors/__init__.py +0 -0
- /synth_ai/{lm → v0/lm}/vendors/base.py +0 -0
- /synth_ai/{lm → v0/lm}/vendors/core/__init__.py +0 -0
- /synth_ai/{lm → v0/lm}/vendors/core/synth_dev_api.py +0 -0
- /synth_ai/{lm → v0/lm}/vendors/local/__init__.py +0 -0
- /synth_ai/{lm → v0/lm}/vendors/local/ollama.py +0 -0
- /synth_ai/{lm → v0/lm}/vendors/retries.py +0 -0
- /synth_ai/{lm → v0/lm}/vendors/supported/__init__.py +0 -0
- /synth_ai/{lm → v0/lm}/warmup.py +0 -0
- {synth_ai-0.2.9.dev7.dist-info → synth_ai-0.2.10.dist-info}/WHEEL +0 -0
- {synth_ai-0.2.9.dev7.dist-info → synth_ai-0.2.10.dist-info}/entry_points.txt +0 -0
- {synth_ai-0.2.9.dev7.dist-info → synth_ai-0.2.10.dist-info}/licenses/LICENSE +0 -0
- {synth_ai-0.2.9.dev7.dist-info → synth_ai-0.2.10.dist-info}/top_level.txt +0 -0
synth_ai/learning/validators.py
CHANGED
|
@@ -1,40 +1,37 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from pathlib import Path
|
|
4
3
|
import json
|
|
5
|
-
from
|
|
6
|
-
from
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from synth_ai.learning.sft import SFTDataError, parse_jsonl_line
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
def validate_training_jsonl(path: str | Path, *, sample_lines: int = 50) -> None:
|
|
10
11
|
p = Path(path)
|
|
11
12
|
if not p.exists():
|
|
12
13
|
raise FileNotFoundError(str(p))
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
|
|
15
|
+
max_samples = max(1, sample_lines)
|
|
16
|
+
non_empty_lines = 0
|
|
17
|
+
|
|
18
|
+
with p.open("r", encoding="utf-8") as fh:
|
|
19
|
+
for lineno, raw_line in enumerate(fh, start=1):
|
|
20
|
+
stripped = raw_line.strip()
|
|
21
|
+
if not stripped:
|
|
22
|
+
continue
|
|
23
|
+
non_empty_lines += 1
|
|
24
|
+
if non_empty_lines > max_samples:
|
|
25
|
+
break
|
|
26
|
+
try:
|
|
27
|
+
parse_jsonl_line(stripped, min_messages=2)
|
|
28
|
+
except json.JSONDecodeError as exc:
|
|
29
|
+
raise ValueError(f"invalid json on line {lineno}: {exc}") from exc
|
|
30
|
+
except SFTDataError as exc:
|
|
31
|
+
raise ValueError(f"line {lineno}: {exc}") from exc
|
|
32
|
+
|
|
33
|
+
if non_empty_lines == 0:
|
|
15
34
|
raise ValueError("empty JSONL")
|
|
16
|
-
for i, line in enumerate(lines[: max(1, sample_lines)], start=1):
|
|
17
|
-
if not line.strip():
|
|
18
|
-
continue
|
|
19
|
-
try:
|
|
20
|
-
obj = json.loads(line)
|
|
21
|
-
except Exception as e:
|
|
22
|
-
raise ValueError(f"invalid json on line {i}: {e}") from e
|
|
23
|
-
msgs = obj.get("messages")
|
|
24
|
-
if not isinstance(msgs, list) or len(msgs) < 2:
|
|
25
|
-
raise ValueError(f"line {i}: missing messages[] with at least 2 turns")
|
|
26
|
-
roles = [m.get("role") for m in msgs if isinstance(m, dict)]
|
|
27
|
-
if not roles or not isinstance(roles[0], str):
|
|
28
|
-
raise ValueError(f"line {i}: missing first role")
|
|
29
|
-
for m in msgs:
|
|
30
|
-
if not isinstance(m, dict):
|
|
31
|
-
raise ValueError(f"line {i}: non-dict message")
|
|
32
|
-
if (
|
|
33
|
-
not isinstance(m.get("role"), str)
|
|
34
|
-
or not isinstance(m.get("content"), str)
|
|
35
|
-
or not m["content"].strip()
|
|
36
|
-
):
|
|
37
|
-
raise ValueError(f"line {i}: invalid role/content")
|
|
38
35
|
|
|
39
36
|
|
|
40
37
|
def validate_task_app_url(url: str, *, name: str = "TASK_APP_BASE_URL") -> None:
|
|
@@ -43,7 +40,7 @@ def validate_task_app_url(url: str, *, name: str = "TASK_APP_BASE_URL") -> None:
|
|
|
43
40
|
_vt(url, name=name)
|
|
44
41
|
|
|
45
42
|
|
|
46
|
-
def validate_trainer_cfg_rl(trainer:
|
|
43
|
+
def validate_trainer_cfg_rl(trainer: dict[str, Any]) -> None:
|
|
47
44
|
bs = int(trainer.get("batch_size", 1))
|
|
48
45
|
gs = int(trainer.get("group_size", 2))
|
|
49
46
|
if bs < 1:
|
synth_ai/lm/__init__.py
CHANGED
|
@@ -1,51 +1,25 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Synth AI Language Model Interface.
|
|
1
|
+
"""Deprecated shim forwarding to synth_ai.v0.lm."""
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import importlib as _importlib
|
|
4
|
+
import pkgutil as _pkgutil
|
|
5
|
+
import sys as _sys
|
|
6
|
+
from pathlib import Path as _Path
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
from .unified_interface import (
|
|
10
|
-
OpenAIProvider,
|
|
11
|
-
SynthProvider,
|
|
12
|
-
UnifiedLMClient,
|
|
13
|
-
UnifiedLMProvider,
|
|
14
|
-
create_provider,
|
|
15
|
-
)
|
|
16
|
-
from .vendors.synth_client import (
|
|
17
|
-
AsyncSynthClient,
|
|
18
|
-
SyncSynthClient,
|
|
19
|
-
create_async_client,
|
|
20
|
-
create_chat_completion_async,
|
|
21
|
-
create_chat_completion_sync,
|
|
22
|
-
create_sync_client,
|
|
23
|
-
)
|
|
24
|
-
from .warmup import get_warmup_status, warmup_synth_model
|
|
8
|
+
_TARGET_PREFIX = "synth_ai.v0.lm"
|
|
9
|
+
_ALIAS_PREFIX = __name__
|
|
25
10
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"SynthConfig",
|
|
29
|
-
"OpenAIConfig",
|
|
30
|
-
# Warmup utilities
|
|
31
|
-
"warmup_synth_model",
|
|
32
|
-
"get_warmup_status",
|
|
33
|
-
# Unified interface
|
|
34
|
-
"UnifiedLMProvider",
|
|
35
|
-
"OpenAIProvider",
|
|
36
|
-
"SynthProvider",
|
|
37
|
-
"UnifiedLMClient",
|
|
38
|
-
"create_provider",
|
|
39
|
-
# Synth client
|
|
40
|
-
"AsyncSynthClient",
|
|
41
|
-
"SyncSynthClient",
|
|
42
|
-
"create_async_client",
|
|
43
|
-
"create_sync_client",
|
|
44
|
-
"create_chat_completion_async",
|
|
45
|
-
"create_chat_completion_sync",
|
|
46
|
-
# Core LM class
|
|
47
|
-
"LM",
|
|
48
|
-
]
|
|
11
|
+
_alias_path = _Path(__file__).resolve().parents[1] / "v0" / "lm"
|
|
12
|
+
__path__ = [str(_alias_path)] # type: ignore[assignment]
|
|
49
13
|
|
|
50
|
-
|
|
51
|
-
|
|
14
|
+
_pkg = _importlib.import_module(_TARGET_PREFIX)
|
|
15
|
+
_sys.modules[_ALIAS_PREFIX] = _pkg
|
|
16
|
+
|
|
17
|
+
for _finder, _name, _ispkg in _pkgutil.walk_packages(_pkg.__path__, prefix=_TARGET_PREFIX + "."): # type: ignore[attr-defined]
|
|
18
|
+
try:
|
|
19
|
+
_module = _importlib.import_module(_name)
|
|
20
|
+
except Exception: # pragma: no cover - best effort
|
|
21
|
+
continue
|
|
22
|
+
_alias = _ALIAS_PREFIX + _name[len(_TARGET_PREFIX) :]
|
|
23
|
+
_sys.modules[_alias] = _module
|
|
24
|
+
|
|
25
|
+
del _finder, _name, _ispkg, _module, _alias, _TARGET_PREFIX, _ALIAS_PREFIX, _alias_path
|
synth_ai/task/__init__.py
CHANGED
|
@@ -1,59 +1,57 @@
|
|
|
1
|
-
from .
|
|
2
|
-
|
|
1
|
+
from .auth import (
|
|
2
|
+
is_api_key_header_authorized,
|
|
3
|
+
normalize_environment_api_key,
|
|
4
|
+
require_api_key_dependency,
|
|
5
|
+
)
|
|
6
|
+
from .client import TaskAppClient
|
|
3
7
|
from .contracts import (
|
|
4
|
-
TaskAppContract,
|
|
5
|
-
TaskAppEndpoints,
|
|
6
8
|
RolloutEnvSpec,
|
|
9
|
+
RolloutMetrics,
|
|
7
10
|
RolloutPolicySpec,
|
|
8
11
|
RolloutRecordConfig,
|
|
9
|
-
RolloutSafetyConfig,
|
|
10
12
|
RolloutRequest,
|
|
11
13
|
RolloutResponse,
|
|
12
|
-
|
|
14
|
+
RolloutSafetyConfig,
|
|
13
15
|
RolloutStep,
|
|
14
|
-
|
|
16
|
+
RolloutTrajectory,
|
|
17
|
+
TaskAppContract,
|
|
18
|
+
TaskAppEndpoints,
|
|
15
19
|
TaskInfo,
|
|
16
20
|
)
|
|
21
|
+
from .datasets import TaskDatasetRegistry, TaskDatasetSpec
|
|
22
|
+
from .errors import error_payload, http_exception, json_error_response
|
|
23
|
+
from .health import task_app_health
|
|
17
24
|
from .json import to_jsonable
|
|
18
|
-
from .auth import (
|
|
19
|
-
normalize_environment_api_key,
|
|
20
|
-
is_api_key_header_authorized,
|
|
21
|
-
require_api_key_dependency,
|
|
22
|
-
)
|
|
23
|
-
from .vendors import (
|
|
24
|
-
normalize_vendor_keys,
|
|
25
|
-
get_openai_key_or_503,
|
|
26
|
-
get_groq_key_or_503,
|
|
27
|
-
)
|
|
28
25
|
from .proxy import (
|
|
29
26
|
INTERACT_TOOL_SCHEMA,
|
|
30
|
-
prepare_for_openai,
|
|
31
|
-
prepare_for_groq,
|
|
32
|
-
inject_system_hint,
|
|
33
27
|
extract_message_text,
|
|
28
|
+
inject_system_hint,
|
|
34
29
|
parse_tool_call_from_text,
|
|
30
|
+
prepare_for_groq,
|
|
31
|
+
prepare_for_openai,
|
|
35
32
|
synthesize_tool_call_if_missing,
|
|
36
33
|
)
|
|
37
|
-
from .datasets import TaskDatasetSpec, TaskDatasetRegistry
|
|
38
34
|
from .rubrics import (
|
|
39
35
|
Criterion,
|
|
40
36
|
Rubric,
|
|
41
|
-
load_rubric,
|
|
42
37
|
blend_rubrics,
|
|
38
|
+
load_rubric,
|
|
43
39
|
score_events_against_rubric,
|
|
44
40
|
score_outcome_against_rubric,
|
|
45
41
|
)
|
|
46
|
-
from .client import TaskAppClient
|
|
47
|
-
from .errors import error_payload, http_exception, json_error_response
|
|
48
|
-
|
|
49
|
-
|
|
50
42
|
from .server import (
|
|
51
|
-
TaskAppConfig,
|
|
52
43
|
ProxyConfig,
|
|
53
44
|
RubricBundle,
|
|
45
|
+
TaskAppConfig,
|
|
54
46
|
create_task_app,
|
|
55
47
|
run_task_app,
|
|
56
48
|
)
|
|
49
|
+
from .validators import validate_task_app_url
|
|
50
|
+
from .vendors import (
|
|
51
|
+
get_groq_key_or_503,
|
|
52
|
+
get_openai_key_or_503,
|
|
53
|
+
normalize_vendor_keys,
|
|
54
|
+
)
|
|
57
55
|
|
|
58
56
|
__all__ = [
|
|
59
57
|
"validate_task_app_url",
|
synth_ai/task/apps/__init__.py
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
1
|
"""Registry for Task Apps exposed via the shared FastAPI harness."""
|
|
4
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
5
|
import importlib
|
|
6
|
-
import os
|
|
7
6
|
import sys
|
|
7
|
+
from collections.abc import Callable, Iterable, Sequence
|
|
8
8
|
from dataclasses import dataclass, field
|
|
9
9
|
from pathlib import Path
|
|
10
|
-
from typing import Callable, Dict, Iterable, List, Sequence
|
|
11
10
|
|
|
12
11
|
from ..server import TaskAppConfig
|
|
13
12
|
|
|
@@ -45,8 +44,8 @@ class TaskAppRegistry:
|
|
|
45
44
|
"""In-memory registry of known task apps."""
|
|
46
45
|
|
|
47
46
|
def __init__(self) -> None:
|
|
48
|
-
self._entries:
|
|
49
|
-
self._alias_to_id:
|
|
47
|
+
self._entries: dict[str, TaskAppEntry] = {}
|
|
48
|
+
self._alias_to_id: dict[str, str] = {}
|
|
50
49
|
|
|
51
50
|
def register(self, entry: TaskAppEntry) -> None:
|
|
52
51
|
if entry.app_id in self._entries:
|
|
@@ -63,7 +62,7 @@ class TaskAppRegistry:
|
|
|
63
62
|
raise KeyError(f"Unknown task app id: {app_id}")
|
|
64
63
|
return self._entries[resolved]
|
|
65
64
|
|
|
66
|
-
def list(self) ->
|
|
65
|
+
def list(self) -> list[TaskAppEntry]:
|
|
67
66
|
return sorted(self._entries.values(), key=lambda entry: entry.app_id)
|
|
68
67
|
|
|
69
68
|
def __iter__(self) -> Iterable[TaskAppEntry]:
|
|
@@ -116,7 +115,7 @@ def discover_task_apps_from_cwd() -> None:
|
|
|
116
115
|
try:
|
|
117
116
|
# Import the module to trigger registration
|
|
118
117
|
importlib.import_module(module_name)
|
|
119
|
-
except Exception
|
|
118
|
+
except Exception:
|
|
120
119
|
# Silently skip modules that can't be imported
|
|
121
120
|
# This allows for graceful handling of missing dependencies
|
|
122
121
|
continue
|
synth_ai/task/auth.py
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
1
|
"""Authentication helpers shared by Task Apps."""
|
|
4
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
5
|
import os
|
|
6
|
-
from
|
|
6
|
+
from collections.abc import Iterable
|
|
7
|
+
from contextlib import suppress
|
|
8
|
+
from typing import Any
|
|
7
9
|
|
|
8
10
|
from .errors import http_exception
|
|
9
11
|
|
|
@@ -24,7 +26,7 @@ def _mask(value: str, *, prefix: int = 4) -> str:
|
|
|
24
26
|
return f"{visible}{'…' if len(value) > prefix else ''}"
|
|
25
27
|
|
|
26
28
|
|
|
27
|
-
def normalize_environment_api_key() ->
|
|
29
|
+
def normalize_environment_api_key() -> str | None:
|
|
28
30
|
"""Ensure `ENVIRONMENT_API_KEY` is populated from dev fallbacks.
|
|
29
31
|
|
|
30
32
|
Returns the resolved key (if any) so callers can branch on configuration.
|
|
@@ -45,7 +47,7 @@ def normalize_environment_api_key() -> Optional[str]:
|
|
|
45
47
|
return None
|
|
46
48
|
|
|
47
49
|
|
|
48
|
-
def allowed_environment_api_keys() ->
|
|
50
|
+
def allowed_environment_api_keys() -> set[str]:
|
|
49
51
|
"""Return the set of valid environment API keys for this Task App.
|
|
50
52
|
|
|
51
53
|
Includes:
|
|
@@ -135,7 +137,7 @@ def require_api_key_dependency(request: Any) -> None:
|
|
|
135
137
|
bearer.append(a.split(" ", 1)[1].strip())
|
|
136
138
|
candidates = _split_csv(single + multi + bearer)
|
|
137
139
|
if not any(candidate in allowed for candidate in candidates):
|
|
138
|
-
|
|
140
|
+
with suppress(Exception):
|
|
139
141
|
print(
|
|
140
142
|
{
|
|
141
143
|
"task_auth_failed": True,
|
|
@@ -149,8 +151,6 @@ def require_api_key_dependency(request: Any) -> None:
|
|
|
149
151
|
},
|
|
150
152
|
flush=True,
|
|
151
153
|
)
|
|
152
|
-
except Exception:
|
|
153
|
-
pass
|
|
154
154
|
# Use 400 to make failures unmistakable during preflight
|
|
155
155
|
raise http_exception(
|
|
156
156
|
400,
|
synth_ai/task/client.py
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
1
|
"""Async HTTP client for interacting with Task Apps."""
|
|
4
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
5
|
import asyncio
|
|
6
|
-
from typing import Any, Dict, Iterable, List, Optional
|
|
7
6
|
import os
|
|
7
|
+
from typing import Any
|
|
8
8
|
|
|
9
9
|
import httpx
|
|
10
10
|
from pydantic import BaseModel
|
|
@@ -37,7 +37,7 @@ class TaskAppClient:
|
|
|
37
37
|
self._client: httpx.AsyncClient | None = None
|
|
38
38
|
self.env = _TaskAppEnvironmentClient(self)
|
|
39
39
|
|
|
40
|
-
async def __aenter__(self) ->
|
|
40
|
+
async def __aenter__(self) -> TaskAppClient:
|
|
41
41
|
await self._ensure_client()
|
|
42
42
|
return self
|
|
43
43
|
|
|
@@ -53,8 +53,8 @@ class TaskAppClient:
|
|
|
53
53
|
)
|
|
54
54
|
return self._client
|
|
55
55
|
|
|
56
|
-
def _headers(self) ->
|
|
57
|
-
headers:
|
|
56
|
+
def _headers(self) -> dict[str, str]:
|
|
57
|
+
headers: dict[str, str] = {}
|
|
58
58
|
# Primary key
|
|
59
59
|
primary = (self.api_key or "").strip()
|
|
60
60
|
if primary:
|
|
@@ -85,7 +85,7 @@ class TaskAppClient:
|
|
|
85
85
|
method: str,
|
|
86
86
|
path: str,
|
|
87
87
|
*,
|
|
88
|
-
params:
|
|
88
|
+
params: dict[str, Any] | list[tuple[str, Any]] | None = None,
|
|
89
89
|
json_payload: Any = None,
|
|
90
90
|
) -> httpx.Response:
|
|
91
91
|
client = await self._ensure_client()
|
|
@@ -118,16 +118,16 @@ class TaskAppClient:
|
|
|
118
118
|
raise last_exc
|
|
119
119
|
raise RuntimeError("Unreachable code in TaskAppClient._request")
|
|
120
120
|
|
|
121
|
-
async def health(self) ->
|
|
121
|
+
async def health(self) -> dict[str, Any]:
|
|
122
122
|
response = await self._request("GET", "/health")
|
|
123
123
|
return response.json()
|
|
124
124
|
|
|
125
|
-
async def info(self) ->
|
|
125
|
+
async def info(self) -> dict[str, Any]:
|
|
126
126
|
response = await self._request("GET", "/info")
|
|
127
127
|
return response.json()
|
|
128
128
|
|
|
129
129
|
async def task_info(self, seeds: list[int] | None = None) -> TaskInfo | list[TaskInfo]:
|
|
130
|
-
params:
|
|
130
|
+
params: list[tuple[str, Any]] | None = None
|
|
131
131
|
if seeds:
|
|
132
132
|
params = [("seed", seed) for seed in seeds]
|
|
133
133
|
response = await self._request("GET", "/task_info", params=params)
|
|
@@ -146,21 +146,21 @@ class _TaskAppEnvironmentClient:
|
|
|
146
146
|
def __init__(self, client: TaskAppClient) -> None:
|
|
147
147
|
self._client = client
|
|
148
148
|
|
|
149
|
-
async def initialize(self, env_name: str, payload:
|
|
149
|
+
async def initialize(self, env_name: str, payload: dict[str, Any]) -> dict[str, Any]:
|
|
150
150
|
response = await self._client._request(
|
|
151
151
|
"POST", f"/env/{env_name}/initialize", json_payload=payload
|
|
152
152
|
)
|
|
153
153
|
return response.json()
|
|
154
154
|
|
|
155
|
-
async def step(self, env_name: str, payload:
|
|
155
|
+
async def step(self, env_name: str, payload: dict[str, Any]) -> dict[str, Any]:
|
|
156
156
|
response = await self._client._request(
|
|
157
157
|
"POST", f"/env/{env_name}/step", json_payload=payload
|
|
158
158
|
)
|
|
159
159
|
return response.json()
|
|
160
160
|
|
|
161
161
|
async def terminate(
|
|
162
|
-
self, env_name: str, payload:
|
|
163
|
-
) ->
|
|
162
|
+
self, env_name: str, payload: dict[str, Any] | None = None
|
|
163
|
+
) -> dict[str, Any]:
|
|
164
164
|
response = await self._client._request(
|
|
165
165
|
"POST", f"/env/{env_name}/terminate", json_payload=payload or {}
|
|
166
166
|
)
|
synth_ai/task/contracts.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
|
-
from typing import
|
|
4
|
+
from typing import Any, Literal
|
|
5
|
+
|
|
5
6
|
from pydantic import BaseModel, Field
|
|
6
7
|
|
|
7
8
|
|
|
@@ -40,7 +41,7 @@ class TaskAppContract:
|
|
|
40
41
|
"""
|
|
41
42
|
|
|
42
43
|
base_url: str
|
|
43
|
-
env_name:
|
|
44
|
+
env_name: str | None = None
|
|
44
45
|
requires_api_key_header: bool = True
|
|
45
46
|
|
|
46
47
|
|
|
@@ -48,16 +49,16 @@ class TaskAppContract:
|
|
|
48
49
|
|
|
49
50
|
|
|
50
51
|
class RolloutEnvSpec(BaseModel):
|
|
51
|
-
env_id:
|
|
52
|
-
env_name:
|
|
53
|
-
config:
|
|
54
|
-
seed:
|
|
52
|
+
env_id: str | None = None
|
|
53
|
+
env_name: str | None = None
|
|
54
|
+
config: dict[str, Any] = Field(default_factory=dict)
|
|
55
|
+
seed: int | None = None
|
|
55
56
|
|
|
56
57
|
|
|
57
58
|
class RolloutPolicySpec(BaseModel):
|
|
58
|
-
policy_id:
|
|
59
|
-
policy_name:
|
|
60
|
-
config:
|
|
59
|
+
policy_id: str | None = None
|
|
60
|
+
policy_name: str | None = None
|
|
61
|
+
config: dict[str, Any] = Field(default_factory=dict)
|
|
61
62
|
|
|
62
63
|
|
|
63
64
|
class RolloutRecordConfig(BaseModel):
|
|
@@ -77,60 +78,60 @@ class RolloutRequest(BaseModel):
|
|
|
77
78
|
run_id: str
|
|
78
79
|
env: RolloutEnvSpec
|
|
79
80
|
policy: RolloutPolicySpec
|
|
80
|
-
ops:
|
|
81
|
+
ops: list[dict[str, Any]] | list[str]
|
|
81
82
|
record: RolloutRecordConfig = RolloutRecordConfig()
|
|
82
83
|
on_done: str = "reset"
|
|
83
84
|
safety: RolloutSafetyConfig = RolloutSafetyConfig()
|
|
84
|
-
training_session_id:
|
|
85
|
-
synth_base_url:
|
|
85
|
+
training_session_id: str | None = None
|
|
86
|
+
synth_base_url: str | None = None
|
|
86
87
|
|
|
87
88
|
|
|
88
89
|
class RolloutStep(BaseModel):
|
|
89
|
-
obs:
|
|
90
|
-
tool_calls:
|
|
91
|
-
reward:
|
|
90
|
+
obs: dict[str, Any]
|
|
91
|
+
tool_calls: list[dict[str, Any]]
|
|
92
|
+
reward: float | None = None
|
|
92
93
|
done: bool = False
|
|
93
|
-
truncated:
|
|
94
|
-
info:
|
|
94
|
+
truncated: bool | None = None
|
|
95
|
+
info: dict[str, Any] | None = None
|
|
95
96
|
|
|
96
97
|
|
|
97
98
|
class RolloutTrajectory(BaseModel):
|
|
98
99
|
env_id: str
|
|
99
100
|
policy_id: str
|
|
100
|
-
steps:
|
|
101
|
-
final:
|
|
101
|
+
steps: list[RolloutStep]
|
|
102
|
+
final: dict[str, Any] | None = None
|
|
102
103
|
length: int
|
|
103
104
|
|
|
104
105
|
|
|
105
106
|
class RolloutMetrics(BaseModel):
|
|
106
|
-
episode_returns:
|
|
107
|
+
episode_returns: list[float]
|
|
107
108
|
mean_return: float
|
|
108
109
|
num_steps: int
|
|
109
110
|
num_episodes: int = 0
|
|
110
|
-
outcome_score:
|
|
111
|
-
events_score:
|
|
112
|
-
details:
|
|
111
|
+
outcome_score: float | None = None
|
|
112
|
+
events_score: float | None = None
|
|
113
|
+
details: dict[str, Any] = Field(default_factory=dict)
|
|
113
114
|
|
|
114
115
|
|
|
115
116
|
class RolloutResponse(BaseModel):
|
|
116
117
|
run_id: str
|
|
117
|
-
trajectories:
|
|
118
|
-
branches:
|
|
118
|
+
trajectories: list[RolloutTrajectory]
|
|
119
|
+
branches: dict[str, list[str]] = Field(default_factory=dict)
|
|
119
120
|
metrics: RolloutMetrics
|
|
120
121
|
aborted: bool = False
|
|
121
122
|
ops_executed: int = 0
|
|
122
|
-
trace:
|
|
123
|
+
trace: dict[str, Any] | None = None
|
|
123
124
|
|
|
124
125
|
|
|
125
126
|
class TaskInfo(BaseModel):
|
|
126
127
|
"""Static metadata describing the capabilities of a Task App task."""
|
|
127
128
|
|
|
128
|
-
task:
|
|
129
|
-
environments:
|
|
130
|
-
action_space:
|
|
131
|
-
observation:
|
|
132
|
-
dataset:
|
|
133
|
-
rubric:
|
|
134
|
-
inference:
|
|
135
|
-
capabilities:
|
|
136
|
-
limits:
|
|
129
|
+
task: dict[str, Any]
|
|
130
|
+
environments: list[str]
|
|
131
|
+
action_space: dict[str, Any]
|
|
132
|
+
observation: dict[str, Any]
|
|
133
|
+
dataset: dict[str, Any]
|
|
134
|
+
rubric: dict[str, Any]
|
|
135
|
+
inference: dict[str, Any]
|
|
136
|
+
capabilities: dict[str, Any]
|
|
137
|
+
limits: dict[str, Any]
|
synth_ai/task/datasets.py
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
1
|
"""Dataset registry and helpers shared by Task Apps."""
|
|
4
2
|
|
|
5
|
-
from
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Callable, Hashable
|
|
6
|
+
from typing import Any
|
|
6
7
|
|
|
7
8
|
from pydantic import BaseModel, Field, field_validator
|
|
8
9
|
|
|
@@ -34,8 +35,8 @@ class TaskDatasetRegistry:
|
|
|
34
35
|
"""Lightweight registry mapping dataset specs to loader callables."""
|
|
35
36
|
|
|
36
37
|
def __init__(self) -> None:
|
|
37
|
-
self._entries:
|
|
38
|
-
self._cache:
|
|
38
|
+
self._entries: dict[str, tuple[TaskDatasetSpec, RegistryLoader, bool]] = {}
|
|
39
|
+
self._cache: dict[Hashable, Any] = {}
|
|
39
40
|
|
|
40
41
|
def register(
|
|
41
42
|
self, spec: TaskDatasetSpec, loader: RegistryLoader, *, cache: bool = True
|
synth_ai/task/errors.py
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
1
|
"""Error helpers used across Task App implementations."""
|
|
4
2
|
|
|
5
|
-
from
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
6
|
|
|
7
7
|
from .json import to_jsonable
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
def error_payload(
|
|
11
|
-
code: str, message: str, *, extra:
|
|
12
|
-
) ->
|
|
13
|
-
payload:
|
|
11
|
+
code: str, message: str, *, extra: dict[str, Any] | None = None
|
|
12
|
+
) -> dict[str, Any]:
|
|
13
|
+
payload: dict[str, Any] = {"error": {"code": code, "message": message}}
|
|
14
14
|
if extra:
|
|
15
15
|
payload["error"].update(extra)
|
|
16
16
|
return payload
|
|
@@ -21,8 +21,8 @@ def http_exception(
|
|
|
21
21
|
code: str,
|
|
22
22
|
message: str,
|
|
23
23
|
*,
|
|
24
|
-
extra:
|
|
25
|
-
headers:
|
|
24
|
+
extra: dict[str, Any] | None = None,
|
|
25
|
+
headers: dict[str, str] | None = None,
|
|
26
26
|
):
|
|
27
27
|
try:
|
|
28
28
|
from fastapi import HTTPException # type: ignore
|
|
@@ -38,8 +38,8 @@ def json_error_response(
|
|
|
38
38
|
code: str,
|
|
39
39
|
message: str,
|
|
40
40
|
*,
|
|
41
|
-
extra:
|
|
42
|
-
headers:
|
|
41
|
+
extra: dict[str, Any] | None = None,
|
|
42
|
+
headers: dict[str, str] | None = None,
|
|
43
43
|
):
|
|
44
44
|
try:
|
|
45
45
|
from fastapi.responses import JSONResponse # type: ignore
|
synth_ai/task/health.py
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
"""Helpers for probing Task App health endpoints."""
|
|
2
|
+
|
|
1
3
|
from __future__ import annotations
|
|
2
4
|
|
|
3
|
-
from typing import Any
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
4
7
|
import aiohttp
|
|
5
8
|
|
|
6
9
|
|
|
7
|
-
async def task_app_health(task_app_url: str) ->
|
|
10
|
+
async def task_app_health(task_app_url: str) -> dict[str, Any]:
|
|
8
11
|
"""Probe a Task App base URL for basic reachability.
|
|
9
12
|
|
|
10
13
|
Behavior:
|
|
@@ -12,15 +15,20 @@ async def task_app_health(task_app_url: str) -> Dict[str, Any]:
|
|
|
12
15
|
- Fallback to GET if HEAD is unsupported
|
|
13
16
|
- Returns {ok: bool, status?: int, error?: str}
|
|
14
17
|
"""
|
|
18
|
+
|
|
19
|
+
async def _try_request(session: aiohttp.ClientSession, method: str) -> dict[str, Any] | None:
|
|
20
|
+
request = getattr(session, method)
|
|
21
|
+
async with request(task_app_url, allow_redirects=True) as response:
|
|
22
|
+
if 200 <= response.status < 400:
|
|
23
|
+
return {"ok": True, "status": response.status}
|
|
24
|
+
return None
|
|
25
|
+
|
|
15
26
|
try:
|
|
16
27
|
async with aiohttp.ClientSession() as session:
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
async with session.get(task_app_url, allow_redirects=True) as r2:
|
|
22
|
-
if 200 <= r2.status < 400:
|
|
23
|
-
return {"ok": True, "status": r2.status}
|
|
28
|
+
for method in ("head", "get"):
|
|
29
|
+
result = await _try_request(session, method)
|
|
30
|
+
if result is not None:
|
|
31
|
+
return result
|
|
24
32
|
return {"ok": False, "status": None}
|
|
25
33
|
except Exception as e:
|
|
26
34
|
return {"ok": False, "error": f"{type(e).__name__}: {e}"}
|