synth-ai 0.2.13.dev1__py3-none-any.whl → 0.2.14__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/multi_step/configs/README_verilog_rl.md +77 -0
- examples/multi_step/configs/VERILOG_REWARDS.md +90 -0
- examples/multi_step/configs/VERILOG_RL_CHECKLIST.md +183 -0
- examples/multi_step/configs/crafter_eval_synth_qwen4b.toml +35 -0
- examples/multi_step/configs/crafter_eval_text_only_groq_qwen32b.toml +36 -0
- examples/multi_step/configs/crafter_rl_stepwise_hosted_judge.toml +17 -5
- examples/multi_step/configs/crafter_synth_backend.md +40 -0
- examples/multi_step/configs/verilog_eval_groq_qwen32b.toml +31 -0
- examples/multi_step/configs/verilog_eval_synth_qwen8b.toml +33 -0
- examples/multi_step/configs/verilog_rl_lora.toml +190 -0
- examples/multi_step/judges/crafter_backend_judge.py +220 -0
- examples/multi_step/judges/verilog_backend_judge.py +234 -0
- examples/multi_step/readme.md +48 -0
- examples/multi_step/verilog_rl_lora.md +218 -0
- examples/qwen_coder/configs/coder_lora_30b.toml +1 -1
- examples/sft/evaluate.py +2 -0
- examples/sft/generate_traces.py +2 -0
- examples/swe/task_app/grpo_swe_mini.py +56 -26
- examples/swe/task_app/hosted/rollout.py +42 -0
- examples/swe/task_app/hosted/test_service.py +5 -6
- examples/task_apps/IMAGE_ONLY_EVAL_QUICKSTART.md +258 -0
- examples/task_apps/TESTING.md +275 -0
- examples/task_apps/__init__.py +0 -0
- examples/task_apps/crafter/CREATE_SFT_DATASET.md +273 -0
- examples/task_apps/crafter/EVAL_IMAGE_ONLY_RESULTS.md +152 -0
- examples/task_apps/crafter/FILTER_COMMAND_STATUS.md +174 -0
- examples/task_apps/crafter/FILTER_COMMAND_SUCCESS.md +268 -0
- examples/task_apps/crafter/QUERY_EXAMPLES.md +203 -0
- examples/task_apps/crafter/README_IMAGE_ONLY_EVAL.md +316 -0
- examples/task_apps/crafter/__init__.py +0 -0
- examples/task_apps/crafter/eval_image_only_gpt4o.toml +28 -0
- examples/task_apps/crafter/eval_text_only_groq_llama.toml +36 -0
- examples/task_apps/crafter/filter_sft_dataset.toml +16 -0
- examples/task_apps/crafter/task_app/__init__.py +5 -0
- examples/{warming_up_to_rl → task_apps/crafter}/task_app/grpo_crafter.py +324 -21
- examples/{warming_up_to_rl → task_apps/crafter}/task_app/grpo_crafter_task_app.py +1 -1
- examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/environment.py +10 -0
- examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/policy.py +76 -7
- examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/react_agent.py +17 -2
- examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/inference/openai_client.py +25 -3
- examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/policy_routes.py +77 -4
- examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/rollout.py +117 -9
- examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/test_service.py +5 -6
- examples/task_apps/crafter/task_app/synth_envs_hosted/utils.py +218 -0
- examples/task_apps/dev/pokemon_emerald/__init__.py +2 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/README.md +811 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/__init__.py +120 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/action.py +160 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/memory.py +155 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/perception.py +69 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/planning.py +96 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/simple.py +1502 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/system_prompt.py +4 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/grab_map.py +68 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/manual.py +216 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/__init__.py +35 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/emerald_utils.py +631 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/emulator.py +1544 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/enums.py +1428 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/memory_reader.py +4848 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/types.py +41 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/utils.py +298 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pyproject.toml +95 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/run.py +204 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/server/__init__.py +0 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/server/app.py +2152 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/server/client.py +429 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/server/frame_server.py +155 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/README.md +78 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/__init__.py +0 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/run_tests.py +122 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_agent_direct.py +76 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_agent_prompts.py +413 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_battle_state_formatting.py +204 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_dialogue_detection.py +133 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_dialogue_detection_comprehensive.py +229 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_direct_agent_emulator.py +300 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_fps_adjustment_pytest.py +205 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_house_to_outside_direct.py +200 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_house_to_outside_transition.py +284 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_map_ground_truth_comparison.py +468 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_memory_map.py +575 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_server_map_validation.py +311 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_torchic_state.py +259 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/__init__.py +0 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/anticheat.py +372 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/checkpoint.py +296 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/error_handler.py +275 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/get_local_ip.py +22 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/helpers.py +44 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/llm_logger.py +514 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_formatter.py +415 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_stitcher.py +1763 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_stitcher_singleton.py +33 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_trimmer.py +106 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_visualizer.py +334 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/ocr_dialogue.py +1020 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/recording.py +188 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/state_formatter.py +1481 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/vlm.py +862 -0
- examples/task_apps/dev/pokemon_emerald/modal_app.py +114 -0
- examples/task_apps/dev/pokemon_emerald/task_app/README.md +81 -0
- examples/task_apps/dev/pokemon_emerald/task_app/__init__.py +6 -0
- examples/task_apps/dev/pokemon_emerald/task_app/pokemon_emerald.py +685 -0
- examples/task_apps/enron/__init__.py +1 -0
- examples/task_apps/enron/eval_groq_qwen32.toml +16 -0
- examples/task_apps/enron/filter_sft.toml +5 -0
- examples/task_apps/enron/task_app/README.md +14 -0
- examples/task_apps/enron/task_app/__init__.py +1 -0
- examples/task_apps/enron/task_app/grpo_enron.py +906 -0
- examples/task_apps/enron/task_app/grpo_enron_task_app.py +146 -0
- examples/task_apps/enron/tests/__init__.py +4 -0
- examples/task_apps/enron/tests/conftest.py +115 -0
- examples/task_apps/enron/tests/integration/__init__.py +4 -0
- examples/task_apps/enron/tests/integration/test_enron_eval.py +179 -0
- examples/task_apps/enron/tests/integration/test_enron_rollout.py +135 -0
- examples/task_apps/enron/tests/unit/__init__.py +4 -0
- examples/task_apps/enron/tests/unit/test_enron_environment.py +126 -0
- examples/task_apps/math/__init__.py +0 -0
- examples/{rl/task_app → task_apps/math}/math_single_step.py +19 -10
- examples/task_apps/pokemon_battle/__init__.py +2 -0
- examples/task_apps/pokemon_battle/modal_app.py +104 -0
- examples/task_apps/pokemon_battle/task_app/README.md +68 -0
- examples/task_apps/pokemon_battle/task_app/__init__.py +6 -0
- examples/task_apps/pokemon_battle/task_app/pokemon_showdown.py +932 -0
- examples/task_apps/pokemon_red/EVAL_IMAGE_ONLY_COMPLETE.md +283 -0
- examples/task_apps/pokemon_red/EVAL_IMAGE_ONLY_STATUS.md +155 -0
- examples/task_apps/pokemon_red/README.md +357 -0
- examples/task_apps/pokemon_red/README_IMAGE_ONLY_EVAL.md +415 -0
- examples/task_apps/pokemon_red/__init__.py +3 -0
- examples/task_apps/pokemon_red/eval_image_only_gpt4o.toml +29 -0
- examples/task_apps/pokemon_red/eval_pokemon_red_policy.py +225 -0
- examples/task_apps/pokemon_red/pallet_town_rl_config.toml +75 -0
- examples/task_apps/pokemon_red/task_app.py +799 -0
- examples/task_apps/pokemon_red/test_pallet_town_rewards.py +193 -0
- examples/task_apps/sokoban/README.md +307 -0
- examples/task_apps/sokoban/__init__.py +3 -0
- examples/task_apps/sokoban/eval_groq_qwen32.toml +16 -0
- examples/task_apps/sokoban/eval_openai_gpt5.toml +16 -0
- examples/task_apps/sokoban/filter_sft.toml +5 -0
- examples/task_apps/sokoban/task_app.py +1058 -0
- examples/task_apps/sokoban/tests/__init__.py +4 -0
- examples/task_apps/sokoban/tests/conftest.py +113 -0
- examples/task_apps/sokoban/tests/integration/__init__.py +4 -0
- examples/task_apps/sokoban/tests/integration/test_sokoban_eval.py +57 -0
- examples/task_apps/sokoban/tests/integration/test_sokoban_rollout.py +198 -0
- examples/task_apps/sokoban/tests/unit/__init__.py +4 -0
- examples/task_apps/sokoban/tests/unit/test_sokoban_environment.py +114 -0
- examples/task_apps/verilog/__init__.py +1 -0
- examples/task_apps/verilog/eval_groq_qwen32b.toml +24 -0
- examples/task_apps/verilog/filter_sft.toml +5 -0
- examples/task_apps/verilog/task_app/README.md +12 -0
- examples/task_apps/verilog/task_app/__init__.py +1 -0
- examples/task_apps/verilog/task_app/grpo_verilog.py +1166 -0
- examples/task_apps/verilog/task_app/grpo_verilog_task_app.py +145 -0
- examples/task_apps/verilog/tests/__init__.py +4 -0
- examples/task_apps/verilog/tests/conftest.py +115 -0
- examples/task_apps/verilog/tests/integration/__init__.py +4 -0
- examples/task_apps/verilog/tests/integration/test_verilog_eval.py +181 -0
- examples/task_apps/verilog/tests/integration/test_verilog_rollout.py +55 -0
- examples/task_apps/verilog/tests/unit/__init__.py +4 -0
- examples/task_apps/verilog/tests/unit/test_verilog_scoring.py +118 -0
- examples/vlm/crafter_openai_vlm_agent.py +4 -4
- examples/vlm/run_crafter_vlm_benchmark.py +4 -4
- examples/warming_up_to_rl/groq_test.py +2 -0
- examples/warming_up_to_rl/run_local_rollout.py +2 -0
- examples/warming_up_to_rl/run_local_rollout_modal.py +2 -0
- examples/warming_up_to_rl/run_local_rollout_parallel.py +2 -0
- examples/warming_up_to_rl/run_local_rollout_traced.py +2 -0
- examples/warming_up_to_rl/run_rollout_remote.py +2 -0
- examples/workflows/__init__.py +0 -0
- examples/workflows/math_rl/__init__.py +0 -0
- examples/workflows/math_rl/download_dataset.py +80 -0
- synth_ai/__init__.py +2 -2
- synth_ai/api/models/supported.py +1 -0
- synth_ai/api/train/builders.py +25 -11
- synth_ai/api/train/cli.py +12 -6
- synth_ai/api/train/configs/__init__.py +10 -10
- synth_ai/api/train/configs/rl.py +5 -4
- synth_ai/api/train/configs/sft.py +4 -3
- synth_ai/api/train/env_resolver.py +5 -2
- synth_ai/api/train/supported_algos.py +10 -5
- synth_ai/api/train/utils.py +7 -4
- synth_ai/cli/__init__.py +48 -59
- synth_ai/cli/_modal_wrapper.py +3 -2
- synth_ai/cli/_storage.py +4 -3
- synth_ai/cli/_validate_task_app.py +11 -0
- synth_ai/cli/balance.py +4 -3
- synth_ai/cli/calc.py +2 -2
- synth_ai/cli/demo.py +14 -7
- synth_ai/cli/legacy_root_backup.py +1 -1
- synth_ai/cli/recent.py +1 -1
- synth_ai/cli/rl_demo.py +8 -7
- synth_ai/cli/root.py +0 -97
- synth_ai/cli/status.py +1 -1
- synth_ai/cli/task_apps.py +1922 -190
- synth_ai/cli/traces.py +1 -1
- synth_ai/cli/tui.py +57 -0
- synth_ai/cli/turso.py +1 -1
- synth_ai/cli/watch.py +1 -1
- synth_ai/demos/demo_task_apps/crafter/grpo_crafter_task_app.py +29 -17
- synth_ai/environments/examples/crafter_classic/environment.py +1 -1
- synth_ai/environments/examples/enron/engine.py +7 -2
- synth_ai/environments/examples/enron/environment.py +68 -0
- synth_ai/environments/examples/red/engine.py +27 -0
- synth_ai/environments/examples/red/engine_helpers/memory_map.py +7 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/pallet_town_progression.py +477 -0
- synth_ai/environments/examples/red/engine_helpers/state_extraction.py +32 -0
- synth_ai/environments/examples/red/environment.py +60 -0
- synth_ai/environments/examples/sokoban/taskset.py +116 -0
- synth_ai/environments/examples/verilog/engine.py +104 -12
- synth_ai/evals/client.py +58 -61
- synth_ai/jobs/client.py +16 -4
- synth_ai/judge_schemas.py +9 -9
- synth_ai/py.typed +0 -0
- synth_ai/task/__init__.py +24 -5
- synth_ai/task/apps/__init__.py +1 -0
- synth_ai/task/config.py +257 -0
- synth_ai/task/contracts.py +138 -39
- synth_ai/task/proxy.py +48 -56
- synth_ai/task/rubrics/__init__.py +56 -0
- synth_ai/task/rubrics/loaders.py +152 -0
- synth_ai/task/rubrics/models.py +57 -0
- synth_ai/task/rubrics/scoring.py +116 -0
- synth_ai/{rubrics/validators.py → task/rubrics/strict.py} +53 -30
- synth_ai/task/server.py +8 -7
- synth_ai/task/trace_correlation_helpers.py +315 -0
- synth_ai/task/validators.py +413 -6
- synth_ai/tracing_v3/abstractions.py +3 -3
- synth_ai/tracing_v3/decorators.py +7 -3
- synth_ai/tracing_v3/llm_call_record_helpers.py +5 -5
- synth_ai/tracing_v3/replica_sync.py +4 -4
- synth_ai/tracing_v3/serialization.py +5 -5
- synth_ai/tracing_v3/session_tracer.py +16 -6
- synth_ai/tracing_v3/storage/base.py +29 -29
- synth_ai/tracing_v3/storage/config.py +3 -3
- synth_ai/tracing_v3/trace_utils.py +317 -0
- synth_ai/tracing_v3/turso/daemon.py +8 -7
- synth_ai/tracing_v3/turso/native_manager.py +66 -43
- synth_ai/tracing_v3/utils.py +3 -3
- synth_ai/tui/__init__.py +5 -0
- synth_ai/tui/__main__.py +13 -0
- synth_ai/tui/cli/__init__.py +1 -0
- synth_ai/tui/cli/query_experiments.py +164 -0
- synth_ai/tui/cli/query_experiments_v3.py +164 -0
- synth_ai/tui/dashboard.py +906 -0
- {synth_ai-0.2.13.dev1.dist-info → synth_ai-0.2.14.dist-info}/METADATA +4 -1
- {synth_ai-0.2.13.dev1.dist-info → synth_ai-0.2.14.dist-info}/RECORD +278 -126
- examples/agora_ex/README_MoE.md +0 -224
- examples/agora_ex/__init__.py +0 -7
- examples/agora_ex/agora_ex.py +0 -65
- examples/agora_ex/agora_ex_task_app.py +0 -590
- examples/agora_ex/configs/rl_lora_qwen3_moe_2xh200.toml +0 -121
- examples/agora_ex/reward_fn_grpo-human.py +0 -129
- examples/agora_ex/system_prompt_CURRENT.md +0 -63
- examples/agora_ex/task_app/agora_ex_task_app.py +0 -590
- examples/agora_ex/task_app/reward_fn_grpo-human.py +0 -129
- examples/agora_ex/task_app/system_prompt_CURRENT.md +0 -63
- examples/warming_up_to_rl/task_app/synth_envs_hosted/utils.py +0 -62
- synth_ai/rubrics/__init__.py +0 -22
- synth_ai/task/rubrics.py +0 -219
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/README.md +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/README.md +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/__init__.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/branching.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/environment_routes.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/__init__.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/__init__.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/app.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/shared.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/tools.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/hosted_app.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/inference/__init__.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/main.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/registry.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/storage/__init__.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/storage/volume.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/test_agents.py +0 -0
- /examples/{rl/task_app → task_apps/math}/README.md +0 -0
- /examples/{rl/task_app → task_apps/math}/math_task_app.py +0 -0
- /examples/{rl → workflows/math_rl}/configs/eval_base_qwen.toml +0 -0
- /examples/{rl → workflows/math_rl}/configs/eval_rl_qwen.toml +0 -0
- /examples/{rl → workflows/math_rl}/configs/rl_from_base_qwen.toml +0 -0
- /examples/{rl → workflows/math_rl}/configs/rl_from_base_qwen17.toml +0 -0
- /examples/{rl → workflows/math_rl}/configs/rl_from_ft_qwen.toml +0 -0
- /examples/{rl → workflows/math_rl}/run_eval.py +0 -0
- /examples/{rl → workflows/math_rl}/run_rl_and_save.py +0 -0
- {synth_ai-0.2.13.dev1.dist-info → synth_ai-0.2.14.dist-info}/WHEEL +0 -0
- {synth_ai-0.2.13.dev1.dist-info → synth_ai-0.2.14.dist-info}/entry_points.txt +0 -0
- {synth_ai-0.2.13.dev1.dist-info → synth_ai-0.2.14.dist-info}/licenses/LICENSE +0 -0
- {synth_ai-0.2.13.dev1.dist-info → synth_ai-0.2.14.dist-info}/top_level.txt +0 -0
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import logging
|
|
2
4
|
import os
|
|
5
|
+
from collections.abc import Iterable, Sequence
|
|
3
6
|
from dataclasses import asdict, dataclass, fields
|
|
4
7
|
from typing import List, Tuple
|
|
5
8
|
from uuid import UUID, uuid4
|
|
@@ -18,6 +21,7 @@ from synth_ai.environments.tasks.core import (
|
|
|
18
21
|
TaskInstanceMetadataFilter,
|
|
19
22
|
TaskInstanceSet,
|
|
20
23
|
)
|
|
24
|
+
from synth_ai.task.contracts import TaskInfo
|
|
21
25
|
|
|
22
26
|
logger = logging.getLogger(__name__)
|
|
23
27
|
|
|
@@ -96,6 +100,118 @@ class SokobanTaskInstance(TaskInstance):
|
|
|
96
100
|
return cls(**filtered_data)
|
|
97
101
|
|
|
98
102
|
|
|
103
|
+
def _base_task_info_template() -> TaskInfo:
|
|
104
|
+
return TaskInfo(
|
|
105
|
+
task={"id": "sokoban", "name": "Sokoban", "version": "1.0.0"},
|
|
106
|
+
environment="sokoban",
|
|
107
|
+
action_space={
|
|
108
|
+
"type": "tool_call",
|
|
109
|
+
"tools": [{"name": "interact", "schema": {"action": "int"}}],
|
|
110
|
+
"max_calls": 1,
|
|
111
|
+
},
|
|
112
|
+
observation={"summary": "Sokoban grid observation", "keys": ["grid", "player"]},
|
|
113
|
+
dataset={"id": "sokoban", "name": "Sokoban", "version": "1.0.0"},
|
|
114
|
+
rubric={"version": "1", "criteria_count": 1, "source": "inline"},
|
|
115
|
+
inference={"supports_proxy": False},
|
|
116
|
+
capabilities={"supports_rollout": True, "supports_env_lifecycle": True},
|
|
117
|
+
limits={"max_turns": 200},
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class SokobanTaskSet:
|
|
122
|
+
"""Minimal helper compatible with Task App expectations."""
|
|
123
|
+
|
|
124
|
+
def __init__(self) -> None:
|
|
125
|
+
self._taskset: TaskInstanceSet | None = None
|
|
126
|
+
self._seed_index: dict[int, SokobanTaskInstance] = {}
|
|
127
|
+
self._base_info = _base_task_info_template()
|
|
128
|
+
|
|
129
|
+
async def _ensure_loaded(self) -> TaskInstanceSet:
|
|
130
|
+
if self._taskset is None:
|
|
131
|
+
dataset = await create_sokoban_taskset()
|
|
132
|
+
self._taskset = dataset
|
|
133
|
+
self._seed_index.clear()
|
|
134
|
+
for inst in dataset.instances:
|
|
135
|
+
try:
|
|
136
|
+
seed_value = int(getattr(inst.metadata, "seed"))
|
|
137
|
+
except Exception:
|
|
138
|
+
continue
|
|
139
|
+
# Keep the first instance encountered for a seed
|
|
140
|
+
self._seed_index.setdefault(seed_value, inst)
|
|
141
|
+
return self._taskset
|
|
142
|
+
|
|
143
|
+
def describe(self) -> dict[str, object]:
|
|
144
|
+
if not self._taskset:
|
|
145
|
+
return {"id": "sokoban", "name": "Sokoban"}
|
|
146
|
+
return {
|
|
147
|
+
"id": "sokoban",
|
|
148
|
+
"name": self._taskset.name,
|
|
149
|
+
"description": self._taskset.description,
|
|
150
|
+
"instance_count": len(self._taskset.instances),
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async def provide_task_instances(self, seeds: Sequence[int]) -> Iterable[TaskInfo]:
|
|
154
|
+
await self._ensure_loaded()
|
|
155
|
+
if not seeds:
|
|
156
|
+
return []
|
|
157
|
+
|
|
158
|
+
infos: list[TaskInfo] = []
|
|
159
|
+
for raw_seed in seeds:
|
|
160
|
+
try:
|
|
161
|
+
seed_value = int(raw_seed)
|
|
162
|
+
except Exception:
|
|
163
|
+
continue
|
|
164
|
+
|
|
165
|
+
instance = self._seed_index.get(seed_value)
|
|
166
|
+
if instance is None:
|
|
167
|
+
# Attempt to construct on the fly; try configured difficulties in order
|
|
168
|
+
for difficulty in DIFFICULTY_CONFIGS:
|
|
169
|
+
try:
|
|
170
|
+
instance = await create_task_instance_from_seed(difficulty, seed_value)
|
|
171
|
+
break
|
|
172
|
+
except Exception:
|
|
173
|
+
continue
|
|
174
|
+
if instance is None:
|
|
175
|
+
continue
|
|
176
|
+
self._seed_index[seed_value] = instance
|
|
177
|
+
|
|
178
|
+
metadata = getattr(instance, "metadata", None)
|
|
179
|
+
base_info = self._base_info.model_copy(deep=True)
|
|
180
|
+
|
|
181
|
+
observation = dict(base_info.observation)
|
|
182
|
+
dataset_info = dict(base_info.dataset)
|
|
183
|
+
task_metadata = {"seed": seed_value}
|
|
184
|
+
|
|
185
|
+
if metadata is not None:
|
|
186
|
+
for key in ("difficulty", "num_boxes", "dim_room", "max_steps", "shortest_path_length"):
|
|
187
|
+
value = getattr(metadata, key, None)
|
|
188
|
+
if value is not None:
|
|
189
|
+
observation[key] = value
|
|
190
|
+
task_metadata[key] = value
|
|
191
|
+
dataset_info.update(
|
|
192
|
+
{
|
|
193
|
+
"seed": getattr(metadata, "seed", seed_value),
|
|
194
|
+
"difficulty": getattr(metadata, "difficulty", None),
|
|
195
|
+
"num_boxes": getattr(metadata, "num_boxes", None),
|
|
196
|
+
"dim_room": getattr(metadata, "dim_room", None),
|
|
197
|
+
}
|
|
198
|
+
)
|
|
199
|
+
generation_params = getattr(metadata, "generation_params", None)
|
|
200
|
+
if generation_params is not None:
|
|
201
|
+
task_metadata["generation_params"] = generation_params
|
|
202
|
+
|
|
203
|
+
infos.append(
|
|
204
|
+
base_info.model_copy(
|
|
205
|
+
update={
|
|
206
|
+
"observation": observation,
|
|
207
|
+
"dataset": dataset_info,
|
|
208
|
+
"task_metadata": task_metadata,
|
|
209
|
+
}
|
|
210
|
+
)
|
|
211
|
+
)
|
|
212
|
+
return infos
|
|
213
|
+
|
|
214
|
+
|
|
99
215
|
async def create_sokoban_taskset() -> TaskInstanceSet:
|
|
100
216
|
"""Generates Sokoban task instances from pre-generated verified puzzles."""
|
|
101
217
|
instances = []
|
|
@@ -46,7 +46,7 @@ class VerilogCompileSuccessComponent(RewardComponent):
|
|
|
46
46
|
if hasattr(action, "get") and action.get("type") == "compile":
|
|
47
47
|
# Check if compilation was successful (returncode 0)
|
|
48
48
|
if action.get("returncode") == 0:
|
|
49
|
-
return 0.1
|
|
49
|
+
return 0.01 # Normalized: 0.1 / 10.0 = 0.01
|
|
50
50
|
return 0.0
|
|
51
51
|
|
|
52
52
|
|
|
@@ -55,24 +55,37 @@ class VerilogSimulationPassComponent(RewardComponent):
|
|
|
55
55
|
if hasattr(action, "get") and action.get("type") == "simulate":
|
|
56
56
|
# Check if simulation passed
|
|
57
57
|
if action.get("passed", False):
|
|
58
|
-
return 1.0
|
|
58
|
+
return 0.1 # Normalized: 1.0 / 10.0 = 0.1
|
|
59
59
|
return 0.0
|
|
60
60
|
|
|
61
61
|
|
|
62
62
|
class VerilogStepPenaltyComponent(RewardComponent):
|
|
63
|
-
def __init__(self, penalty: float =
|
|
63
|
+
def __init__(self, penalty: float = 0.0): # No per-step reward - only reward accomplishments
|
|
64
64
|
self.penalty = penalty
|
|
65
65
|
|
|
66
66
|
async def score(self, state: Any, action: Any) -> float:
|
|
67
67
|
return self.penalty
|
|
68
68
|
|
|
69
69
|
|
|
70
|
+
class VerilogSubmitSuccessComponent(RewardComponent):
|
|
71
|
+
"""Reward for successful submission (tests passed). Max reward = 1.0 (normalized)."""
|
|
72
|
+
async def score(self, state: VerilogPublicState, action: Any) -> float:
|
|
73
|
+
if hasattr(action, "get") and action.get("type") == "submit":
|
|
74
|
+
# Check if submission passed
|
|
75
|
+
if action.get("passed", False):
|
|
76
|
+
return 1.0 # Normalized: Maximum reward is now 1.0
|
|
77
|
+
return 0.0
|
|
78
|
+
|
|
79
|
+
|
|
70
80
|
class VerilogEngine(StatefulEngine):
|
|
71
81
|
"""
|
|
72
82
|
Stateful Verilog evaluation engine with persistent artifact snapshots.
|
|
73
83
|
"""
|
|
74
84
|
|
|
75
85
|
def __init__(self, task_instance: TaskInstance):
|
|
86
|
+
# Validate required Verilog tools are available
|
|
87
|
+
self._validate_verilog_tools()
|
|
88
|
+
|
|
76
89
|
self.task_instance = task_instance
|
|
77
90
|
self._total_reward = 0.0
|
|
78
91
|
self._current_action_for_reward: Optional[Dict[str, Any]] = None
|
|
@@ -81,7 +94,8 @@ class VerilogEngine(StatefulEngine):
|
|
|
81
94
|
components=[
|
|
82
95
|
VerilogCompileSuccessComponent(),
|
|
83
96
|
VerilogSimulationPassComponent(),
|
|
84
|
-
|
|
97
|
+
VerilogSubmitSuccessComponent(),
|
|
98
|
+
VerilogStepPenaltyComponent(penalty=0.0), # No per-step reward
|
|
85
99
|
]
|
|
86
100
|
)
|
|
87
101
|
|
|
@@ -92,6 +106,39 @@ class VerilogEngine(StatefulEngine):
|
|
|
92
106
|
# Track last compile/simulate outputs
|
|
93
107
|
self._last_compile_output: Optional[str] = None
|
|
94
108
|
self._last_simulate_output: Optional[str] = None
|
|
109
|
+
|
|
110
|
+
@staticmethod
|
|
111
|
+
def _validate_verilog_tools() -> None:
|
|
112
|
+
"""Validate that required Verilog tools (iverilog, vvp) are available."""
|
|
113
|
+
missing_tools = []
|
|
114
|
+
|
|
115
|
+
if not shutil.which("iverilog"):
|
|
116
|
+
missing_tools.append("iverilog")
|
|
117
|
+
if not shutil.which("vvp"):
|
|
118
|
+
missing_tools.append("vvp")
|
|
119
|
+
|
|
120
|
+
if missing_tools:
|
|
121
|
+
error_msg = (
|
|
122
|
+
f"🚨🚨🚨 CRITICAL CONFIGURATION ERROR 🚨🚨🚨\n"
|
|
123
|
+
f"\n"
|
|
124
|
+
f"Missing required Verilog tools: {', '.join(missing_tools)}\n"
|
|
125
|
+
f"\n"
|
|
126
|
+
f"The Verilog environment CANNOT function without these tools.\n"
|
|
127
|
+
f"ALL compile/simulate operations will FAIL.\n"
|
|
128
|
+
f"ALL rewards will be ZERO.\n"
|
|
129
|
+
f"Training or evaluation will be COMPLETELY BROKEN.\n"
|
|
130
|
+
f"\n"
|
|
131
|
+
f"🔧 FIX THIS NOW:\n"
|
|
132
|
+
f"1. Add 'iverilog' to apt_packages in Modal deployment config\n"
|
|
133
|
+
f"2. Location: examples/task_apps/verilog/task_app/grpo_verilog.py\n"
|
|
134
|
+
f"3. Look for: modal=ModalDeploymentConfig(\n"
|
|
135
|
+
f"4. Add: apt_packages=('iverilog',) # Provides both iverilog and vvp\n"
|
|
136
|
+
f"5. Redeploy: uvx synth-ai modal-serve grpo-verilog\n"
|
|
137
|
+
f"\n"
|
|
138
|
+
f"{'='*80}"
|
|
139
|
+
)
|
|
140
|
+
print(f"\n{'='*80}\n{error_msg}\n{'='*80}\n", flush=True)
|
|
141
|
+
raise RuntimeError(error_msg)
|
|
95
142
|
|
|
96
143
|
async def _reset_engine(
|
|
97
144
|
self, *, seed: Optional[int] = None
|
|
@@ -122,6 +169,13 @@ class VerilogEngine(StatefulEngine):
|
|
|
122
169
|
) -> Tuple[VerilogPrivateState, VerilogPublicState]:
|
|
123
170
|
"""Process an action result and update engine state."""
|
|
124
171
|
self._current_action_for_reward = action_result
|
|
172
|
+
|
|
173
|
+
# DEBUG: Print action_result
|
|
174
|
+
print(f"\n[ENGINE DEBUG] _step_engine called")
|
|
175
|
+
print(f" action_result: {action_result}")
|
|
176
|
+
print(f" action_result.type: {action_result.get('type')}")
|
|
177
|
+
print(f" action_result.returncode: {action_result.get('returncode')}")
|
|
178
|
+
print(f" action_result.ok: {action_result.get('ok')}")
|
|
125
179
|
|
|
126
180
|
# Update last outputs if this is a compile or simulate action
|
|
127
181
|
if action_result.get("type") == "compile":
|
|
@@ -136,18 +190,21 @@ class VerilogEngine(StatefulEngine):
|
|
|
136
190
|
current_pub_state = VerilogPublicState(
|
|
137
191
|
files=self._get_file_contents(),
|
|
138
192
|
build_dir=str(self.build_dir),
|
|
139
|
-
task_completed=action_result.get("passed", False),
|
|
193
|
+
task_completed=action_result.get("submitted", False) and action_result.get("passed", False),
|
|
140
194
|
)
|
|
141
195
|
|
|
142
196
|
reward_from_stack = await self.reward_stack.step_reward(
|
|
143
197
|
state=current_pub_state, action=self._current_action_for_reward
|
|
144
198
|
)
|
|
145
199
|
self._current_action_for_reward = None
|
|
200
|
+
|
|
201
|
+
# DEBUG: Print reward
|
|
202
|
+
print(f"[ENGINE DEBUG] reward_from_stack: {reward_from_stack}")
|
|
146
203
|
|
|
147
204
|
self._total_reward += reward_from_stack
|
|
148
205
|
|
|
149
|
-
# Check termination conditions
|
|
150
|
-
terminated = action_result.get("
|
|
206
|
+
# Check termination conditions - only terminate if submitted (regardless of pass/fail)
|
|
207
|
+
terminated = action_result.get("submitted", False)
|
|
151
208
|
|
|
152
209
|
priv = VerilogPrivateState(
|
|
153
210
|
reward_last=reward_from_stack,
|
|
@@ -159,7 +216,7 @@ class VerilogEngine(StatefulEngine):
|
|
|
159
216
|
pub = VerilogPublicState(
|
|
160
217
|
files=self._get_file_contents(),
|
|
161
218
|
build_dir=str(self.build_dir),
|
|
162
|
-
task_completed=action_result.get("passed", False),
|
|
219
|
+
task_completed=action_result.get("submitted", False) and action_result.get("passed", False),
|
|
163
220
|
last_compile_output=self._last_compile_output,
|
|
164
221
|
last_simulate_output=self._last_simulate_output,
|
|
165
222
|
)
|
|
@@ -248,6 +305,16 @@ class VerilogEngine(StatefulEngine):
|
|
|
248
305
|
}
|
|
249
306
|
except subprocess.TimeoutExpired:
|
|
250
307
|
return {"ok": False, "error": "Compilation timeout", "type": "compile"}
|
|
308
|
+
except FileNotFoundError:
|
|
309
|
+
error_msg = (
|
|
310
|
+
"🚨 CRITICAL ERROR: 'iverilog' executable not found! 🚨\n"
|
|
311
|
+
"The Verilog compiler (iverilog) is not installed in this environment.\n"
|
|
312
|
+
"This will cause ALL compile operations to fail and result in ZERO rewards.\n"
|
|
313
|
+
"Fix: Add 'iverilog' to apt_packages in the Modal deployment config.\n"
|
|
314
|
+
"Location: examples/task_apps/verilog/task_app/grpo_verilog.py -> modal=ModalDeploymentConfig(apt_packages=('iverilog',))"
|
|
315
|
+
)
|
|
316
|
+
print(f"\n{'='*80}\n{error_msg}\n{'='*80}\n", flush=True)
|
|
317
|
+
raise RuntimeError(error_msg) from None
|
|
251
318
|
except Exception as e:
|
|
252
319
|
return {"ok": False, "error": str(e), "type": "compile"}
|
|
253
320
|
|
|
@@ -279,18 +346,43 @@ class VerilogEngine(StatefulEngine):
|
|
|
279
346
|
}
|
|
280
347
|
except subprocess.TimeoutExpired:
|
|
281
348
|
return {"ok": False, "error": "Simulation timeout", "type": "simulate"}
|
|
349
|
+
except FileNotFoundError:
|
|
350
|
+
error_msg = (
|
|
351
|
+
"🚨 CRITICAL ERROR: 'vvp' executable not found! 🚨\n"
|
|
352
|
+
"The Verilog simulator (vvp) is not installed in this environment.\n"
|
|
353
|
+
"This will cause ALL simulate operations to fail and result in ZERO rewards.\n"
|
|
354
|
+
"Fix: Add 'iverilog' to apt_packages in the Modal deployment config (provides both iverilog and vvp).\n"
|
|
355
|
+
"Location: examples/task_apps/verilog/task_app/grpo_verilog.py -> modal=ModalDeploymentConfig(apt_packages=('iverilog',))"
|
|
356
|
+
)
|
|
357
|
+
print(f"\n{'='*80}\n{error_msg}\n{'='*80}\n", flush=True)
|
|
358
|
+
raise RuntimeError(error_msg) from None
|
|
282
359
|
except Exception as e:
|
|
283
360
|
return {"ok": False, "error": str(e), "type": "simulate"}
|
|
284
361
|
|
|
285
362
|
async def submit(self) -> Dict[str, Any]:
|
|
286
363
|
"""Submit solution for grading."""
|
|
287
|
-
#
|
|
288
|
-
#
|
|
364
|
+
# Check if the last simulation passed
|
|
365
|
+
# Parse the last simulation output to determine if tests passed
|
|
366
|
+
passed = False
|
|
367
|
+
detail = "No simulation run yet"
|
|
368
|
+
|
|
369
|
+
if self._last_simulate_output:
|
|
370
|
+
stdout = self._last_simulate_output
|
|
371
|
+
passed = (
|
|
372
|
+
"ALL_TESTS_PASSED" in stdout
|
|
373
|
+
or ("Mismatches: 0 " in stdout and "samples" in stdout)
|
|
374
|
+
or ("no mismatches" in stdout.lower() and "errors" not in stdout.lower())
|
|
375
|
+
)
|
|
376
|
+
if passed:
|
|
377
|
+
detail = "All tests passed"
|
|
378
|
+
else:
|
|
379
|
+
detail = "Tests failed - please review simulation output"
|
|
380
|
+
|
|
289
381
|
return {
|
|
290
382
|
"ok": True,
|
|
291
383
|
"type": "submit",
|
|
292
|
-
"passed":
|
|
293
|
-
"detail":
|
|
384
|
+
"passed": passed,
|
|
385
|
+
"detail": detail,
|
|
294
386
|
"submitted": True,
|
|
295
387
|
}
|
|
296
388
|
|
synth_ai/evals/client.py
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
1
|
"""Experimental Judge API client.
|
|
4
2
|
|
|
5
3
|
This surface is experimental and subject to change without notice.
|
|
6
4
|
Set environment variable `SYNTH_SILENCE_EXPERIMENTAL=1` to silence warnings.
|
|
7
5
|
"""
|
|
8
6
|
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
9
|
import os
|
|
10
10
|
import warnings
|
|
11
11
|
from typing import Any, Literal, TypedDict
|
|
@@ -13,73 +13,70 @@ from typing import Any, Literal, TypedDict
|
|
|
13
13
|
from synth_ai.http import AsyncHttpClient, HTTPError
|
|
14
14
|
from synth_ai.tracing_v3.serialization import normalize_for_json
|
|
15
15
|
|
|
16
|
-
|
|
17
16
|
Provider = Literal["groq", "gemini"]
|
|
18
17
|
|
|
19
18
|
|
|
20
19
|
class JudgeOptions(TypedDict, total=False):
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
20
|
+
event: bool
|
|
21
|
+
outcome: bool
|
|
22
|
+
rubric_id: str
|
|
23
|
+
rubric_overrides: dict[str, Any]
|
|
24
|
+
provider: Provider
|
|
25
|
+
model: str
|
|
26
|
+
max_concurrency: int
|
|
28
27
|
|
|
29
28
|
|
|
30
29
|
class JudgeScoreResponse(TypedDict, total=False):
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
status: str
|
|
31
|
+
event_rewards: list[dict[str, Any]]
|
|
32
|
+
outcome_reward: dict[str, Any]
|
|
33
|
+
details: dict[str, Any]
|
|
35
34
|
|
|
36
35
|
|
|
37
36
|
class JudgeClient:
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
async def score(
|
|
51
|
-
self,
|
|
52
|
-
*,
|
|
53
|
-
trace: dict[str, Any] | Any,
|
|
54
|
-
policy_name: str,
|
|
55
|
-
task_app_id: str,
|
|
56
|
-
options: JudgeOptions,
|
|
57
|
-
task_app_base_url: str | None = None,
|
|
58
|
-
) -> JudgeScoreResponse:
|
|
59
|
-
body = {
|
|
60
|
-
"policy_name": policy_name,
|
|
61
|
-
"task_app": {"id": task_app_id, **({"base_url": task_app_base_url} if task_app_base_url else {})},
|
|
62
|
-
"trace": normalize_for_json(trace),
|
|
63
|
-
"options": options or {},
|
|
64
|
-
}
|
|
65
|
-
try:
|
|
66
|
-
async with AsyncHttpClient(self._base, self._key, timeout=self._timeout) as http:
|
|
67
|
-
js = await http.post_json("/api/judge/v1/score", json=body)
|
|
68
|
-
if not isinstance(js, dict):
|
|
69
|
-
raise ValueError("invalid_judge_response_shape")
|
|
70
|
-
return js # type: ignore[return-value]
|
|
71
|
-
except HTTPError as e: # map to friendlier exceptions
|
|
72
|
-
status = int(getattr(e, "status", 0) or 0)
|
|
73
|
-
if status in (400, 422):
|
|
74
|
-
raise ValueError(f"judge_validation_error: {e.detail}") from e
|
|
75
|
-
if status in (401, 403):
|
|
76
|
-
raise PermissionError(f"judge_auth_error: {e.detail}") from e
|
|
77
|
-
if status == 404:
|
|
78
|
-
raise FileNotFoundError(f"judge_route_not_found: {e.detail}") from e
|
|
79
|
-
if status == 429:
|
|
80
|
-
raise Exception("judge_rate_limited") from e # replace with RetryLater in future
|
|
81
|
-
if status >= 500:
|
|
82
|
-
raise Exception("judge_transient_error") from e # replace with TransientError in future
|
|
83
|
-
raise
|
|
84
|
-
|
|
37
|
+
def __init__(self, base_url: str, api_key: str, *, timeout: float = 60.0) -> None:
|
|
38
|
+
_silence = (os.getenv("SYNTH_SILENCE_EXPERIMENTAL") or "").strip().lower()
|
|
39
|
+
if _silence not in {"1", "true", "t", "yes", "y", "on"}:
|
|
40
|
+
warnings.warn(
|
|
41
|
+
"Experimental API: synth_ai.evals.JudgeClient is experimental and may change without notice.",
|
|
42
|
+
UserWarning,
|
|
43
|
+
stacklevel=2,
|
|
44
|
+
)
|
|
45
|
+
self._base = base_url.rstrip("/")
|
|
46
|
+
self._key = api_key
|
|
47
|
+
self._timeout = timeout
|
|
85
48
|
|
|
49
|
+
async def score(
|
|
50
|
+
self,
|
|
51
|
+
*,
|
|
52
|
+
trace: dict[str, Any] | Any,
|
|
53
|
+
policy_name: str,
|
|
54
|
+
task_app_id: str,
|
|
55
|
+
options: JudgeOptions,
|
|
56
|
+
task_app_base_url: str | None = None,
|
|
57
|
+
) -> JudgeScoreResponse:
|
|
58
|
+
body = {
|
|
59
|
+
"policy_name": policy_name,
|
|
60
|
+
"task_app": {"id": task_app_id, **({"base_url": task_app_base_url} if task_app_base_url else {})},
|
|
61
|
+
"trace": normalize_for_json(trace),
|
|
62
|
+
"options": options or {},
|
|
63
|
+
}
|
|
64
|
+
try:
|
|
65
|
+
async with AsyncHttpClient(self._base, self._key, timeout=self._timeout) as http:
|
|
66
|
+
js = await http.post_json("/api/judge/v1/score", json=body)
|
|
67
|
+
if not isinstance(js, dict):
|
|
68
|
+
raise ValueError("invalid_judge_response_shape")
|
|
69
|
+
return js # type: ignore[return-value]
|
|
70
|
+
except HTTPError as err: # map to friendlier exceptions
|
|
71
|
+
status = int(getattr(err, "status", 0) or 0)
|
|
72
|
+
if status in (400, 422):
|
|
73
|
+
raise ValueError(f"judge_validation_error: {err.detail}") from err
|
|
74
|
+
if status in (401, 403):
|
|
75
|
+
raise PermissionError(f"judge_auth_error: {err.detail}") from err
|
|
76
|
+
if status == 404:
|
|
77
|
+
raise FileNotFoundError(f"judge_route_not_found: {err.detail}") from err
|
|
78
|
+
if status == 429:
|
|
79
|
+
raise Exception("judge_rate_limited") from err # replace with RetryLater in future
|
|
80
|
+
if status >= 500:
|
|
81
|
+
raise Exception("judge_transient_error") from err # replace with TransientError in future
|
|
82
|
+
raise
|
synth_ai/jobs/client.py
CHANGED
|
@@ -1,20 +1,32 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import importlib
|
|
4
|
-
from
|
|
4
|
+
from collections.abc import Callable
|
|
5
|
+
from typing import Any, cast
|
|
5
6
|
|
|
6
7
|
try:
|
|
7
|
-
|
|
8
|
+
_supported_module = cast(
|
|
9
|
+
Any, importlib.import_module("synth_ai.api.models.supported")
|
|
10
|
+
)
|
|
11
|
+
normalize_model_identifier = cast(
|
|
12
|
+
Callable[[str], str], _supported_module.normalize_model_identifier
|
|
13
|
+
)
|
|
8
14
|
except Exception as exc: # pragma: no cover - critical dependency
|
|
9
15
|
raise RuntimeError("Unable to load supported model utilities") from exc
|
|
10
16
|
|
|
11
17
|
try:
|
|
12
|
-
|
|
18
|
+
_http_module = cast(Any, importlib.import_module("synth_ai.http"))
|
|
19
|
+
AsyncHttpClient = _http_module.AsyncHttpClient
|
|
13
20
|
except Exception as exc: # pragma: no cover - critical dependency
|
|
14
21
|
raise RuntimeError("Unable to load HTTP client") from exc
|
|
15
22
|
|
|
16
23
|
try:
|
|
17
|
-
|
|
24
|
+
_sft_config_module = cast(
|
|
25
|
+
Any, importlib.import_module("synth_ai.learning.sft.config")
|
|
26
|
+
)
|
|
27
|
+
prepare_sft_job_payload = cast(
|
|
28
|
+
Callable[..., dict[str, Any]], _sft_config_module.prepare_sft_job_payload
|
|
29
|
+
)
|
|
18
30
|
except Exception as exc: # pragma: no cover - critical dependency
|
|
19
31
|
raise RuntimeError("Unable to load SFT configuration helpers") from exc
|
|
20
32
|
|
synth_ai/judge_schemas.py
CHANGED
|
@@ -9,7 +9,7 @@ This is the canonical contract that the backend MUST conform to.
|
|
|
9
9
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
|
-
from typing import Any, Dict,
|
|
12
|
+
from typing import Any, Dict, Literal, Optional
|
|
13
13
|
|
|
14
14
|
from pydantic import BaseModel, Field
|
|
15
15
|
|
|
@@ -26,7 +26,7 @@ class CriterionScorePayload(BaseModel):
|
|
|
26
26
|
class ReviewPayload(BaseModel):
|
|
27
27
|
"""Rubric review (event-level or outcome-level)."""
|
|
28
28
|
|
|
29
|
-
criteria:
|
|
29
|
+
criteria: dict[str, CriterionScorePayload] = Field(
|
|
30
30
|
default_factory=dict,
|
|
31
31
|
description="Map of criterion keys to their scores"
|
|
32
32
|
)
|
|
@@ -42,7 +42,7 @@ class JudgeScoreResponse(BaseModel):
|
|
|
42
42
|
"""
|
|
43
43
|
|
|
44
44
|
status: Literal["ok", "failed"] = Field(default="ok", description="Request status")
|
|
45
|
-
event_reviews:
|
|
45
|
+
event_reviews: list[ReviewPayload] = Field(
|
|
46
46
|
default_factory=list,
|
|
47
47
|
description="List of per-event rubric reviews (one per step)"
|
|
48
48
|
)
|
|
@@ -50,15 +50,15 @@ class JudgeScoreResponse(BaseModel):
|
|
|
50
50
|
None,
|
|
51
51
|
description="Optional outcome-level rubric review"
|
|
52
52
|
)
|
|
53
|
-
event_totals:
|
|
53
|
+
event_totals: list[float] = Field(
|
|
54
54
|
default_factory=list,
|
|
55
55
|
description="List of aggregated scores per event (matches event_reviews length)"
|
|
56
56
|
)
|
|
57
|
-
details:
|
|
57
|
+
details: dict[str, Any] = Field(
|
|
58
58
|
default_factory=dict,
|
|
59
59
|
description="Additional details (provider, latency, etc.)"
|
|
60
60
|
)
|
|
61
|
-
metadata:
|
|
61
|
+
metadata: dict[str, Any] = Field(
|
|
62
62
|
default_factory=dict,
|
|
63
63
|
description="Request metadata (provider, options, etc.)"
|
|
64
64
|
)
|
|
@@ -108,12 +108,12 @@ class JudgeOptions(BaseModel):
|
|
|
108
108
|
class JudgeTracePayload(BaseModel):
|
|
109
109
|
"""Trace payload containing trajectory context."""
|
|
110
110
|
|
|
111
|
-
event_history:
|
|
112
|
-
markov_blanket_message_history:
|
|
111
|
+
event_history: list[dict[str, Any]] = Field(..., description="List of events/steps")
|
|
112
|
+
markov_blanket_message_history: list[dict[str, Any]] = Field(
|
|
113
113
|
default_factory=list,
|
|
114
114
|
description="Optional message history for context"
|
|
115
115
|
)
|
|
116
|
-
metadata:
|
|
116
|
+
metadata: dict[str, Any] = Field(default_factory=dict, description="Trace metadata")
|
|
117
117
|
|
|
118
118
|
|
|
119
119
|
class JudgeScoreRequest(BaseModel):
|
synth_ai/py.typed
ADDED
|
File without changes
|
synth_ai/task/__init__.py
CHANGED
|
@@ -4,7 +4,11 @@ from .auth import (
|
|
|
4
4
|
require_api_key_dependency,
|
|
5
5
|
)
|
|
6
6
|
from .client import TaskAppClient
|
|
7
|
+
from .config import EvalConfig, FilterConfig
|
|
7
8
|
from .contracts import (
|
|
9
|
+
DatasetInfo,
|
|
10
|
+
InferenceInfo,
|
|
11
|
+
LimitsInfo,
|
|
8
12
|
RolloutEnvSpec,
|
|
9
13
|
RolloutMetrics,
|
|
10
14
|
RolloutPolicySpec,
|
|
@@ -14,8 +18,10 @@ from .contracts import (
|
|
|
14
18
|
RolloutSafetyConfig,
|
|
15
19
|
RolloutStep,
|
|
16
20
|
RolloutTrajectory,
|
|
17
|
-
|
|
21
|
+
RubricInfo,
|
|
22
|
+
RubricSection,
|
|
18
23
|
TaskAppEndpoints,
|
|
24
|
+
TaskDescriptor,
|
|
19
25
|
TaskInfo,
|
|
20
26
|
)
|
|
21
27
|
from .datasets import TaskDatasetRegistry, TaskDatasetSpec
|
|
@@ -23,7 +29,6 @@ from .errors import error_payload, http_exception, json_error_response
|
|
|
23
29
|
from .health import task_app_health
|
|
24
30
|
from .json import to_jsonable
|
|
25
31
|
from .proxy import (
|
|
26
|
-
INTERACT_TOOL_SCHEMA,
|
|
27
32
|
extract_message_text,
|
|
28
33
|
inject_system_hint,
|
|
29
34
|
parse_tool_call_from_text,
|
|
@@ -46,7 +51,12 @@ from .server import (
|
|
|
46
51
|
create_task_app,
|
|
47
52
|
run_task_app,
|
|
48
53
|
)
|
|
49
|
-
from .validators import
|
|
54
|
+
from .validators import (
|
|
55
|
+
normalize_inference_url,
|
|
56
|
+
validate_rollout_response_for_rl,
|
|
57
|
+
validate_task_app_endpoint,
|
|
58
|
+
validate_task_app_url,
|
|
59
|
+
)
|
|
50
60
|
from .vendors import (
|
|
51
61
|
get_groq_key_or_503,
|
|
52
62
|
get_openai_key_or_503,
|
|
@@ -54,9 +64,13 @@ from .vendors import (
|
|
|
54
64
|
)
|
|
55
65
|
|
|
56
66
|
__all__ = [
|
|
67
|
+
"normalize_inference_url",
|
|
68
|
+
"validate_rollout_response_for_rl",
|
|
57
69
|
"validate_task_app_url",
|
|
70
|
+
"validate_task_app_endpoint",
|
|
58
71
|
"task_app_health",
|
|
59
|
-
"
|
|
72
|
+
"EvalConfig",
|
|
73
|
+
"FilterConfig",
|
|
60
74
|
"TaskAppEndpoints",
|
|
61
75
|
"RolloutEnvSpec",
|
|
62
76
|
"RolloutPolicySpec",
|
|
@@ -67,6 +81,12 @@ __all__ = [
|
|
|
67
81
|
"RolloutTrajectory",
|
|
68
82
|
"RolloutStep",
|
|
69
83
|
"RolloutMetrics",
|
|
84
|
+
"TaskDescriptor",
|
|
85
|
+
"DatasetInfo",
|
|
86
|
+
"RubricInfo",
|
|
87
|
+
"RubricSection",
|
|
88
|
+
"InferenceInfo",
|
|
89
|
+
"LimitsInfo",
|
|
70
90
|
"TaskInfo",
|
|
71
91
|
"to_jsonable",
|
|
72
92
|
"normalize_environment_api_key",
|
|
@@ -75,7 +95,6 @@ __all__ = [
|
|
|
75
95
|
"normalize_vendor_keys",
|
|
76
96
|
"get_openai_key_or_503",
|
|
77
97
|
"get_groq_key_or_503",
|
|
78
|
-
"INTERACT_TOOL_SCHEMA",
|
|
79
98
|
"prepare_for_openai",
|
|
80
99
|
"prepare_for_groq",
|
|
81
100
|
"inject_system_hint",
|
synth_ai/task/apps/__init__.py
CHANGED
|
@@ -18,6 +18,7 @@ class ModalDeploymentConfig:
|
|
|
18
18
|
app_name: str
|
|
19
19
|
python_version: str = "3.11"
|
|
20
20
|
pip_packages: Sequence[str] = field(default_factory=tuple)
|
|
21
|
+
apt_packages: Sequence[str] = field(default_factory=tuple)
|
|
21
22
|
extra_local_dirs: Sequence[tuple[str, str]] = field(default_factory=tuple)
|
|
22
23
|
secret_names: Sequence[str] = field(default_factory=tuple)
|
|
23
24
|
volume_mounts: Sequence[tuple[str, str]] = field(default_factory=tuple)
|