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
synth_ai/tracing_v3/utils.py
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
1
|
"""Utility functions for tracing v3."""
|
|
3
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
4
5
|
import hashlib
|
|
5
6
|
import json
|
|
6
7
|
import uuid
|
|
7
|
-
from datetime import datetime
|
|
8
|
+
from datetime import UTC, datetime
|
|
8
9
|
from typing import Any
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
def iso_now() -> str:
|
|
12
|
-
"""Get current
|
|
13
|
-
return datetime.
|
|
13
|
+
"""Get current timezone.utc time as ISO format string."""
|
|
14
|
+
return datetime.now(UTC).isoformat()
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
def json_dumps(obj: Any) -> str:
|
synth_ai/tunnel.py
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Cloudflare Tunnel client module.
|
|
3
|
+
|
|
4
|
+
Provides functions to spawn and manage cloudflared processes for quick and managed tunnels.
|
|
5
|
+
"""
|
|
6
|
+
import os
|
|
7
|
+
import re
|
|
8
|
+
import shutil
|
|
9
|
+
import subprocess
|
|
10
|
+
import time
|
|
11
|
+
from typing import Optional, Tuple
|
|
12
|
+
|
|
13
|
+
_URL_RE = re.compile(r"https://[a-z0-9-]+\.trycloudflare\.com", re.I)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _which_cloudflared() -> str:
|
|
17
|
+
"""Find cloudflared binary in PATH or common install locations."""
|
|
18
|
+
path = shutil.which("cloudflared")
|
|
19
|
+
if path:
|
|
20
|
+
return path
|
|
21
|
+
|
|
22
|
+
# Check common install locations
|
|
23
|
+
common_paths = [
|
|
24
|
+
"/usr/local/bin/cloudflared",
|
|
25
|
+
"/opt/homebrew/bin/cloudflared",
|
|
26
|
+
os.path.expanduser("~/bin/cloudflared"),
|
|
27
|
+
]
|
|
28
|
+
for common_path in common_paths:
|
|
29
|
+
if os.path.exists(common_path) and os.access(common_path, os.X_OK):
|
|
30
|
+
return common_path
|
|
31
|
+
|
|
32
|
+
raise FileNotFoundError(
|
|
33
|
+
"cloudflared not found. Install it:\n"
|
|
34
|
+
" macOS: brew install cloudflare/cloudflare/cloudflared\n"
|
|
35
|
+
" Linux/Windows: https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/create-local-tunnel/"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def open_quick_tunnel(port: int, wait_s: float = 10.0) -> Tuple[str, subprocess.Popen]:
|
|
40
|
+
"""
|
|
41
|
+
Open a quick (ephemeral) Cloudflare tunnel.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
port: Local port to tunnel to
|
|
45
|
+
wait_s: Maximum time to wait for URL in seconds
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
Tuple of (public_url, process_handle)
|
|
49
|
+
|
|
50
|
+
Raises:
|
|
51
|
+
FileNotFoundError: If cloudflared is not installed
|
|
52
|
+
RuntimeError: If tunnel fails to start or URL cannot be parsed
|
|
53
|
+
"""
|
|
54
|
+
bin_path = _which_cloudflared()
|
|
55
|
+
proc = subprocess.Popen(
|
|
56
|
+
[bin_path, "tunnel", "--url", f"http://127.0.0.1:{port}"],
|
|
57
|
+
stdout=subprocess.PIPE,
|
|
58
|
+
stderr=subprocess.STDOUT,
|
|
59
|
+
text=True,
|
|
60
|
+
bufsize=1,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
start = time.time()
|
|
64
|
+
url: Optional[str] = None
|
|
65
|
+
|
|
66
|
+
# Stream stdout to detect the trycloudflare URL
|
|
67
|
+
while time.time() - start < wait_s:
|
|
68
|
+
if proc.poll() is not None:
|
|
69
|
+
# Process exited early
|
|
70
|
+
stdout, _ = proc.communicate()
|
|
71
|
+
raise RuntimeError(
|
|
72
|
+
f"cloudflared exited early with code {proc.returncode}. "
|
|
73
|
+
f"Output: {stdout[:500] if stdout else 'no output'}"
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
if proc.stdout is None:
|
|
77
|
+
raise RuntimeError("cloudflared process has no stdout")
|
|
78
|
+
|
|
79
|
+
line = proc.stdout.readline()
|
|
80
|
+
if not line:
|
|
81
|
+
time.sleep(0.05)
|
|
82
|
+
continue
|
|
83
|
+
|
|
84
|
+
match = _URL_RE.search(line)
|
|
85
|
+
if match:
|
|
86
|
+
url = match.group(0)
|
|
87
|
+
break
|
|
88
|
+
|
|
89
|
+
if not url:
|
|
90
|
+
proc.terminate()
|
|
91
|
+
stdout, _ = proc.communicate(timeout=2.0)
|
|
92
|
+
raise RuntimeError(
|
|
93
|
+
f"Failed to parse trycloudflare URL from cloudflared output after {wait_s}s. "
|
|
94
|
+
f"Output: {stdout[:500] if stdout else 'no output'}"
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
return url, proc
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def open_managed_tunnel(tunnel_token: str) -> subprocess.Popen:
|
|
101
|
+
"""
|
|
102
|
+
Open a managed (named) Cloudflare tunnel using a token.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
tunnel_token: Cloudflare tunnel token from backend API
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
Process handle for the tunnel
|
|
109
|
+
|
|
110
|
+
Raises:
|
|
111
|
+
FileNotFoundError: If cloudflared is not installed
|
|
112
|
+
"""
|
|
113
|
+
bin_path = _which_cloudflared()
|
|
114
|
+
# cloudflared v2023.4+ accepts --token for named tunnels
|
|
115
|
+
return subprocess.Popen(
|
|
116
|
+
[bin_path, "tunnel", "run", "--token", tunnel_token],
|
|
117
|
+
stdout=subprocess.PIPE,
|
|
118
|
+
stderr=subprocess.STDOUT,
|
|
119
|
+
text=True,
|
|
120
|
+
bufsize=1,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def stop_tunnel(proc: Optional[subprocess.Popen]) -> None:
|
|
125
|
+
"""
|
|
126
|
+
Gracefully stop a tunnel process.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
proc: Process handle to terminate, or None
|
|
130
|
+
"""
|
|
131
|
+
if proc is None:
|
|
132
|
+
return
|
|
133
|
+
|
|
134
|
+
if proc.poll() is None:
|
|
135
|
+
# Process is still running
|
|
136
|
+
proc.terminate()
|
|
137
|
+
try:
|
|
138
|
+
proc.wait(timeout=5.0)
|
|
139
|
+
except subprocess.TimeoutExpired:
|
|
140
|
+
# Force kill if graceful termination fails
|
|
141
|
+
proc.kill()
|
|
142
|
+
proc.wait()
|
|
143
|
+
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Cloudflare Tunnel deployment handler.
|
|
3
|
+
|
|
4
|
+
Manages the full lifecycle of deploying a task app via Cloudflare Tunnel:
|
|
5
|
+
1. Start local task app (uvicorn) in background
|
|
6
|
+
2. Wait for health check
|
|
7
|
+
3. Open tunnel (quick or managed)
|
|
8
|
+
4. Write credentials to .env
|
|
9
|
+
5. Optionally keep processes alive (blocking mode)
|
|
10
|
+
|
|
11
|
+
This module provides a clean abstraction that shields users from:
|
|
12
|
+
- Process management details (uvicorn threads, cloudflared subprocesses)
|
|
13
|
+
- Health check polling logic
|
|
14
|
+
- Credential storage
|
|
15
|
+
- Cleanup on errors
|
|
16
|
+
"""
|
|
17
|
+
import asyncio
|
|
18
|
+
import os
|
|
19
|
+
import signal
|
|
20
|
+
import subprocess
|
|
21
|
+
import sys
|
|
22
|
+
import time
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
from typing import Optional
|
|
25
|
+
|
|
26
|
+
import httpx
|
|
27
|
+
from synth_ai.cfgs import CloudflareTunnelDeployCfg
|
|
28
|
+
from synth_ai.tunnel import open_managed_tunnel, open_quick_tunnel, stop_tunnel
|
|
29
|
+
from synth_ai.utils.apps import get_asgi_app, load_file_to_module
|
|
30
|
+
from synth_ai.utils.env import resolve_env_var
|
|
31
|
+
from synth_ai.utils.paths import REPO_ROOT, configure_import_paths
|
|
32
|
+
from synth_ai.utils.tunnel import store_tunnel_credentials
|
|
33
|
+
|
|
34
|
+
import uvicorn
|
|
35
|
+
from uvicorn._types import ASGIApplication
|
|
36
|
+
|
|
37
|
+
_TUNNEL_PROCESSES: dict[int, subprocess.Popen] = {} # Store tunnel process handles for cleanup
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
async def _wait_for_health_check(
|
|
41
|
+
host: str,
|
|
42
|
+
port: int,
|
|
43
|
+
api_key: str,
|
|
44
|
+
timeout: float = 30.0,
|
|
45
|
+
) -> None:
|
|
46
|
+
"""
|
|
47
|
+
Wait for task app health endpoint to be ready.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
host: Host to check
|
|
51
|
+
port: Port to check
|
|
52
|
+
api_key: API key for authentication
|
|
53
|
+
timeout: Maximum time to wait in seconds
|
|
54
|
+
|
|
55
|
+
Raises:
|
|
56
|
+
RuntimeError: If health check fails or times out
|
|
57
|
+
"""
|
|
58
|
+
health_url = f"http://{host}:{port}/health"
|
|
59
|
+
headers = {"X-API-Key": api_key}
|
|
60
|
+
start = time.time()
|
|
61
|
+
|
|
62
|
+
while time.time() - start < timeout:
|
|
63
|
+
try:
|
|
64
|
+
async with httpx.AsyncClient(timeout=5.0) as client:
|
|
65
|
+
response = await client.get(health_url, headers=headers)
|
|
66
|
+
# Accept both 200 (success) and 400 (auth error means server is up)
|
|
67
|
+
if response.status_code in (200, 400):
|
|
68
|
+
return
|
|
69
|
+
except (httpx.RequestError, httpx.TimeoutException):
|
|
70
|
+
pass
|
|
71
|
+
|
|
72
|
+
await asyncio.sleep(0.5)
|
|
73
|
+
|
|
74
|
+
raise RuntimeError(
|
|
75
|
+
f"Health check failed: {health_url} not ready after {timeout}s. "
|
|
76
|
+
"Make sure your task app has a /health endpoint."
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _start_uvicorn_background(
|
|
81
|
+
app: ASGIApplication,
|
|
82
|
+
host: str,
|
|
83
|
+
port: int,
|
|
84
|
+
daemon: bool = True,
|
|
85
|
+
) -> None:
|
|
86
|
+
"""
|
|
87
|
+
Start uvicorn server in a background thread.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
app: ASGI application
|
|
91
|
+
host: Host to bind to
|
|
92
|
+
port: Port to bind to
|
|
93
|
+
daemon: If True, thread dies when main process exits. If False, thread keeps running.
|
|
94
|
+
"""
|
|
95
|
+
import threading
|
|
96
|
+
|
|
97
|
+
def serve():
|
|
98
|
+
try:
|
|
99
|
+
uvicorn.run(
|
|
100
|
+
app,
|
|
101
|
+
host=host,
|
|
102
|
+
port=port,
|
|
103
|
+
reload=False,
|
|
104
|
+
log_level="info",
|
|
105
|
+
)
|
|
106
|
+
except Exception as exc:
|
|
107
|
+
# Log error but don't raise (background thread)
|
|
108
|
+
print(f"Uvicorn error: {exc}", flush=True)
|
|
109
|
+
|
|
110
|
+
thread = threading.Thread(
|
|
111
|
+
target=serve,
|
|
112
|
+
name=f"synth-uvicorn-tunnel-{port}",
|
|
113
|
+
daemon=daemon,
|
|
114
|
+
)
|
|
115
|
+
thread.start()
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
async def deploy_app_tunnel(
|
|
119
|
+
cfg: CloudflareTunnelDeployCfg,
|
|
120
|
+
env_file: Optional[Path] = None,
|
|
121
|
+
keep_alive: bool = False,
|
|
122
|
+
) -> str:
|
|
123
|
+
"""
|
|
124
|
+
Deploy task app via Cloudflare Tunnel.
|
|
125
|
+
|
|
126
|
+
This function provides a clean abstraction that handles:
|
|
127
|
+
1. Starting the local task app (uvicorn) in background
|
|
128
|
+
2. Waiting for health check
|
|
129
|
+
3. Opening tunnel (quick or managed)
|
|
130
|
+
4. Writing tunnel URL and Access credentials to .env
|
|
131
|
+
5. Optionally keeping processes alive (blocking vs non-blocking mode)
|
|
132
|
+
|
|
133
|
+
When `keep_alive=True`, this function blocks and keeps the tunnel running
|
|
134
|
+
until interrupted (Ctrl+C). This is similar to how `deploy_app_uvicorn`
|
|
135
|
+
blocks for local deployments.
|
|
136
|
+
|
|
137
|
+
When `keep_alive=False`, this function returns immediately after deployment.
|
|
138
|
+
Processes run in the background and will continue until explicitly stopped
|
|
139
|
+
or the parent process exits. Use this for headless/background deployments.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
cfg: Tunnel deployment configuration
|
|
143
|
+
env_file: Optional path to .env file (defaults to .env in current directory)
|
|
144
|
+
keep_alive: If True, block and keep tunnel alive until interrupted.
|
|
145
|
+
If False, return immediately after deployment (background mode).
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
Public tunnel URL
|
|
149
|
+
|
|
150
|
+
Raises:
|
|
151
|
+
RuntimeError: If deployment fails at any step
|
|
152
|
+
|
|
153
|
+
Example:
|
|
154
|
+
# Non-blocking (background mode, returns immediately)
|
|
155
|
+
url = await deploy_app_tunnel(cfg, keep_alive=False)
|
|
156
|
+
|
|
157
|
+
# Blocking (keeps tunnel alive, similar to local deployment)
|
|
158
|
+
url = await deploy_app_tunnel(cfg, keep_alive=True)
|
|
159
|
+
"""
|
|
160
|
+
# Set up environment
|
|
161
|
+
os.environ["ENVIRONMENT_API_KEY"] = cfg.env_api_key
|
|
162
|
+
if cfg.trace:
|
|
163
|
+
os.environ["TASKAPP_TRACING_ENABLED"] = "1"
|
|
164
|
+
else:
|
|
165
|
+
os.environ.pop("TASKAPP_TRACING_ENABLED", None)
|
|
166
|
+
|
|
167
|
+
# Load and configure task app
|
|
168
|
+
configure_import_paths(cfg.task_app_path, REPO_ROOT)
|
|
169
|
+
module = load_file_to_module(
|
|
170
|
+
cfg.task_app_path,
|
|
171
|
+
f"_synth_tunnel_task_app_{cfg.task_app_path.stem}",
|
|
172
|
+
)
|
|
173
|
+
app = get_asgi_app(module)
|
|
174
|
+
|
|
175
|
+
# Start uvicorn in background
|
|
176
|
+
# Use daemon=False if keep_alive=True (so thread doesn't die when we block)
|
|
177
|
+
# Use daemon=True if keep_alive=False (background mode, dies with parent)
|
|
178
|
+
_start_uvicorn_background(app, cfg.host, cfg.port, daemon=not keep_alive)
|
|
179
|
+
|
|
180
|
+
# Wait for health check (with API key for authentication)
|
|
181
|
+
await _wait_for_health_check(cfg.host, cfg.port, cfg.env_api_key)
|
|
182
|
+
|
|
183
|
+
tunnel_proc = None
|
|
184
|
+
try:
|
|
185
|
+
if cfg.mode == "quick":
|
|
186
|
+
# Quick tunnel: ephemeral, no backend API call
|
|
187
|
+
url, tunnel_proc = open_quick_tunnel(cfg.port)
|
|
188
|
+
_TUNNEL_PROCESSES[cfg.port] = tunnel_proc
|
|
189
|
+
store_tunnel_credentials(url, None, None, env_file)
|
|
190
|
+
else:
|
|
191
|
+
# Managed tunnel: provision via backend API
|
|
192
|
+
try:
|
|
193
|
+
from synth_ai.api.tunnel import create_tunnel # type: ignore[import-untyped]
|
|
194
|
+
except ImportError as err:
|
|
195
|
+
raise RuntimeError(
|
|
196
|
+
"Managed tunnel mode requires synth_ai.api.tunnel module. "
|
|
197
|
+
"This is only available in the managed tunnel implementation."
|
|
198
|
+
) from err
|
|
199
|
+
|
|
200
|
+
synth_api_key = resolve_env_var("SYNTH_API_KEY")
|
|
201
|
+
data = await create_tunnel(synth_api_key, cfg.port, cfg.subdomain)
|
|
202
|
+
|
|
203
|
+
tunnel_token = data["tunnel_token"]
|
|
204
|
+
hostname = data["hostname"]
|
|
205
|
+
access_client_id = data.get("access_client_id")
|
|
206
|
+
access_client_secret = data.get("access_client_secret")
|
|
207
|
+
|
|
208
|
+
tunnel_proc = open_managed_tunnel(tunnel_token)
|
|
209
|
+
_TUNNEL_PROCESSES[cfg.port] = tunnel_proc
|
|
210
|
+
|
|
211
|
+
url = f"https://{hostname}"
|
|
212
|
+
store_tunnel_credentials(url, access_client_id, access_client_secret, env_file)
|
|
213
|
+
|
|
214
|
+
# If keep_alive is True, block and keep processes alive until interrupted
|
|
215
|
+
if keep_alive:
|
|
216
|
+
_keep_tunnel_alive(cfg.port, url)
|
|
217
|
+
else:
|
|
218
|
+
# Background mode: print URL and return immediately
|
|
219
|
+
# Processes will keep running in background
|
|
220
|
+
print(f"✓ Tunnel ready: {url}")
|
|
221
|
+
print(f"⏳ Tunnel running in background (PID: {tunnel_proc.pid if tunnel_proc else 'N/A'})")
|
|
222
|
+
print(" Press Ctrl+C in this process to stop, or use: pkill -f cloudflared")
|
|
223
|
+
|
|
224
|
+
return url
|
|
225
|
+
|
|
226
|
+
except Exception as exc:
|
|
227
|
+
# Clean up tunnel process on error
|
|
228
|
+
if tunnel_proc:
|
|
229
|
+
stop_tunnel(tunnel_proc)
|
|
230
|
+
_TUNNEL_PROCESSES.pop(cfg.port, None)
|
|
231
|
+
raise RuntimeError(f"Failed to deploy tunnel: {exc}") from exc
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def _keep_tunnel_alive(port: int, url: str) -> None:
|
|
235
|
+
"""
|
|
236
|
+
Keep tunnel processes alive until interrupted.
|
|
237
|
+
|
|
238
|
+
This function blocks and monitors the tunnel process, similar to how
|
|
239
|
+
local deployments block. Users can interrupt with Ctrl+C to stop.
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
port: Port the tunnel is running on
|
|
243
|
+
url: Public tunnel URL (for display)
|
|
244
|
+
"""
|
|
245
|
+
import subprocess
|
|
246
|
+
|
|
247
|
+
def signal_handler(signum, frame): # noqa: ARG001
|
|
248
|
+
"""Handle SIGINT/SIGTERM to cleanup gracefully."""
|
|
249
|
+
if port in _TUNNEL_PROCESSES:
|
|
250
|
+
stop_tunnel(_TUNNEL_PROCESSES[port])
|
|
251
|
+
_TUNNEL_PROCESSES.pop(port, None)
|
|
252
|
+
sys.exit(0)
|
|
253
|
+
|
|
254
|
+
# Register signal handlers for graceful shutdown
|
|
255
|
+
signal.signal(signal.SIGINT, signal_handler)
|
|
256
|
+
signal.signal(signal.SIGTERM, signal_handler)
|
|
257
|
+
|
|
258
|
+
print(f"✓ Tunnel ready: {url}")
|
|
259
|
+
print("⏳ Keeping tunnel running... (Press Ctrl+C to stop)")
|
|
260
|
+
|
|
261
|
+
try:
|
|
262
|
+
# Monitor tunnel process and keep alive
|
|
263
|
+
while True:
|
|
264
|
+
if port in _TUNNEL_PROCESSES:
|
|
265
|
+
proc = _TUNNEL_PROCESSES[port]
|
|
266
|
+
if isinstance(proc, subprocess.Popen) and proc.poll() is not None:
|
|
267
|
+
print(f"❌ Tunnel process exited with code {proc.returncode}")
|
|
268
|
+
break
|
|
269
|
+
time.sleep(1)
|
|
270
|
+
except KeyboardInterrupt:
|
|
271
|
+
pass
|
|
272
|
+
finally:
|
|
273
|
+
# Cleanup on exit
|
|
274
|
+
if port in _TUNNEL_PROCESSES:
|
|
275
|
+
stop_tunnel(_TUNNEL_PROCESSES[port])
|
|
276
|
+
_TUNNEL_PROCESSES.pop(port, None)
|
|
277
|
+
print("\n🛑 Tunnel stopped")
|
|
278
|
+
|
synth_ai/types.py
ADDED
synth_ai/urls.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Base URL for all backends
|
|
2
|
+
BACKEND_URL_BASE = "https://agent-learning.onrender.com"
|
|
3
|
+
|
|
4
|
+
# Synth Research API base (supports OpenAI, Anthropic, and custom formats)
|
|
5
|
+
# Real routes: /api/synth-research/chat/completions, /api/synth-research/messages
|
|
6
|
+
# V1 routes: /api/synth-research/v1/chat/completions, /api/synth-research/v1/messages
|
|
7
|
+
BACKEND_URL_SYNTH_RESEARCH_BASE = BACKEND_URL_BASE + "/api/synth-research"
|
|
8
|
+
|
|
9
|
+
# Provider-specific URLs (for SDKs that expect standard paths)
|
|
10
|
+
BACKEND_URL_SYNTH_RESEARCH_OPENAI = BACKEND_URL_SYNTH_RESEARCH_BASE + "/v1" # For OpenAI SDKs (appends /chat/completions)
|
|
11
|
+
BACKEND_URL_SYNTH_RESEARCH_ANTHROPIC = BACKEND_URL_SYNTH_RESEARCH_BASE # For Anthropic SDKs (appends /v1/messages)
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
from . import task_app_state
|
|
2
|
+
from .agents import write_agents_md
|
|
3
|
+
from .apps import (
|
|
4
|
+
extract_routes_from_app,
|
|
5
|
+
get_asgi_app,
|
|
6
|
+
load_file_to_module,
|
|
7
|
+
validate_modal_app,
|
|
8
|
+
validate_task_app,
|
|
9
|
+
)
|
|
10
|
+
from .base_url import PROD_BASE_URL_DEFAULT, get_backend_from_env, get_learning_v2_base_url
|
|
11
|
+
from .bin import install_bin, verify_bin
|
|
12
|
+
from .claude import ClaudeConfig
|
|
13
|
+
from .cli import (
|
|
14
|
+
PromptedChoiceOption,
|
|
15
|
+
PromptedChoiceType,
|
|
16
|
+
PromptedPathOption,
|
|
17
|
+
print_next_step,
|
|
18
|
+
prompt_choice,
|
|
19
|
+
prompt_for_path,
|
|
20
|
+
)
|
|
21
|
+
from .env import (
|
|
22
|
+
mask_str,
|
|
23
|
+
read_env_var_from_file,
|
|
24
|
+
resolve_env_var,
|
|
25
|
+
write_env_var_to_dotenv,
|
|
26
|
+
write_env_var_to_json,
|
|
27
|
+
)
|
|
28
|
+
from .http import AsyncHttpClient, HTTPError, http_request
|
|
29
|
+
from .json import create_and_write_json, load_json_to_dict, strip_json_comments
|
|
30
|
+
from .logging import flush_logger, log_batch, log_error, log_event, log_info, log_warning
|
|
31
|
+
from .modal import (
|
|
32
|
+
ensure_modal_installed,
|
|
33
|
+
ensure_task_app_ready,
|
|
34
|
+
find_asgi_apps,
|
|
35
|
+
is_local_demo_url,
|
|
36
|
+
is_modal_public_url,
|
|
37
|
+
normalize_endpoint_url,
|
|
38
|
+
)
|
|
39
|
+
from .paths import (
|
|
40
|
+
REPO_ROOT,
|
|
41
|
+
cleanup_paths,
|
|
42
|
+
configure_import_paths,
|
|
43
|
+
find_config_path,
|
|
44
|
+
get_bin_path,
|
|
45
|
+
get_env_file_paths,
|
|
46
|
+
get_home_config_file_paths,
|
|
47
|
+
is_py_file,
|
|
48
|
+
)
|
|
49
|
+
from .process import ensure_local_port_available, popen_capture, popen_stream, popen_stream_capture
|
|
50
|
+
from .sqld import SQLD_VERSION, find_sqld_binary, install_sqld
|
|
51
|
+
from .task_app_discovery import AppChoice, discover_eval_config_paths, select_app_choice
|
|
52
|
+
from .task_app_env import ensure_env_credentials, ensure_port_free, preflight_env_key
|
|
53
|
+
from .task_app_state import (
|
|
54
|
+
DEFAULT_TASK_APP_SECRET_NAME,
|
|
55
|
+
current_task_app_id,
|
|
56
|
+
load_demo_dir,
|
|
57
|
+
load_template_id,
|
|
58
|
+
now_iso,
|
|
59
|
+
persist_api_key,
|
|
60
|
+
persist_demo_dir,
|
|
61
|
+
persist_env_api_key,
|
|
62
|
+
persist_task_url,
|
|
63
|
+
persist_template_id,
|
|
64
|
+
read_task_app_config,
|
|
65
|
+
record_task_app,
|
|
66
|
+
resolve_task_app_entry,
|
|
67
|
+
task_app_config_path,
|
|
68
|
+
task_app_id_from_path,
|
|
69
|
+
update_task_app_entry,
|
|
70
|
+
write_task_app_config,
|
|
71
|
+
)
|
|
72
|
+
from .user_config import (
|
|
73
|
+
USER_CONFIG_PATH,
|
|
74
|
+
load_user_config,
|
|
75
|
+
load_user_env,
|
|
76
|
+
save_user_config,
|
|
77
|
+
update_user_config,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
__all__ = [
|
|
81
|
+
"AppChoice",
|
|
82
|
+
"AsyncHttpClient",
|
|
83
|
+
"DEFAULT_TASK_APP_SECRET_NAME",
|
|
84
|
+
"HTTPError",
|
|
85
|
+
"PROD_BASE_URL_DEFAULT",
|
|
86
|
+
"PromptedChoiceOption",
|
|
87
|
+
"PromptedChoiceType",
|
|
88
|
+
"PromptedPathOption",
|
|
89
|
+
"prompt_for_path",
|
|
90
|
+
"SQLD_VERSION",
|
|
91
|
+
"USER_CONFIG_PATH",
|
|
92
|
+
"ClaudeConfig",
|
|
93
|
+
"cleanup_paths",
|
|
94
|
+
"REPO_ROOT",
|
|
95
|
+
"create_and_write_json",
|
|
96
|
+
"current_task_app_id",
|
|
97
|
+
"configure_import_paths",
|
|
98
|
+
"discover_eval_config_paths",
|
|
99
|
+
"ensure_env_credentials",
|
|
100
|
+
"ensure_local_port_available",
|
|
101
|
+
"ensure_modal_installed",
|
|
102
|
+
"ensure_port_free",
|
|
103
|
+
"ensure_task_app_ready",
|
|
104
|
+
"extract_routes_from_app",
|
|
105
|
+
"find_asgi_apps",
|
|
106
|
+
"get_bin_path",
|
|
107
|
+
"find_config_path",
|
|
108
|
+
"find_sqld_binary",
|
|
109
|
+
"get_backend_from_env",
|
|
110
|
+
"get_env_file_paths",
|
|
111
|
+
"get_asgi_app",
|
|
112
|
+
"get_home_config_file_paths",
|
|
113
|
+
"get_learning_v2_base_url",
|
|
114
|
+
"http_request",
|
|
115
|
+
"install_bin",
|
|
116
|
+
"install_sqld",
|
|
117
|
+
"is_local_demo_url",
|
|
118
|
+
"is_modal_public_url",
|
|
119
|
+
"is_py_file",
|
|
120
|
+
"flush_logger",
|
|
121
|
+
"log_batch",
|
|
122
|
+
"log_error",
|
|
123
|
+
"log_event",
|
|
124
|
+
"log_info",
|
|
125
|
+
"log_warning",
|
|
126
|
+
"load_demo_dir",
|
|
127
|
+
"load_json_to_dict",
|
|
128
|
+
"load_file_to_module",
|
|
129
|
+
"load_template_id",
|
|
130
|
+
"load_user_config",
|
|
131
|
+
"load_user_env",
|
|
132
|
+
"mask_str",
|
|
133
|
+
"normalize_endpoint_url",
|
|
134
|
+
"now_iso",
|
|
135
|
+
"persist_api_key",
|
|
136
|
+
"persist_demo_dir",
|
|
137
|
+
"persist_env_api_key",
|
|
138
|
+
"persist_task_url",
|
|
139
|
+
"persist_template_id",
|
|
140
|
+
"popen_capture",
|
|
141
|
+
"popen_stream",
|
|
142
|
+
"popen_stream_capture",
|
|
143
|
+
"preflight_env_key",
|
|
144
|
+
"print_next_step",
|
|
145
|
+
"prompt_choice",
|
|
146
|
+
"read_env_var_from_file",
|
|
147
|
+
"read_task_app_config",
|
|
148
|
+
"record_task_app",
|
|
149
|
+
"resolve_env_var",
|
|
150
|
+
"resolve_task_app_entry",
|
|
151
|
+
"save_user_config",
|
|
152
|
+
"select_app_choice",
|
|
153
|
+
"strip_json_comments",
|
|
154
|
+
"task_app_config_path",
|
|
155
|
+
"task_app_id_from_path",
|
|
156
|
+
"task_app_state",
|
|
157
|
+
"update_task_app_entry",
|
|
158
|
+
"update_user_config",
|
|
159
|
+
"validate_modal_app",
|
|
160
|
+
"validate_task_app",
|
|
161
|
+
"verify_bin",
|
|
162
|
+
'write_agents_md',
|
|
163
|
+
"write_env_var_to_dotenv",
|
|
164
|
+
"write_env_var_to_json",
|
|
165
|
+
"write_task_app_config",
|
|
166
|
+
]
|
synth_ai/utils/agents.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
AGENTS_TEXT = """
|
|
4
|
+
sinf
|
|
5
|
+
"""
|
|
6
|
+
SYNTH_DIV_START = "### --- SYNTH SECTION START ---"
|
|
7
|
+
SYNTH_DIV_END = "### ---- SYNTH SECTION END ----"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _render_block() -> str:
|
|
11
|
+
return f"{SYNTH_DIV_START}\n{AGENTS_TEXT}\n{SYNTH_DIV_END}"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _append_block(prefix: str) -> str:
|
|
15
|
+
prefix = prefix.rstrip()
|
|
16
|
+
block = _render_block()
|
|
17
|
+
if prefix:
|
|
18
|
+
return f"{prefix}\n\n{block}\n"
|
|
19
|
+
return f"{block}\n"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def write_agents_md() -> None:
|
|
23
|
+
path = Path.cwd() / "AGENTS.md"
|
|
24
|
+
if not path.exists():
|
|
25
|
+
path.write_text(_append_block(""), encoding="utf-8")
|
|
26
|
+
return
|
|
27
|
+
|
|
28
|
+
file_text = path.read_text(encoding="utf-8")
|
|
29
|
+
|
|
30
|
+
# Remove orphan end markers first (end markers without a preceding start marker)
|
|
31
|
+
cleaned = file_text
|
|
32
|
+
end_pos = cleaned.find(SYNTH_DIV_END)
|
|
33
|
+
start_pos = cleaned.find(SYNTH_DIV_START)
|
|
34
|
+
|
|
35
|
+
# If there's an end marker before any start marker
|
|
36
|
+
if end_pos != -1 and (start_pos == -1 or end_pos < start_pos):
|
|
37
|
+
if start_pos == -1:
|
|
38
|
+
# No start markers at all - remove everything including content before orphan
|
|
39
|
+
cleaned = cleaned[end_pos + len(SYNTH_DIV_END):].lstrip()
|
|
40
|
+
else:
|
|
41
|
+
# There are start markers after the orphan - preserve content before orphan
|
|
42
|
+
before_orphan = cleaned[:end_pos].rstrip()
|
|
43
|
+
after_orphan = cleaned[end_pos + len(SYNTH_DIV_END):].lstrip()
|
|
44
|
+
cleaned = "\n\n".join(filter(None, [before_orphan, after_orphan]))
|
|
45
|
+
|
|
46
|
+
# Find the first start and last end marker to consolidate multiple sections
|
|
47
|
+
first_start = cleaned.find(SYNTH_DIV_START)
|
|
48
|
+
last_end = cleaned.rfind(SYNTH_DIV_END)
|
|
49
|
+
|
|
50
|
+
if first_start != -1 and last_end != -1 and last_end > first_start:
|
|
51
|
+
# We have at least one valid section, consolidate all into one
|
|
52
|
+
before = cleaned[:first_start].rstrip()
|
|
53
|
+
after = cleaned[last_end + len(SYNTH_DIV_END):].lstrip()
|
|
54
|
+
|
|
55
|
+
parts: list[str] = []
|
|
56
|
+
if before:
|
|
57
|
+
parts.append(before)
|
|
58
|
+
parts.append(_render_block())
|
|
59
|
+
if after:
|
|
60
|
+
parts.append(after)
|
|
61
|
+
|
|
62
|
+
new_text = "\n\n".join(parts)
|
|
63
|
+
if not new_text.endswith("\n"):
|
|
64
|
+
new_text += "\n"
|
|
65
|
+
path.write_text(new_text, encoding="utf-8")
|
|
66
|
+
return
|
|
67
|
+
|
|
68
|
+
# No valid sections found, remove any remaining orphan markers
|
|
69
|
+
cleaned = cleaned.replace(SYNTH_DIV_END, "")
|
|
70
|
+
cleaned = cleaned.replace(AGENTS_TEXT, "")
|
|
71
|
+
cleaned = cleaned.strip()
|
|
72
|
+
if cleaned:
|
|
73
|
+
cleaned += "\n\n"
|
|
74
|
+
path.write_text(f"{cleaned}{_render_block()}\n", encoding="utf-8")
|