synth-ai 0.2.8.dev4__py3-none-any.whl → 0.2.23.dev3__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.
- examples/README.md +1 -0
- examples/__init__.py +16 -0
- examples/analyze_semantic_words.sh +17 -0
- examples/baseline/banking77_baseline.py +243 -0
- examples/baseline/banking77_pipeline_baseline.py +294 -0
- examples/baseline/crafter_baseline.py +407 -0
- examples/baseline/pokemon_red_baseline.py +326 -0
- examples/baseline/simple_baseline.py +56 -0
- examples/baseline/warming_up_to_rl_baseline.py +239 -0
- examples/blog_posts/gepa/README.md +355 -0
- examples/blog_posts/gepa/configs/banking77_gepa_local.toml +95 -0
- examples/blog_posts/gepa/configs/banking77_gepa_test.toml +80 -0
- examples/blog_posts/gepa/configs/banking77_mipro_local.toml +50 -0
- examples/blog_posts/gepa/configs/banking77_pipeline_gepa_local.toml +101 -0
- examples/blog_posts/gepa/configs/banking77_pipeline_gepa_test.toml +96 -0
- examples/blog_posts/gepa/configs/hotpotqa_gepa_local.toml +57 -0
- examples/blog_posts/gepa/configs/hotpotqa_gepa_qwen.toml +35 -0
- examples/blog_posts/gepa/configs/hotpotqa_mipro_local.toml +51 -0
- examples/blog_posts/gepa/configs/hover_gepa_local.toml +57 -0
- examples/blog_posts/gepa/configs/hover_gepa_qwen.toml +35 -0
- examples/blog_posts/gepa/configs/hover_mipro_local.toml +51 -0
- examples/blog_posts/gepa/configs/ifbench_gepa_local.toml +57 -0
- examples/blog_posts/gepa/configs/ifbench_gepa_qwen.toml +35 -0
- examples/blog_posts/gepa/configs/ifbench_mipro_local.toml +51 -0
- examples/blog_posts/gepa/configs/pupa_gepa_local.toml +58 -0
- examples/blog_posts/gepa/configs/pupa_mipro_local.toml +52 -0
- examples/blog_posts/gepa/deploy_banking77_task_app.sh +54 -0
- examples/blog_posts/gepa/gepa_baseline.py +204 -0
- examples/blog_posts/gepa/query_prompts_example.py +97 -0
- examples/blog_posts/gepa/run_gepa_banking77.sh +112 -0
- examples/blog_posts/gepa/run_gepa_banking77_pipeline.sh +163 -0
- examples/blog_posts/gepa/task_apps.py +105 -0
- examples/blog_posts/gepa/test_gepa_local.sh +67 -0
- examples/blog_posts/gepa/verify_banking77_setup.sh +123 -0
- examples/blog_posts/mipro/README.md +415 -0
- examples/blog_posts/mipro/configs/banking77_mipro_local.toml +91 -0
- examples/blog_posts/mipro/configs/banking77_mipro_test.toml +87 -0
- examples/blog_posts/mipro/configs/banking77_pipeline_mipro_gemini_flash_lite_local.toml +98 -0
- examples/blog_posts/mipro/configs/banking77_pipeline_mipro_gpt41mini_local.toml +96 -0
- examples/blog_posts/mipro/configs/banking77_pipeline_mipro_local.toml +94 -0
- examples/blog_posts/mipro/configs/banking77_pipeline_mipro_test.toml +170 -0
- examples/blog_posts/mipro/deploy_banking77_pipeline_task_app.sh +59 -0
- examples/blog_posts/mipro/deploy_banking77_task_app.sh +41 -0
- examples/blog_posts/mipro/multi_step.md +79 -0
- examples/blog_posts/mipro/run_mipro_banking77.sh +191 -0
- examples/blog_posts/mipro/run_mipro_banking77_pipeline.sh +171 -0
- examples/blog_posts/mipro/run_mipro_banking77_pipeline_gemini_flash_lite.sh +177 -0
- examples/blog_posts/mipro/run_mipro_banking77_pipeline_gpt41mini.sh +173 -0
- examples/blog_posts/mipro/verify_banking77_setup.sh +117 -0
- examples/blog_posts/pokemon_vl/README.md +98 -0
- examples/blog_posts/pokemon_vl/configs/eval_gpt5nano.toml +26 -0
- examples/blog_posts/pokemon_vl/configs/eval_qwen3_vl.toml +27 -0
- examples/blog_posts/pokemon_vl/configs/eval_rl_final.toml +24 -0
- examples/blog_posts/pokemon_vl/configs/filter_high_reward.toml +10 -0
- examples/blog_posts/pokemon_vl/configs/train_rl_from_sft.toml +43 -0
- examples/blog_posts/pokemon_vl/configs/train_sft_qwen4b_vl.toml +40 -0
- examples/blog_posts/pokemon_vl/extract_images.py +239 -0
- examples/blog_posts/pokemon_vl/pokemon_vl_baseline.py +326 -0
- examples/blog_posts/pokemon_vl/run_eval_extract_images.py +209 -0
- examples/blog_posts/pokemon_vl/run_qwen_eval_extract_images.py +212 -0
- examples/blog_posts/pokemon_vl/text_box_analysis.md +106 -0
- examples/blog_posts/warming_up_to_rl/ARCHITECTURE.md +195 -0
- examples/blog_posts/warming_up_to_rl/FINAL_TEST_RESULTS.md +127 -0
- examples/blog_posts/warming_up_to_rl/INFERENCE_SUCCESS.md +132 -0
- examples/blog_posts/warming_up_to_rl/README.md +158 -0
- examples/blog_posts/warming_up_to_rl/SMOKE_TESTING.md +164 -0
- examples/blog_posts/warming_up_to_rl/SMOKE_TEST_COMPLETE.md +253 -0
- examples/blog_posts/warming_up_to_rl/configs/eval_baseline_qwen32b_10x20.toml +25 -0
- examples/blog_posts/warming_up_to_rl/configs/eval_ft_qwen4b.toml +25 -0
- examples/blog_posts/warming_up_to_rl/configs/eval_ft_qwen4b_10x20.toml +26 -0
- examples/blog_posts/warming_up_to_rl/configs/eval_groq_qwen32b.toml +25 -0
- examples/blog_posts/warming_up_to_rl/configs/eval_openai_gpt_oss_120b.toml +29 -0
- examples/blog_posts/warming_up_to_rl/configs/filter_high_reward_dataset.toml +10 -0
- examples/blog_posts/warming_up_to_rl/configs/smoke_test.toml +75 -0
- examples/blog_posts/warming_up_to_rl/configs/train_rl_from_sft.toml +91 -0
- examples/blog_posts/warming_up_to_rl/configs/train_sft_qwen4b.toml +40 -0
- examples/blog_posts/warming_up_to_rl/warming_up_to_rl_baseline.py +187 -0
- examples/crafter_debug_render.py +186 -0
- examples/dev/qwen3_32b_qlora_4xh100.toml +45 -0
- examples/gepa/banking77_pipeline_gepa.toml +96 -0
- examples/gepa/multi_stage_gepa_example.toml +84 -0
- examples/gepa/run_gepa_banking77_pipeline.sh +157 -0
- examples/multi_step/SFT_README.md +147 -0
- examples/multi_step/configs/README_verilog_rl.md +77 -0
- examples/multi_step/configs/VERILOG_REWARDS.md +103 -0
- examples/multi_step/configs/VERILOG_RL_CHECKLIST.md +196 -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_outcome.toml +75 -0
- examples/multi_step/configs/crafter_rl_stepwise_hosted_judge.toml +145 -0
- examples/multi_step/configs/crafter_rl_stepwise_shaped.toml +84 -0
- examples/multi_step/configs/crafter_rl_stepwise_simple.toml +79 -0
- examples/multi_step/configs/crafter_rl_stepwise_simple_NEW_FORMAT.toml +105 -0
- examples/multi_step/configs/crafter_sft_qwen30b_lora.toml +62 -0
- 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 +147 -0
- examples/multi_step/convert_traces_to_sft.py +84 -0
- examples/multi_step/crafter_rl_lora.md +70 -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/run_sft_qwen30b.sh +45 -0
- examples/multi_step/sse_metrics_streaming_notes.md +357 -0
- examples/multi_step/task_app_config_notes.md +494 -0
- examples/multi_step/verilog_rl_lora.md +218 -0
- examples/qwen_coder/README.md +102 -0
- examples/qwen_coder/_shared.py +113 -0
- examples/qwen_coder/configs/coder_lora_30b.toml +60 -0
- examples/qwen_coder/configs/coder_lora_4b.toml +61 -0
- examples/qwen_coder/configs/coder_lora_small.toml +57 -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/qwen_vl/BUGS_AND_FIXES.md +232 -0
- examples/qwen_vl/IMAGE_VALIDATION_COMPLETE.md +271 -0
- examples/qwen_vl/IMAGE_VALIDATION_SUMMARY.md +260 -0
- examples/qwen_vl/INFERENCE_SFT_TESTS.md +412 -0
- examples/qwen_vl/NEXT_STEPS_2B.md +325 -0
- examples/qwen_vl/QUICKSTART.md +327 -0
- examples/qwen_vl/QUICKSTART_RL_VISION.md +110 -0
- examples/qwen_vl/README.md +152 -0
- examples/qwen_vl/RL_VISION_COMPLETE.md +475 -0
- examples/qwen_vl/RL_VISION_TESTING.md +333 -0
- examples/qwen_vl/SDK_VISION_INTEGRATION.md +328 -0
- examples/qwen_vl/SETUP_COMPLETE.md +274 -0
- examples/qwen_vl/VISION_TESTS_COMPLETE.md +489 -0
- examples/qwen_vl/VLM_PIPELINE_COMPLETE.md +242 -0
- examples/qwen_vl/__init__.py +2 -0
- examples/qwen_vl/collect_data_via_cli.md +415 -0
- examples/qwen_vl/collect_vision_traces.py +368 -0
- examples/qwen_vl/configs/crafter_rl_vision_qwen3vl4b.toml +110 -0
- examples/qwen_vl/configs/crafter_vlm_sft_example.toml +59 -0
- examples/qwen_vl/configs/eval_gpt4o_mini_vision.toml +26 -0
- examples/qwen_vl/configs/eval_gpt4o_vision_proper.toml +29 -0
- examples/qwen_vl/configs/eval_gpt5nano_vision.toml +26 -0
- examples/qwen_vl/configs/eval_qwen3vl_vision.toml +26 -0
- examples/qwen_vl/configs/filter_qwen3vl_sft.toml +49 -0
- examples/qwen_vl/configs/filter_vision_sft.toml +52 -0
- examples/qwen_vl/configs/filter_vision_test.toml +8 -0
- examples/qwen_vl/configs/sft_qwen3_vl_2b_test.toml +54 -0
- examples/qwen_vl/crafter_gpt5nano_agent.py +308 -0
- examples/qwen_vl/crafter_qwen_vl_agent.py +300 -0
- examples/qwen_vl/run_vision_comparison.sh +61 -0
- examples/qwen_vl/run_vision_sft_pipeline.sh +175 -0
- examples/qwen_vl/test_image_validation.py +201 -0
- examples/qwen_vl/test_sft_vision_data.py +110 -0
- examples/rl/README.md +169 -0
- examples/rl/configs/eval_base_qwen.toml +17 -0
- examples/rl/configs/eval_rl_qwen.toml +13 -0
- examples/rl/configs/rl_from_base_qwen.toml +62 -0
- examples/rl/configs/rl_from_base_qwen17.toml +80 -0
- examples/rl/configs/rl_from_ft_qwen.toml +37 -0
- examples/rl/download_dataset.py +80 -0
- examples/rl/run_eval.py +436 -0
- examples/rl/run_rl_and_save.py +111 -0
- examples/rl/task_app/README.md +21 -0
- examples/rl/task_app/math_single_step.py +990 -0
- examples/rl/task_app/math_task_app.py +111 -0
- examples/run_crafter_demo.sh +10 -0
- examples/sdk_prompt_learning_example.py +55 -0
- examples/sft/README.md +139 -0
- examples/sft/configs/crafter_fft_qwen0p6b.toml +49 -0
- examples/sft/configs/crafter_lora_qwen0p6b.toml +49 -0
- examples/sft/evaluate.py +117 -0
- examples/sft/export_dataset.py +120 -0
- examples/sft/generate_traces.py +164 -0
- examples/swe/__init__.py +12 -0
- examples/swe/task_app/README.md +135 -0
- examples/swe/task_app/__init__.py +2 -0
- examples/swe/task_app/grpo_swe_mini.py +604 -0
- examples/swe/task_app/grpo_swe_mini_task_app.py +124 -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 +1191 -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 +584 -0
- examples/swe/task_app/hosted/main.py +100 -0
- examples/swe/task_app/hosted/policy_routes.py +1094 -0
- examples/swe/task_app/hosted/registry.py +195 -0
- examples/swe/task_app/hosted/rollout.py +1905 -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 +136 -0
- examples/swe/task_app/hosted/utils.py +62 -0
- examples/swe/task_app/morph_backend.py +178 -0
- examples/task_apps/IMAGE_ONLY_EVAL_QUICKSTART.md +258 -0
- examples/task_apps/TESTING.md +275 -0
- examples/task_apps/banking77/__init__.py +6 -0
- examples/task_apps/banking77/banking77_task_app.py +912 -0
- examples/task_apps/banking77/deploy_wrapper.py +46 -0
- examples/task_apps/banking77_pipeline/__init__.py +6 -0
- examples/task_apps/banking77_pipeline/banking77_pipeline_task_app.py +489 -0
- examples/task_apps/banking77_pipeline/deploy_wrapper.py +50 -0
- examples/task_apps/crafter/CREATE_SFT_DATASET.md +286 -0
- examples/task_apps/crafter/EVAL_IMAGE_ONLY_RESULTS.md +152 -0
- examples/task_apps/crafter/FILTER_COMMAND_STATUS.md +187 -0
- examples/task_apps/crafter/FILTER_COMMAND_SUCCESS.md +281 -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/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/README.md +42 -0
- examples/task_apps/crafter/task_app/__init__.py +5 -0
- examples/task_apps/crafter/task_app/grpo_crafter.py +1055 -0
- examples/task_apps/crafter/task_app/grpo_crafter_task_app.py +146 -0
- examples/task_apps/crafter/task_app/synth_envs_hosted/README.md +173 -0
- examples/task_apps/crafter/task_app/synth_envs_hosted/__init__.py +5 -0
- examples/task_apps/crafter/task_app/synth_envs_hosted/branching.py +143 -0
- examples/task_apps/crafter/task_app/synth_envs_hosted/environment_routes.py +1226 -0
- examples/task_apps/crafter/task_app/synth_envs_hosted/envs/__init__.py +1 -0
- examples/task_apps/crafter/task_app/synth_envs_hosted/envs/crafter/__init__.py +6 -0
- examples/task_apps/crafter/task_app/synth_envs_hosted/envs/crafter/app.py +1 -0
- examples/task_apps/crafter/task_app/synth_envs_hosted/envs/crafter/environment.py +532 -0
- examples/task_apps/crafter/task_app/synth_envs_hosted/envs/crafter/policy.py +583 -0
- examples/task_apps/crafter/task_app/synth_envs_hosted/envs/crafter/react_agent.py +122 -0
- examples/task_apps/crafter/task_app/synth_envs_hosted/envs/crafter/shared.py +305 -0
- examples/task_apps/crafter/task_app/synth_envs_hosted/envs/crafter/tools.py +47 -0
- examples/task_apps/crafter/task_app/synth_envs_hosted/hosted_app.py +253 -0
- examples/task_apps/crafter/task_app/synth_envs_hosted/inference/__init__.py +5 -0
- examples/task_apps/crafter/task_app/synth_envs_hosted/inference/openai_client.py +999 -0
- examples/task_apps/crafter/task_app/synth_envs_hosted/main.py +100 -0
- examples/task_apps/crafter/task_app/synth_envs_hosted/policy_routes.py +1252 -0
- examples/task_apps/crafter/task_app/synth_envs_hosted/registry.py +195 -0
- examples/task_apps/crafter/task_app/synth_envs_hosted/rollout.py +2233 -0
- examples/task_apps/crafter/task_app/synth_envs_hosted/storage/__init__.py +5 -0
- examples/task_apps/crafter/task_app/synth_envs_hosted/storage/volume.py +211 -0
- examples/task_apps/crafter/task_app/synth_envs_hosted/test_agents.py +161 -0
- examples/task_apps/crafter/task_app/synth_envs_hosted/test_service.py +136 -0
- examples/task_apps/crafter/task_app/synth_envs_hosted/utils.py +411 -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/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/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/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 +2 -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/gepa_benchmarks/__init__.py +7 -0
- examples/task_apps/gepa_benchmarks/common.py +260 -0
- examples/task_apps/gepa_benchmarks/hotpotqa_task_app.py +507 -0
- examples/task_apps/gepa_benchmarks/hover_task_app.py +436 -0
- examples/task_apps/gepa_benchmarks/ifbench_task_app.py +563 -0
- examples/task_apps/gepa_benchmarks/pupa_task_app.py +460 -0
- examples/task_apps/math/README.md +21 -0
- examples/task_apps/math/math_single_step.py +1000 -0
- examples/task_apps/math/math_task_app.py +115 -0
- 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 +356 -0
- examples/task_apps/pokemon_red/README_IMAGE_ONLY_EVAL.md +428 -0
- examples/task_apps/pokemon_red/__init__.py +3 -0
- examples/task_apps/pokemon_red/eval_image_only_gpt4o.toml +30 -0
- examples/task_apps/pokemon_red/eval_pokemon_red_policy.py +224 -0
- examples/task_apps/pokemon_red/pallet_town_rl_config.toml +75 -0
- examples/task_apps/pokemon_red/task_app.py +1048 -0
- examples/task_apps/pokemon_red/test_pallet_town_rewards.py +193 -0
- examples/task_apps/sokoban/README.md +306 -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 +22 -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/tunnel_gepa_banking77/README.md +106 -0
- examples/tunnel_gepa_banking77/banking77_gepa_tunnel.toml +95 -0
- examples/tunnel_gepa_banking77/keep_tunnel_running.py +60 -0
- examples/tunnel_gepa_banking77/run_gepa_with_tunnel.sh +226 -0
- examples/vlm/PROPOSAL.md +53 -0
- examples/vlm/README.md +68 -0
- examples/vlm/configs/crafter_vlm_gpt4o.toml +49 -0
- examples/vlm/crafter_image_only_agent.py +207 -0
- examples/vlm/crafter_openai_vlm_agent.py +275 -0
- examples/vlm/filter_image_rows.py +63 -0
- examples/vlm/run_crafter_vlm_benchmark.py +316 -0
- examples/warming_up_to_rl/_utils.py +92 -0
- examples/warming_up_to_rl/analyze_trace_db.py +422 -0
- examples/warming_up_to_rl/configs/crafter_fft.toml +53 -0
- examples/warming_up_to_rl/configs/crafter_fft_4b.toml +54 -0
- examples/warming_up_to_rl/configs/eval_fft_qwen4b.toml +22 -0
- examples/warming_up_to_rl/configs/eval_groq_qwen32b.toml +15 -0
- examples/warming_up_to_rl/configs/eval_modal_qwen4b.toml +24 -0
- examples/warming_up_to_rl/configs/eval_stepwise_complex.toml +35 -0
- examples/warming_up_to_rl/configs/eval_stepwise_consistent.toml +26 -0
- examples/warming_up_to_rl/configs/eval_stepwise_per_achievement.toml +36 -0
- examples/warming_up_to_rl/configs/eval_stepwise_simple.toml +32 -0
- examples/warming_up_to_rl/configs/rl_from_base_qwen4b.toml +85 -0
- examples/warming_up_to_rl/configs/rl_from_ft.toml +58 -0
- examples/warming_up_to_rl/export_trace_sft.py +837 -0
- examples/warming_up_to_rl/groq_test.py +97 -0
- examples/warming_up_to_rl/manage_secrets.py +131 -0
- examples/warming_up_to_rl/old/event_rewards.md +234 -0
- examples/warming_up_to_rl/old/notes.md +73 -0
- examples/warming_up_to_rl/readme.md +110 -0
- examples/warming_up_to_rl/run_eval.py +736 -0
- examples/warming_up_to_rl/run_fft_and_save.py +380 -0
- examples/warming_up_to_rl/run_local_rollout.py +239 -0
- examples/warming_up_to_rl/run_local_rollout_modal.py +248 -0
- examples/warming_up_to_rl/run_local_rollout_parallel.py +405 -0
- examples/warming_up_to_rl/run_local_rollout_traced.py +477 -0
- examples/warming_up_to_rl/run_rl_and_save.py +124 -0
- examples/warming_up_to_rl/run_rollout_remote.py +156 -0
- examples/warming_up_to_rl/task_app/README.md +42 -0
- examples/warming_up_to_rl/task_app/grpo_crafter.py +876 -0
- examples/warming_up_to_rl/task_app/grpo_crafter_task_app.py +135 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/README.md +173 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/__init__.py +5 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/branching.py +143 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/environment_routes.py +1226 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/__init__.py +1 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/__init__.py +6 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/app.py +1 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/environment.py +522 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/policy.py +454 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/react_agent.py +108 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/shared.py +305 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/tools.py +47 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/hosted_app.py +253 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/inference/__init__.py +5 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/inference/openai_client.py +729 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/main.py +100 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/policy_routes.py +1114 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/registry.py +195 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/rollout.py +1891 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/storage/__init__.py +5 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/storage/volume.py +211 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/test_agents.py +161 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/test_service.py +137 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/utils.py +129 -0
- examples/workflows/math_rl/configs/eval_base_qwen.toml +15 -0
- examples/workflows/math_rl/configs/eval_rl_qwen.toml +11 -0
- examples/workflows/math_rl/configs/rl_from_base_qwen.toml +62 -0
- examples/workflows/math_rl/configs/rl_from_base_qwen17.toml +80 -0
- examples/workflows/math_rl/configs/rl_from_ft_qwen.toml +35 -0
- examples/workflows/math_rl/download_dataset.py +80 -0
- examples/workflows/math_rl/run_eval.py +436 -0
- examples/workflows/math_rl/run_rl_and_save.py +111 -0
- synth_ai/__init__.py +47 -23
- synth_ai/_utils/__init__.py +47 -0
- synth_ai/_utils/base_url.py +10 -0
- synth_ai/_utils/http.py +10 -0
- synth_ai/_utils/prompts.py +10 -0
- synth_ai/_utils/task_app_state.py +12 -0
- synth_ai/_utils/user_config.py +10 -0
- synth_ai/api/models/supported.py +514 -0
- synth_ai/api/train/__init__.py +63 -0
- synth_ai/api/train/builders.py +473 -0
- synth_ai/api/train/cli.py +1185 -0
- synth_ai/api/train/config_finder.py +246 -0
- synth_ai/api/train/configs/__init__.py +65 -0
- synth_ai/api/train/configs/prompt_learning.py +496 -0
- synth_ai/api/train/configs/rl.py +188 -0
- synth_ai/api/train/configs/sft.py +99 -0
- synth_ai/api/train/configs/shared.py +81 -0
- synth_ai/api/train/env_resolver.py +352 -0
- synth_ai/api/train/pollers.py +91 -0
- synth_ai/api/train/prompt_learning.py +425 -0
- synth_ai/api/train/sft.py +390 -0
- synth_ai/api/train/supported_algos.py +147 -0
- synth_ai/api/train/task_app.py +195 -0
- synth_ai/api/train/utils.py +244 -0
- synth_ai/api/train/validators.py +1117 -0
- synth_ai/api/tunnel.py +49 -0
- synth_ai/auth/credentials.py +94 -0
- synth_ai/baseline/__init__.py +25 -0
- synth_ai/baseline/config.py +209 -0
- synth_ai/baseline/discovery.py +214 -0
- synth_ai/baseline/execution.py +146 -0
- synth_ai/cfgs.py +227 -0
- synth_ai/cli/__init__.py +90 -45
- synth_ai/cli/_modal_wrapper.py +31 -0
- synth_ai/cli/_storage.py +20 -0
- synth_ai/cli/_typer_patch.py +47 -0
- synth_ai/cli/_validate_task_app.py +29 -0
- synth_ai/cli/balance.py +16 -4
- synth_ai/cli/calc.py +36 -21
- synth_ai/cli/claude.py +70 -0
- synth_ai/cli/codex.py +267 -0
- synth_ai/cli/commands/__init__.py +18 -0
- synth_ai/cli/commands/baseline/__init__.py +12 -0
- synth_ai/cli/commands/baseline/core.py +637 -0
- synth_ai/cli/commands/baseline/list.py +93 -0
- synth_ai/cli/commands/demo/__init__.py +6 -0
- synth_ai/cli/commands/demo/core.py +163 -0
- synth_ai/cli/commands/eval/__init__.py +19 -0
- synth_ai/cli/commands/eval/core.py +1112 -0
- synth_ai/cli/commands/eval/errors.py +81 -0
- synth_ai/cli/commands/eval/validation.py +133 -0
- synth_ai/cli/commands/filter/__init__.py +12 -0
- synth_ai/cli/commands/filter/core.py +424 -0
- synth_ai/cli/commands/filter/errors.py +55 -0
- synth_ai/cli/commands/filter/validation.py +77 -0
- synth_ai/cli/commands/help/__init__.py +185 -0
- synth_ai/cli/commands/help/core.py +72 -0
- synth_ai/cli/commands/smoke/__init__.py +7 -0
- synth_ai/cli/commands/smoke/core.py +1437 -0
- synth_ai/cli/commands/status/__init__.py +66 -0
- synth_ai/cli/commands/status/client.py +192 -0
- synth_ai/cli/commands/status/config.py +92 -0
- synth_ai/cli/commands/status/errors.py +20 -0
- synth_ai/cli/commands/status/formatters.py +164 -0
- synth_ai/cli/commands/status/subcommands/__init__.py +9 -0
- synth_ai/cli/commands/status/subcommands/files.py +79 -0
- synth_ai/cli/commands/status/subcommands/jobs.py +334 -0
- synth_ai/cli/commands/status/subcommands/models.py +79 -0
- synth_ai/cli/commands/status/subcommands/pricing.py +22 -0
- synth_ai/cli/commands/status/subcommands/runs.py +81 -0
- synth_ai/cli/commands/status/subcommands/session.py +183 -0
- synth_ai/cli/commands/status/subcommands/summary.py +47 -0
- synth_ai/cli/commands/status/subcommands/usage.py +203 -0
- synth_ai/cli/commands/status/utils.py +114 -0
- synth_ai/cli/commands/train/__init__.py +53 -0
- synth_ai/cli/commands/train/core.py +21 -0
- synth_ai/cli/commands/train/errors.py +117 -0
- synth_ai/cli/commands/train/judge_schemas.py +200 -0
- synth_ai/cli/commands/train/judge_validation.py +305 -0
- synth_ai/cli/commands/train/validation.py +386 -0
- synth_ai/cli/demo.py +32 -140
- synth_ai/cli/deploy.py +233 -0
- synth_ai/cli/eval/__init__.py +36 -0
- synth_ai/cli/eval/core.py +5 -0
- synth_ai/cli/eval/errors.py +31 -0
- synth_ai/cli/eval/validation.py +5 -0
- synth_ai/cli/filter/__init__.py +28 -0
- synth_ai/cli/filter/core.py +5 -0
- synth_ai/cli/filter/errors.py +23 -0
- synth_ai/cli/filter/validation.py +5 -0
- synth_ai/cli/legacy_root_backup.py +28 -22
- synth_ai/cli/lib/__init__.py +10 -0
- synth_ai/cli/lib/task_app_discovery.py +7 -0
- synth_ai/cli/lib/task_app_env.py +518 -0
- synth_ai/cli/mcp.py +34 -0
- synth_ai/cli/modal_serve/__init__.py +12 -0
- synth_ai/cli/modal_serve/core.py +14 -0
- synth_ai/cli/modal_serve/errors.py +8 -0
- synth_ai/cli/modal_serve/validation.py +11 -0
- synth_ai/cli/opencode.py +256 -0
- synth_ai/cli/recent.py +13 -7
- synth_ai/cli/rl_demo.py +166 -114
- synth_ai/cli/root.py +143 -112
- synth_ai/cli/serve/__init__.py +12 -0
- synth_ai/cli/serve/core.py +14 -0
- synth_ai/cli/serve/errors.py +8 -0
- synth_ai/cli/serve/validation.py +11 -0
- synth_ai/cli/setup.py +49 -0
- synth_ai/cli/status.py +7 -125
- synth_ai/cli/task_app_deploy.py +7 -0
- synth_ai/cli/task_app_list.py +25 -0
- synth_ai/cli/task_app_modal_serve.py +11 -0
- synth_ai/cli/task_app_serve.py +11 -0
- synth_ai/cli/task_apps.py +3134 -0
- synth_ai/cli/traces.py +9 -5
- synth_ai/cli/train/__init__.py +12 -0
- synth_ai/cli/train/core.py +21 -0
- synth_ai/cli/train/errors.py +8 -0
- synth_ai/cli/train/validation.py +24 -0
- synth_ai/cli/train.py +5 -0
- synth_ai/cli/turso.py +73 -0
- synth_ai/cli/watch.py +13 -18
- synth_ai/demos/__init__.py +10 -0
- synth_ai/demos/core/__init__.py +28 -1
- synth_ai/demos/core/cli.py +745 -416
- synth_ai/demos/crafter/__init__.py +1 -0
- synth_ai/demos/crafter/crafter_fft_4b.toml +55 -0
- synth_ai/demos/crafter/grpo_crafter_task_app.py +185 -0
- synth_ai/demos/crafter/rl_from_base_qwen4b.toml +74 -0
- synth_ai/demos/demo_registry.py +176 -0
- synth_ai/demos/demo_task_apps/__init__.py +7 -1
- synth_ai/demos/demo_task_apps/core.py +75 -37
- synth_ai/demos/demo_task_apps/crafter/__init__.py +1 -0
- synth_ai/demos/demo_task_apps/crafter/configs/crafter_fft_4b.toml +53 -0
- synth_ai/demos/demo_task_apps/crafter/configs/rl_from_base_qwen4b.toml +73 -0
- synth_ai/demos/demo_task_apps/crafter/grpo_crafter_task_app.py +184 -0
- synth_ai/demos/demo_task_apps/math/_common.py +1 -2
- synth_ai/demos/demo_task_apps/math/app.py +2 -1
- synth_ai/demos/demo_task_apps/math/config.toml +55 -110
- synth_ai/demos/demo_task_apps/math/deploy_modal.py +3 -6
- synth_ai/demos/demo_task_apps/math/modal_task_app.py +491 -166
- synth_ai/demos/demo_task_apps/math/task_app_entry.py +37 -0
- synth_ai/demos/math/__init__.py +1 -0
- synth_ai/demos/math/_common.py +16 -0
- synth_ai/demos/math/app.py +38 -0
- synth_ai/demos/math/config.toml +76 -0
- synth_ai/demos/math/deploy_modal.py +54 -0
- synth_ai/demos/math/modal_task_app.py +703 -0
- synth_ai/demos/math/task_app_entry.py +51 -0
- synth_ai/environments/environment/core.py +7 -1
- synth_ai/environments/examples/bandit/engine.py +12 -5
- synth_ai/environments/examples/bandit/environment.py +0 -1
- synth_ai/environments/examples/bandit/taskset.py +4 -4
- synth_ai/environments/examples/crafter_classic/engine_deterministic_patch.py +7 -4
- synth_ai/environments/examples/crafter_classic/engine_serialization_patch_v3.py +9 -5
- synth_ai/environments/examples/crafter_classic/environment.py +93 -2
- synth_ai/environments/examples/crafter_classic/world_config_patch_simple.py +4 -3
- 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 +60 -12
- synth_ai/environments/examples/red/engine_helpers/memory_map.py +7 -0
- synth_ai/environments/examples/red/engine_helpers/reward_components.py +151 -179
- 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 +86 -0
- synth_ai/environments/examples/red/trace_hooks_v3.py +168 -0
- synth_ai/environments/examples/sokoban/taskset.py +116 -0
- synth_ai/environments/examples/verilog/engine.py +104 -12
- synth_ai/environments/examples/wordle/environment.py +0 -1
- synth_ai/environments/reproducibility/tree.py +5 -6
- synth_ai/environments/service/app.py +11 -12
- synth_ai/environments/service/core_routes.py +10 -9
- 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/evals/__init__.py +15 -0
- synth_ai/evals/base.py +14 -5
- synth_ai/evals/client.py +82 -0
- synth_ai/evals/types.py +42 -0
- synth_ai/http.py +8 -22
- synth_ai/http_client.py +45 -12
- synth_ai/inference/__init__.py +0 -2
- synth_ai/inference/client.py +21 -7
- synth_ai/jobs/client.py +129 -80
- synth_ai/judge_schemas.py +127 -0
- synth_ai/learning/__init__.py +51 -6
- synth_ai/learning/algorithms.py +14 -0
- synth_ai/learning/client.py +122 -30
- synth_ai/learning/config.py +2 -40
- synth_ai/learning/constants.py +0 -2
- synth_ai/learning/ft_client.py +4 -56
- synth_ai/learning/health.py +14 -8
- synth_ai/learning/jobs.py +43 -47
- synth_ai/learning/prompt_learning_client.py +276 -0
- synth_ai/learning/prompt_learning_types.py +185 -0
- synth_ai/{rl → learning/rl}/__init__.py +14 -5
- synth_ai/learning/rl/client.py +269 -0
- synth_ai/learning/rl/config.py +31 -0
- synth_ai/{rl → learning/rl}/contracts.py +5 -10
- synth_ai/{rl → learning/rl}/env_keys.py +45 -16
- synth_ai/learning/rl/secrets.py +13 -0
- synth_ai/learning/rl_client.py +2 -253
- 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 +698 -0
- synth_ai/learning/sse.py +25 -26
- synth_ai/learning/validators.py +29 -25
- synth_ai/mcp/__init__.py +5 -0
- synth_ai/mcp/__main__.py +8 -0
- synth_ai/mcp/main.py +254 -0
- synth_ai/mcp/setup.py +100 -0
- synth_ai/modal.py +257 -0
- synth_ai/pricing/__init__.py +3 -0
- synth_ai/pricing/model_pricing.py +64 -0
- synth_ai/session/__init__.py +75 -0
- synth_ai/session/client.py +383 -0
- synth_ai/session/constants.py +63 -0
- synth_ai/session/exceptions.py +105 -0
- synth_ai/session/manager.py +139 -0
- synth_ai/session/models.py +89 -0
- synth_ai/session/query.py +110 -0
- synth_ai/spec/__init__.py +46 -0
- synth_ai/spec/dataclasses.py +149 -0
- synth_ai/spec/loader.py +144 -0
- synth_ai/spec/serializer.py +199 -0
- synth_ai/spec/validation.py +250 -0
- synth_ai/streaming/__init__.py +29 -0
- synth_ai/streaming/config.py +94 -0
- synth_ai/streaming/handlers.py +589 -0
- synth_ai/streaming/streamer.py +320 -0
- synth_ai/streaming/types.py +95 -0
- synth_ai/task/__init__.py +116 -3
- synth_ai/task/apps/__init__.py +132 -0
- synth_ai/task/auth.py +165 -0
- synth_ai/task/client.py +167 -0
- synth_ai/task/config.py +261 -0
- synth_ai/task/contracts.py +173 -57
- synth_ai/task/datasets.py +108 -0
- synth_ai/task/errors.py +50 -0
- synth_ai/task/health.py +17 -11
- synth_ai/task/inference_api.py +101 -0
- synth_ai/task/json.py +111 -0
- synth_ai/task/proxy.py +251 -0
- synth_ai/task/rubrics/__init__.py +55 -0
- synth_ai/task/rubrics/loaders.py +156 -0
- synth_ai/task/rubrics/models.py +57 -0
- synth_ai/task/rubrics/scoring.py +116 -0
- synth_ai/task/rubrics/strict.py +149 -0
- synth_ai/task/rubrics.py +219 -0
- synth_ai/task/server.py +432 -0
- synth_ai/task/trace_correlation_helpers.py +328 -0
- synth_ai/task/tracing_utils.py +95 -0
- synth_ai/task/validators.py +449 -6
- synth_ai/task/vendors.py +59 -0
- synth_ai/tracing_v3/__init__.py +4 -0
- synth_ai/tracing_v3/abstractions.py +21 -4
- synth_ai/tracing_v3/config.py +167 -22
- synth_ai/tracing_v3/constants.py +21 -0
- synth_ai/tracing_v3/db_config.py +42 -29
- synth_ai/tracing_v3/decorators.py +80 -45
- synth_ai/tracing_v3/examples/basic_usage.py +15 -9
- synth_ai/tracing_v3/hooks.py +6 -4
- synth_ai/tracing_v3/llm_call_record_helpers.py +161 -61
- synth_ai/tracing_v3/migration_helper.py +1 -2
- synth_ai/tracing_v3/replica_sync.py +12 -7
- synth_ai/tracing_v3/serialization.py +130 -0
- synth_ai/tracing_v3/session_tracer.py +86 -21
- synth_ai/tracing_v3/storage/base.py +98 -12
- synth_ai/tracing_v3/storage/config.py +63 -16
- synth_ai/tracing_v3/storage/factory.py +11 -9
- synth_ai/tracing_v3/storage/utils.py +15 -11
- synth_ai/tracing_v3/trace_utils.py +317 -0
- synth_ai/tracing_v3/turso/__init__.py +8 -21
- synth_ai/tracing_v3/turso/daemon.py +123 -15
- synth_ai/tracing_v3/turso/models.py +5 -2
- synth_ai/tracing_v3/turso/native_manager.py +1293 -0
- synth_ai/tracing_v3/utils.py +5 -4
- synth_ai/tunnel.py +143 -0
- synth_ai/tunnel_deploy.py +278 -0
- synth_ai/types.py +8 -0
- synth_ai/urls.py +11 -0
- synth_ai/utils/__init__.py +166 -0
- synth_ai/utils/agents.py +74 -0
- synth_ai/utils/apps.py +152 -0
- synth_ai/utils/base_url.py +94 -0
- synth_ai/utils/bin.py +39 -0
- synth_ai/utils/claude.py +36 -0
- synth_ai/utils/cli.py +284 -0
- synth_ai/utils/config.py +81 -0
- synth_ai/utils/env.py +346 -0
- synth_ai/utils/errors.py +85 -0
- synth_ai/utils/http.py +172 -0
- synth_ai/utils/json.py +72 -0
- synth_ai/utils/log_filter.py +99 -0
- synth_ai/utils/logging.py +198 -0
- synth_ai/utils/modal.py +299 -0
- synth_ai/utils/paths.py +95 -0
- synth_ai/utils/process.py +233 -0
- synth_ai/utils/prompts.py +39 -0
- synth_ai/utils/sqld.py +122 -0
- synth_ai/utils/ssl.py +25 -0
- synth_ai/utils/task_app_discovery.py +882 -0
- synth_ai/utils/task_app_env.py +186 -0
- synth_ai/utils/task_app_state.py +318 -0
- synth_ai/utils/tunnel/__init__.py +12 -0
- synth_ai/utils/tunnel/config.py +55 -0
- synth_ai/utils/user_config.py +137 -0
- synth_ai/uvicorn.py +77 -0
- synth_ai-0.2.23.dev3.dist-info/METADATA +357 -0
- synth_ai-0.2.23.dev3.dist-info/RECORD +983 -0
- {synth_ai-0.2.8.dev4.dist-info → synth_ai-0.2.23.dev3.dist-info}/entry_points.txt +0 -1
- {synth_ai-0.2.8.dev4.dist-info → synth_ai-0.2.23.dev3.dist-info}/top_level.txt +1 -0
- synth_ai/cli/man.py +0 -106
- synth_ai/core/experiment.py +0 -15
- synth_ai/core/system.py +0 -15
- synth_ai/environments/examples/sokoban/units/astar_common.py +0 -95
- synth_ai/experimental/synth_oss.py +0 -446
- synth_ai/handshake.py +0 -63
- synth_ai/install_sqld.sh +0 -40
- 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/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/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/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/base.py +0 -81
- 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/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/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/tracing/__init__.py +0 -30
- synth_ai/tracing_v1/__init__.py +0 -33
- synth_ai/tracing_v3/turso/manager.py +0 -760
- 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.dev4.dist-info/METADATA +0 -129
- synth_ai-0.2.8.dev4.dist-info/RECORD +0 -420
- {synth_ai/lm/caching → examples/task_apps}/__init__.py +0 -0
- {synth_ai/lm/cost → examples/task_apps/crafter}/__init__.py +0 -0
- {synth_ai/lm/structured_outputs → examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/server}/__init__.py +0 -0
- {synth_ai/lm/vendors → examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests}/__init__.py +0 -0
- {synth_ai/lm/vendors/core → examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils}/__init__.py +0 -0
- {synth_ai/lm/vendors/local → examples/task_apps/math}/__init__.py +0 -0
- {synth_ai/lm/vendors/supported → examples/workflows}/__init__.py +0 -0
- {synth_ai/v0/tracing → examples/workflows/math_rl}/__init__.py +0 -0
- /synth_ai/{compound/cais.py → cli/__main__.py} +0 -0
- /synth_ai/{learning/filtering.py → py.typed} +0 -0
- {synth_ai-0.2.8.dev4.dist-info → synth_ai-0.2.23.dev3.dist-info}/WHEEL +0 -0
- {synth_ai-0.2.8.dev4.dist-info → synth_ai-0.2.23.dev3.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,698 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import logging
|
|
5
|
+
from collections.abc import Iterable, Iterator, Sequence
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
SFTMessageContent = str | dict[str, Any] | list[Any] | None
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class SFTDataError(ValueError):
|
|
16
|
+
"""Raised when a JSONL record cannot be coerced into an SFTExample."""
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass(slots=True)
|
|
20
|
+
class SFTToolDefinition:
|
|
21
|
+
name: str
|
|
22
|
+
description: str | None
|
|
23
|
+
parameters: dict[str, Any] | None
|
|
24
|
+
raw: dict[str, Any] = field(default_factory=dict)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass(slots=True)
|
|
28
|
+
class SFTToolCall:
|
|
29
|
+
name: str
|
|
30
|
+
arguments: Any
|
|
31
|
+
call_id: str | None = None
|
|
32
|
+
type: str | None = None
|
|
33
|
+
raw: dict[str, Any] = field(default_factory=dict)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass(slots=True)
|
|
37
|
+
class SFTMessage:
|
|
38
|
+
role: str
|
|
39
|
+
content: SFTMessageContent
|
|
40
|
+
tool_calls: list[SFTToolCall] = field(default_factory=list)
|
|
41
|
+
tool_call_id: str | None = None
|
|
42
|
+
name: str | None = None
|
|
43
|
+
reasoning: str | None = None # NEW: Explicit reasoning/thinking content
|
|
44
|
+
raw_content: str | None = None # NEW: Original unparsed content (before reasoning extraction)
|
|
45
|
+
extra: dict[str, Any] = field(default_factory=dict)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass(slots=True)
|
|
49
|
+
class SFTExample:
|
|
50
|
+
messages: list[SFTMessage]
|
|
51
|
+
tools: list[SFTToolDefinition] = field(default_factory=list)
|
|
52
|
+
tool_choice: Any | None = None
|
|
53
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
54
|
+
extra: dict[str, Any] = field(default_factory=dict)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _parse_tool_arguments(value: Any) -> Any:
|
|
58
|
+
if isinstance(value, str):
|
|
59
|
+
try:
|
|
60
|
+
return json.loads(value)
|
|
61
|
+
except json.JSONDecodeError:
|
|
62
|
+
return value
|
|
63
|
+
return value
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _coerce_tool_definition(raw: Any, *, index: int) -> SFTToolDefinition:
|
|
67
|
+
if not isinstance(raw, dict):
|
|
68
|
+
raise SFTDataError(f"tool {index} is not an object")
|
|
69
|
+
name = raw.get("name")
|
|
70
|
+
if not isinstance(name, str) or not name.strip():
|
|
71
|
+
raise SFTDataError(f"tool {index} missing name")
|
|
72
|
+
description = raw.get("description")
|
|
73
|
+
if description is not None and not isinstance(description, str):
|
|
74
|
+
raise SFTDataError(f"tool {index} description must be a string if present")
|
|
75
|
+
parameters = raw.get("parameters")
|
|
76
|
+
if parameters is not None and not isinstance(parameters, dict):
|
|
77
|
+
raise SFTDataError(f"tool {index} parameters must be an object if present")
|
|
78
|
+
return SFTToolDefinition(
|
|
79
|
+
name=name, description=description, parameters=parameters, raw=dict(raw)
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _coerce_tool_call(raw: Any, *, index: int) -> SFTToolCall:
|
|
84
|
+
if not isinstance(raw, dict):
|
|
85
|
+
raise SFTDataError(f"tool_call {index} is not an object")
|
|
86
|
+
|
|
87
|
+
call_id = raw.get("id")
|
|
88
|
+
call_type = raw.get("type")
|
|
89
|
+
|
|
90
|
+
fn_payload: dict[str, Any] | None = None
|
|
91
|
+
name: str | None = None
|
|
92
|
+
arguments: Any = None
|
|
93
|
+
|
|
94
|
+
fn_obj = raw.get("function")
|
|
95
|
+
if isinstance(fn_obj, dict):
|
|
96
|
+
fn_payload = fn_obj
|
|
97
|
+
name_val = fn_payload.get("name")
|
|
98
|
+
name = name_val if isinstance(name_val, str) else None
|
|
99
|
+
arguments = fn_payload.get("arguments")
|
|
100
|
+
if name is None:
|
|
101
|
+
maybe_name = raw.get("name")
|
|
102
|
+
if isinstance(maybe_name, str):
|
|
103
|
+
name = maybe_name
|
|
104
|
+
arguments = raw.get("arguments")
|
|
105
|
+
|
|
106
|
+
if not isinstance(name, str) or not name.strip():
|
|
107
|
+
raise SFTDataError(f"tool_call {index} missing function name")
|
|
108
|
+
|
|
109
|
+
parsed_arguments = _parse_tool_arguments(arguments)
|
|
110
|
+
|
|
111
|
+
normalized_id = None
|
|
112
|
+
if call_id is not None:
|
|
113
|
+
normalized_id = str(call_id)
|
|
114
|
+
normalized_type = None
|
|
115
|
+
if call_type is not None:
|
|
116
|
+
normalized_type = str(call_type)
|
|
117
|
+
|
|
118
|
+
return SFTToolCall(
|
|
119
|
+
name=name,
|
|
120
|
+
arguments=parsed_arguments,
|
|
121
|
+
call_id=normalized_id,
|
|
122
|
+
type=normalized_type,
|
|
123
|
+
raw=dict(raw),
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _coerce_message(raw: Any, *, index: int) -> SFTMessage:
|
|
128
|
+
if not isinstance(raw, dict):
|
|
129
|
+
raise SFTDataError(f"message {index} is not an object")
|
|
130
|
+
role = raw.get("role")
|
|
131
|
+
if not isinstance(role, str) or not role.strip():
|
|
132
|
+
raise SFTDataError(f"message {index} has invalid role")
|
|
133
|
+
|
|
134
|
+
content = raw.get("content")
|
|
135
|
+
if content is not None and not isinstance(content, str | list | dict):
|
|
136
|
+
raise SFTDataError(f"message {index} has unsupported content type {type(content).__name__}")
|
|
137
|
+
|
|
138
|
+
raw_tool_calls = raw.get("tool_calls")
|
|
139
|
+
tool_calls: list[SFTToolCall] = []
|
|
140
|
+
if raw_tool_calls is not None:
|
|
141
|
+
if not isinstance(raw_tool_calls, list | tuple):
|
|
142
|
+
raise SFTDataError(f"message {index} tool_calls must be a list")
|
|
143
|
+
for call_index, call in enumerate(raw_tool_calls):
|
|
144
|
+
tool_calls.append(_coerce_tool_call(call, index=call_index))
|
|
145
|
+
|
|
146
|
+
tool_call_id = raw.get("tool_call_id")
|
|
147
|
+
if tool_call_id is not None and not isinstance(tool_call_id, str):
|
|
148
|
+
tool_call_id = str(tool_call_id)
|
|
149
|
+
|
|
150
|
+
name = raw.get("name")
|
|
151
|
+
if name is not None and not isinstance(name, str):
|
|
152
|
+
raise SFTDataError(f"message {index} name must be a string if present")
|
|
153
|
+
|
|
154
|
+
# NEW: Extract reasoning and raw_content if present
|
|
155
|
+
reasoning = raw.get("reasoning")
|
|
156
|
+
if reasoning is not None and not isinstance(reasoning, str):
|
|
157
|
+
raise SFTDataError(f"message {index} reasoning must be a string if present")
|
|
158
|
+
|
|
159
|
+
raw_content = raw.get("raw_content")
|
|
160
|
+
if raw_content is not None and not isinstance(raw_content, str):
|
|
161
|
+
raise SFTDataError(f"message {index} raw_content must be a string if present")
|
|
162
|
+
|
|
163
|
+
extra = {
|
|
164
|
+
key: value
|
|
165
|
+
for key, value in raw.items()
|
|
166
|
+
if key not in {"role", "content", "tool_calls", "tool_call_id", "name", "reasoning", "raw_content"}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return SFTMessage(
|
|
170
|
+
role=role,
|
|
171
|
+
content=content,
|
|
172
|
+
tool_calls=tool_calls,
|
|
173
|
+
tool_call_id=tool_call_id,
|
|
174
|
+
name=name,
|
|
175
|
+
reasoning=reasoning,
|
|
176
|
+
raw_content=raw_content,
|
|
177
|
+
extra=extra,
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def coerce_example(raw: Any, *, min_messages: int = 1) -> SFTExample:
|
|
182
|
+
if not isinstance(raw, dict):
|
|
183
|
+
raise SFTDataError("record is not an object")
|
|
184
|
+
|
|
185
|
+
messages_raw = raw.get("messages")
|
|
186
|
+
if not isinstance(messages_raw, Sequence):
|
|
187
|
+
raise SFTDataError("missing messages[] list")
|
|
188
|
+
if len(messages_raw) < min_messages:
|
|
189
|
+
raise SFTDataError(f"missing messages[] with at least {min_messages} turns")
|
|
190
|
+
|
|
191
|
+
messages = [_coerce_message(msg, index=i) for i, msg in enumerate(messages_raw)]
|
|
192
|
+
|
|
193
|
+
tools: list[SFTToolDefinition] = []
|
|
194
|
+
if "tools" in raw and raw["tools"] is not None:
|
|
195
|
+
tools_raw = raw["tools"]
|
|
196
|
+
if not isinstance(tools_raw, Sequence):
|
|
197
|
+
raise SFTDataError("tools must be provided as a list when present")
|
|
198
|
+
for tool_index, tool in enumerate(tools_raw):
|
|
199
|
+
tools.append(_coerce_tool_definition(tool, index=tool_index))
|
|
200
|
+
|
|
201
|
+
tool_choice = raw.get("tool_choice")
|
|
202
|
+
|
|
203
|
+
metadata_field = raw.get("metadata")
|
|
204
|
+
metadata: dict[str, Any] = {}
|
|
205
|
+
if metadata_field is not None:
|
|
206
|
+
if not isinstance(metadata_field, dict):
|
|
207
|
+
raise SFTDataError("metadata must be an object if present")
|
|
208
|
+
metadata = dict(metadata_field)
|
|
209
|
+
|
|
210
|
+
extra = {
|
|
211
|
+
key: value
|
|
212
|
+
for key, value in raw.items()
|
|
213
|
+
if key not in {"messages", "tools", "tool_choice", "metadata"}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return SFTExample(
|
|
217
|
+
messages=messages,
|
|
218
|
+
tools=tools,
|
|
219
|
+
tool_choice=tool_choice,
|
|
220
|
+
metadata=metadata,
|
|
221
|
+
extra=extra,
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def parse_jsonl_line(line: str, *, min_messages: int = 1) -> SFTExample:
|
|
226
|
+
record = json.loads(line)
|
|
227
|
+
return coerce_example(record, min_messages=min_messages)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def iter_sft_examples(
|
|
231
|
+
source: Iterable[str], *, min_messages: int = 1, skip_empty: bool = True
|
|
232
|
+
) -> Iterator[SFTExample]:
|
|
233
|
+
for line in source:
|
|
234
|
+
if skip_empty and not line.strip():
|
|
235
|
+
continue
|
|
236
|
+
yield parse_jsonl_line(line, min_messages=min_messages)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def collect_sft_jsonl_errors(
|
|
240
|
+
path: Path,
|
|
241
|
+
*,
|
|
242
|
+
min_messages: int = 1,
|
|
243
|
+
max_lines: int | None = None,
|
|
244
|
+
max_errors: int | None = None,
|
|
245
|
+
) -> list[str]:
|
|
246
|
+
errors: list[str] = []
|
|
247
|
+
lines_checked = 0
|
|
248
|
+
|
|
249
|
+
with path.open("r", encoding="utf-8") as fh:
|
|
250
|
+
for lineno, raw_line in enumerate(fh, start=1):
|
|
251
|
+
if max_lines is not None and lines_checked >= max_lines:
|
|
252
|
+
break
|
|
253
|
+
stripped = raw_line.strip()
|
|
254
|
+
if not stripped:
|
|
255
|
+
continue
|
|
256
|
+
lines_checked += 1
|
|
257
|
+
try:
|
|
258
|
+
parse_jsonl_line(stripped, min_messages=min_messages)
|
|
259
|
+
except json.JSONDecodeError as exc:
|
|
260
|
+
errors.append(f"Line {lineno}: invalid JSON ({exc.msg})")
|
|
261
|
+
except SFTDataError as exc:
|
|
262
|
+
errors.append(f"Line {lineno}: {exc}")
|
|
263
|
+
if max_errors is not None and len(errors) >= max_errors:
|
|
264
|
+
break
|
|
265
|
+
if lines_checked == 0 and (max_errors is None or len(errors) < max_errors):
|
|
266
|
+
errors.append("File contains no SFT examples")
|
|
267
|
+
return errors
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def validate_jsonl_or_raise(
|
|
271
|
+
path: Path,
|
|
272
|
+
*,
|
|
273
|
+
min_messages: int = 1,
|
|
274
|
+
max_lines: int | None = None,
|
|
275
|
+
max_errors: int | None = None,
|
|
276
|
+
error_factory: type[Exception] = ValueError,
|
|
277
|
+
) -> None:
|
|
278
|
+
if not path.exists():
|
|
279
|
+
raise FileNotFoundError(str(path))
|
|
280
|
+
|
|
281
|
+
issues = collect_sft_jsonl_errors(
|
|
282
|
+
path,
|
|
283
|
+
min_messages=min_messages,
|
|
284
|
+
max_lines=max_lines,
|
|
285
|
+
max_errors=max_errors,
|
|
286
|
+
)
|
|
287
|
+
if issues:
|
|
288
|
+
truncated = max_errors is not None and len(issues) >= max_errors
|
|
289
|
+
suffix = "" if not truncated else f" (showing first {max_errors} issues)"
|
|
290
|
+
details = "\n - ".join(issues)
|
|
291
|
+
raise error_factory(f"{path}: Dataset validation failed{suffix}:\n - {details}")
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def load_jsonl(path: Path, *, min_messages: int = 1) -> list[SFTExample]:
|
|
295
|
+
if not path.exists():
|
|
296
|
+
raise FileNotFoundError(str(path))
|
|
297
|
+
with path.open("r", encoding="utf-8") as fh:
|
|
298
|
+
return list(iter_sft_examples(fh, min_messages=min_messages))
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
# Reasoning/Thinking Utilities
|
|
302
|
+
# ============================================================================
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def extract_reasoning(content: str, *, tag: str = "think") -> tuple[str | None, str]:
|
|
306
|
+
"""Extract reasoning from content with <think> tags.
|
|
307
|
+
|
|
308
|
+
Args:
|
|
309
|
+
content: Raw content string
|
|
310
|
+
tag: Tag name to extract (default: "think")
|
|
311
|
+
|
|
312
|
+
Returns:
|
|
313
|
+
Tuple of (reasoning, clean_content)
|
|
314
|
+
- reasoning: Content inside tags, or None if no tags found
|
|
315
|
+
- clean_content: Content with tags removed
|
|
316
|
+
|
|
317
|
+
Examples:
|
|
318
|
+
>>> extract_reasoning("<think>Let me analyze...</think>The answer is 42")
|
|
319
|
+
('Let me analyze...', 'The answer is 42')
|
|
320
|
+
>>> extract_reasoning("Just plain text")
|
|
321
|
+
(None, 'Just plain text')
|
|
322
|
+
"""
|
|
323
|
+
import re
|
|
324
|
+
|
|
325
|
+
pattern = rf"<{tag}>(.*?)</{tag}>"
|
|
326
|
+
matches = re.findall(pattern, content, re.DOTALL)
|
|
327
|
+
|
|
328
|
+
if not matches:
|
|
329
|
+
return None, content
|
|
330
|
+
|
|
331
|
+
# Combine all reasoning blocks
|
|
332
|
+
reasoning = "\n\n".join(m.strip() for m in matches)
|
|
333
|
+
|
|
334
|
+
# Remove all reasoning blocks from content
|
|
335
|
+
clean_content = re.sub(pattern, "", content, flags=re.DOTALL).strip()
|
|
336
|
+
|
|
337
|
+
return reasoning, clean_content
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def strip_reasoning(content: str, *, tag: str = "think") -> str:
|
|
341
|
+
"""Remove reasoning tags from content.
|
|
342
|
+
|
|
343
|
+
Args:
|
|
344
|
+
content: Content with potential reasoning tags
|
|
345
|
+
tag: Tag name to strip (default: "think")
|
|
346
|
+
|
|
347
|
+
Returns:
|
|
348
|
+
Content with reasoning tags removed
|
|
349
|
+
"""
|
|
350
|
+
_, clean = extract_reasoning(content, tag=tag)
|
|
351
|
+
return clean
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def message_has_reasoning(message: SFTMessage) -> bool:
|
|
355
|
+
"""Check if a message has explicit reasoning.
|
|
356
|
+
|
|
357
|
+
Args:
|
|
358
|
+
message: SFTMessage to check
|
|
359
|
+
|
|
360
|
+
Returns:
|
|
361
|
+
True if message has reasoning field or <think> tags in content
|
|
362
|
+
"""
|
|
363
|
+
# Check explicit reasoning field
|
|
364
|
+
if message.reasoning:
|
|
365
|
+
return True
|
|
366
|
+
|
|
367
|
+
# Check for reasoning tags in content
|
|
368
|
+
if isinstance(message.content, str):
|
|
369
|
+
reasoning, _ = extract_reasoning(message.content)
|
|
370
|
+
return reasoning is not None
|
|
371
|
+
|
|
372
|
+
return False
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def validate_message_content(
|
|
376
|
+
message: SFTMessage, *, require_content: bool = True
|
|
377
|
+
) -> tuple[bool, str | None]:
|
|
378
|
+
"""Validate that message has valid content combinations.
|
|
379
|
+
|
|
380
|
+
Rules:
|
|
381
|
+
- Must have at least one of: reasoning + tool_calls, reasoning + content,
|
|
382
|
+
content, raw_content, or tool_calls
|
|
383
|
+
- If raw_content present with reasoning + content, they should be consistent
|
|
384
|
+
- Cannot have neither reasoning, content, raw_content, nor tool_calls
|
|
385
|
+
|
|
386
|
+
Args:
|
|
387
|
+
message: SFTMessage to validate
|
|
388
|
+
require_content: If True, require some form of content (default: True)
|
|
389
|
+
|
|
390
|
+
Returns:
|
|
391
|
+
Tuple of (is_valid, error_message)
|
|
392
|
+
"""
|
|
393
|
+
has_reasoning = bool(message.reasoning)
|
|
394
|
+
has_content = message.content is not None and message.content != ""
|
|
395
|
+
has_raw = bool(message.raw_content)
|
|
396
|
+
has_tools = len(message.tool_calls) > 0
|
|
397
|
+
|
|
398
|
+
# Check for completely empty message
|
|
399
|
+
if require_content and not (has_reasoning or has_content or has_raw or has_tools):
|
|
400
|
+
return False, "Message has no reasoning, content, raw_content, or tool_calls"
|
|
401
|
+
|
|
402
|
+
# Valid combinations:
|
|
403
|
+
# 1. reasoning + tool_calls (reasoning-based action)
|
|
404
|
+
if has_reasoning and has_tools:
|
|
405
|
+
return True, None
|
|
406
|
+
|
|
407
|
+
# 2. reasoning + content (reasoning then output)
|
|
408
|
+
if has_reasoning and has_content:
|
|
409
|
+
# If raw_content present, validate consistency
|
|
410
|
+
if has_raw and message.raw_content:
|
|
411
|
+
# Raw should contain both reasoning and content
|
|
412
|
+
reasoning_in_raw, content_in_raw = extract_reasoning(message.raw_content)
|
|
413
|
+
if message.reasoning and reasoning_in_raw != message.reasoning.strip():
|
|
414
|
+
logger.warning(
|
|
415
|
+
"raw_content reasoning doesn't match reasoning field"
|
|
416
|
+
)
|
|
417
|
+
# This is okay - just a warning, not an error
|
|
418
|
+
return True, None
|
|
419
|
+
|
|
420
|
+
# 3. content only (standard message)
|
|
421
|
+
if has_content and not has_reasoning:
|
|
422
|
+
return True, None
|
|
423
|
+
|
|
424
|
+
# 4. raw_content only (unparsed content)
|
|
425
|
+
if has_raw and not (has_reasoning and has_content):
|
|
426
|
+
return True, None
|
|
427
|
+
|
|
428
|
+
# 5. tool_calls only (action without reasoning/content - like OpenAI format)
|
|
429
|
+
if has_tools and not has_content:
|
|
430
|
+
return True, None
|
|
431
|
+
|
|
432
|
+
# 6. reasoning only (pure thinking turn)
|
|
433
|
+
if has_reasoning and not has_content and not has_tools:
|
|
434
|
+
return True, None
|
|
435
|
+
|
|
436
|
+
return True, None
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
# Vision/Multimodal Utilities
|
|
440
|
+
# ============================================================================
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
def has_image_content(content: SFTMessageContent) -> bool:
|
|
444
|
+
"""Check if message content contains image data (OpenAI multimodal format).
|
|
445
|
+
|
|
446
|
+
Supports:
|
|
447
|
+
- List of content parts: [{"type": "text", ...}, {"type": "image_url", ...}]
|
|
448
|
+
- Single dict with type field: {"type": "image_url", "image_url": {...}}
|
|
449
|
+
|
|
450
|
+
Args:
|
|
451
|
+
content: Message content (can be str, list, dict, or None)
|
|
452
|
+
|
|
453
|
+
Returns:
|
|
454
|
+
True if content contains an image segment
|
|
455
|
+
|
|
456
|
+
Examples:
|
|
457
|
+
>>> has_image_content([{"type": "text", "text": "What's this?"},
|
|
458
|
+
... {"type": "image_url", "image_url": {"url": "..."}}])
|
|
459
|
+
True
|
|
460
|
+
>>> has_image_content("Just text")
|
|
461
|
+
False
|
|
462
|
+
"""
|
|
463
|
+
if isinstance(content, list):
|
|
464
|
+
return any(
|
|
465
|
+
isinstance(part, dict) and part.get("type") in {"image", "image_url"}
|
|
466
|
+
for part in content
|
|
467
|
+
)
|
|
468
|
+
elif isinstance(content, dict):
|
|
469
|
+
return content.get("type") in {"image", "image_url"}
|
|
470
|
+
return False
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
def message_has_image(message: SFTMessage) -> bool:
|
|
474
|
+
"""Check if an SFTMessage contains image content.
|
|
475
|
+
|
|
476
|
+
Args:
|
|
477
|
+
message: SFTMessage to check
|
|
478
|
+
|
|
479
|
+
Returns:
|
|
480
|
+
True if the message contains image content
|
|
481
|
+
"""
|
|
482
|
+
return has_image_content(message.content)
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
def example_has_image(example: SFTExample) -> bool:
|
|
486
|
+
"""Check if an SFTExample contains any image content.
|
|
487
|
+
|
|
488
|
+
Args:
|
|
489
|
+
example: SFTExample to check
|
|
490
|
+
|
|
491
|
+
Returns:
|
|
492
|
+
True if any message in the example contains image content
|
|
493
|
+
"""
|
|
494
|
+
return any(message_has_image(msg) for msg in example.messages)
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
def count_images_in_content(content: SFTMessageContent) -> int:
|
|
498
|
+
"""Count the number of images in message content.
|
|
499
|
+
|
|
500
|
+
Args:
|
|
501
|
+
content: Message content to analyze
|
|
502
|
+
|
|
503
|
+
Returns:
|
|
504
|
+
Number of image segments found
|
|
505
|
+
"""
|
|
506
|
+
if isinstance(content, list):
|
|
507
|
+
return sum(
|
|
508
|
+
1 for part in content
|
|
509
|
+
if isinstance(part, dict) and part.get("type") in {"image", "image_url"}
|
|
510
|
+
)
|
|
511
|
+
elif isinstance(content, dict) and content.get("type") in {"image", "image_url"}:
|
|
512
|
+
return 1
|
|
513
|
+
return 0
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
def extract_image_urls(content: SFTMessageContent) -> list[str]:
|
|
517
|
+
"""Extract all image URLs from message content.
|
|
518
|
+
|
|
519
|
+
Filters out invalid entries:
|
|
520
|
+
- Non-string URLs
|
|
521
|
+
- Empty strings
|
|
522
|
+
- Whitespace-only strings
|
|
523
|
+
|
|
524
|
+
Args:
|
|
525
|
+
content: Message content to extract from
|
|
526
|
+
|
|
527
|
+
Returns:
|
|
528
|
+
List of valid image URL strings (may be http(s):// URLs or data:image/... base64)
|
|
529
|
+
"""
|
|
530
|
+
urls: list[str] = []
|
|
531
|
+
|
|
532
|
+
if isinstance(content, list):
|
|
533
|
+
for part in content:
|
|
534
|
+
if isinstance(part, dict) and part.get("type") in {"image", "image_url"}:
|
|
535
|
+
# Handle both formats:
|
|
536
|
+
# {"type": "image_url", "image_url": {"url": "..."}}
|
|
537
|
+
# {"type": "image", "image": "..."}
|
|
538
|
+
if "image_url" in part and isinstance(part["image_url"], dict):
|
|
539
|
+
url = part["image_url"].get("url")
|
|
540
|
+
if isinstance(url, str) and url.strip(): # Filter empty/whitespace
|
|
541
|
+
urls.append(url)
|
|
542
|
+
elif "image" in part and isinstance(part["image"], str):
|
|
543
|
+
if part["image"].strip(): # Filter empty/whitespace
|
|
544
|
+
urls.append(part["image"])
|
|
545
|
+
elif isinstance(content, dict) and content.get("type") in {"image", "image_url"}:
|
|
546
|
+
image_url_data = content.get("image_url")
|
|
547
|
+
if isinstance(image_url_data, dict):
|
|
548
|
+
url = image_url_data.get("url")
|
|
549
|
+
if isinstance(url, str) and url.strip(): # Filter empty/whitespace
|
|
550
|
+
urls.append(url)
|
|
551
|
+
else:
|
|
552
|
+
image_value = content.get("image")
|
|
553
|
+
if isinstance(image_value, str) and image_value.strip(): # Filter empty/whitespace
|
|
554
|
+
urls.append(image_value)
|
|
555
|
+
|
|
556
|
+
return urls
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
def validate_vision_example(
|
|
560
|
+
example: SFTExample, *, require_images: bool = True
|
|
561
|
+
) -> tuple[bool, str | None]:
|
|
562
|
+
"""Validate a vision SFT example.
|
|
563
|
+
|
|
564
|
+
Checks:
|
|
565
|
+
- If require_images is True, at least one message must contain an image
|
|
566
|
+
- All image URLs must be non-empty, non-whitespace strings
|
|
567
|
+
- Image entries must have valid URL data
|
|
568
|
+
- Messages must follow valid structure
|
|
569
|
+
|
|
570
|
+
Args:
|
|
571
|
+
example: SFTExample to validate
|
|
572
|
+
require_images: If True, fail if no images are present
|
|
573
|
+
|
|
574
|
+
Returns:
|
|
575
|
+
Tuple of (is_valid, error_message)
|
|
576
|
+
If valid, error_message is None
|
|
577
|
+
"""
|
|
578
|
+
# Count actual valid URLs and detect any invalid entries
|
|
579
|
+
total_valid_urls = 0
|
|
580
|
+
|
|
581
|
+
# Validate image URLs in each message
|
|
582
|
+
for i, msg in enumerate(example.messages):
|
|
583
|
+
# Check if this message has image_url type entries
|
|
584
|
+
if not isinstance(msg.content, list | dict):
|
|
585
|
+
continue
|
|
586
|
+
|
|
587
|
+
# Count image_url type entries vs valid URLs
|
|
588
|
+
content_list = msg.content if isinstance(msg.content, list) else [msg.content]
|
|
589
|
+
image_type_count = sum(
|
|
590
|
+
1 for item in content_list
|
|
591
|
+
if isinstance(item, dict) and item.get("type") in {"image", "image_url"}
|
|
592
|
+
)
|
|
593
|
+
|
|
594
|
+
if image_type_count > 0:
|
|
595
|
+
# Extract valid URLs (after filtering)
|
|
596
|
+
urls = extract_image_urls(msg.content)
|
|
597
|
+
|
|
598
|
+
# If we have image_url type entries but fewer valid URLs, some are invalid
|
|
599
|
+
if len(urls) < image_type_count:
|
|
600
|
+
return False, f"Message {i}: Has {image_type_count} image_url entries but only {len(urls)} valid URLs (some are empty, null, or missing)"
|
|
601
|
+
|
|
602
|
+
# Validate each URL (double-check, though extract_image_urls should have filtered)
|
|
603
|
+
for url in urls:
|
|
604
|
+
# extract_image_urls already filters for isinstance(url, str) and url.strip()
|
|
605
|
+
# but let's be defensive
|
|
606
|
+
if not isinstance(url, str):
|
|
607
|
+
return False, f"Message {i}: Image URL is not a string: {type(url)}"
|
|
608
|
+
|
|
609
|
+
if not url.strip():
|
|
610
|
+
return False, f"Message {i}: Invalid or empty image URL"
|
|
611
|
+
|
|
612
|
+
# Basic URL format check
|
|
613
|
+
if not url.startswith(("http://", "https://", "data:image/")):
|
|
614
|
+
logger.warning(
|
|
615
|
+
f"Message {i}: Image URL doesn't start with http://, https://, or data:image/ - "
|
|
616
|
+
f"this may cause issues during training. URL: {url[:100]}"
|
|
617
|
+
)
|
|
618
|
+
|
|
619
|
+
total_valid_urls += 1
|
|
620
|
+
|
|
621
|
+
# Final check: if images are required, ensure we found at least one valid URL
|
|
622
|
+
if require_images and total_valid_urls == 0:
|
|
623
|
+
return False, "No image content found in any message"
|
|
624
|
+
|
|
625
|
+
return True, None
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
def iter_vision_examples(
|
|
629
|
+
source: Iterable[str],
|
|
630
|
+
*,
|
|
631
|
+
min_messages: int = 1,
|
|
632
|
+
skip_empty: bool = True,
|
|
633
|
+
require_images: bool = True,
|
|
634
|
+
log_validation_errors: bool = False,
|
|
635
|
+
) -> Iterator[SFTExample]:
|
|
636
|
+
"""Iterate over vision SFT examples from JSONL source.
|
|
637
|
+
|
|
638
|
+
Similar to iter_sft_examples but with vision-specific validation.
|
|
639
|
+
|
|
640
|
+
Args:
|
|
641
|
+
source: Iterable of JSONL lines
|
|
642
|
+
min_messages: Minimum number of messages required
|
|
643
|
+
skip_empty: Skip empty lines
|
|
644
|
+
require_images: If True, skip examples without images
|
|
645
|
+
log_validation_errors: If True, log validation failures
|
|
646
|
+
|
|
647
|
+
Yields:
|
|
648
|
+
Valid vision SFTExample objects
|
|
649
|
+
"""
|
|
650
|
+
for line in source:
|
|
651
|
+
if skip_empty and not line.strip():
|
|
652
|
+
continue
|
|
653
|
+
|
|
654
|
+
try:
|
|
655
|
+
example = parse_jsonl_line(line, min_messages=min_messages)
|
|
656
|
+
|
|
657
|
+
# Validate vision content if required
|
|
658
|
+
if require_images:
|
|
659
|
+
is_valid, error = validate_vision_example(example, require_images=True)
|
|
660
|
+
if not is_valid:
|
|
661
|
+
if log_validation_errors:
|
|
662
|
+
logger.warning(f"Skipping invalid vision example: {error}")
|
|
663
|
+
continue
|
|
664
|
+
|
|
665
|
+
yield example
|
|
666
|
+
|
|
667
|
+
except (json.JSONDecodeError, SFTDataError) as exc:
|
|
668
|
+
if log_validation_errors:
|
|
669
|
+
logger.warning(f"Failed to parse vision example: {exc}")
|
|
670
|
+
continue
|
|
671
|
+
|
|
672
|
+
|
|
673
|
+
__all__ = [
|
|
674
|
+
"SFTDataError",
|
|
675
|
+
"SFTExample",
|
|
676
|
+
"SFTMessage",
|
|
677
|
+
"SFTToolCall",
|
|
678
|
+
"SFTToolDefinition",
|
|
679
|
+
"collect_sft_jsonl_errors",
|
|
680
|
+
"coerce_example",
|
|
681
|
+
"iter_sft_examples",
|
|
682
|
+
"load_jsonl",
|
|
683
|
+
"parse_jsonl_line",
|
|
684
|
+
"validate_jsonl_or_raise",
|
|
685
|
+
# Reasoning utilities
|
|
686
|
+
"extract_reasoning",
|
|
687
|
+
"strip_reasoning",
|
|
688
|
+
"message_has_reasoning",
|
|
689
|
+
"validate_message_content",
|
|
690
|
+
# Vision utilities
|
|
691
|
+
"has_image_content",
|
|
692
|
+
"message_has_image",
|
|
693
|
+
"example_has_image",
|
|
694
|
+
"count_images_in_content",
|
|
695
|
+
"extract_image_urls",
|
|
696
|
+
"validate_vision_example",
|
|
697
|
+
"iter_vision_examples",
|
|
698
|
+
]
|