synth-ai 0.2.14__py3-none-any.whl → 0.2.17__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of synth-ai might be problematic. Click here for more details.
- examples/README.md +1 -0
- examples/analyze_semantic_words.sh +2 -2
- examples/blog_posts/pokemon_vl/README.md +98 -0
- examples/blog_posts/pokemon_vl/configs/eval_qwen3_vl.toml +25 -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 +42 -0
- examples/blog_posts/pokemon_vl/configs/train_sft_qwen4b_vl.toml +40 -0
- examples/blog_posts/warming_up_to_rl/README.md +158 -0
- examples/blog_posts/warming_up_to_rl/configs/eval_ft_qwen4b.toml +25 -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/train_rl_from_sft.toml +41 -0
- examples/blog_posts/warming_up_to_rl/configs/train_sft_qwen4b.toml +40 -0
- examples/dev/qwen3_32b_qlora_4xh100.toml +5 -0
- examples/multi_step/SFT_README.md +147 -0
- examples/multi_step/configs/crafter_rl_outcome.toml +1 -1
- examples/multi_step/configs/crafter_rl_stepwise_hosted_judge.toml +73 -115
- examples/multi_step/configs/crafter_rl_stepwise_shaped.toml +1 -1
- examples/multi_step/configs/crafter_rl_stepwise_simple.toml +1 -1
- 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/verilog_rl_lora.toml +80 -123
- examples/multi_step/convert_traces_to_sft.py +84 -0
- examples/multi_step/run_sft_qwen30b.sh +45 -0
- examples/qwen_coder/configs/coder_lora_30b.toml +1 -2
- examples/qwen_coder/configs/coder_lora_4b.toml +5 -1
- examples/qwen_coder/configs/coder_lora_small.toml +1 -2
- 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 +6 -6
- 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 +79 -0
- examples/rl/configs/rl_from_ft_qwen.toml +37 -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 +2 -2
- examples/sft/README.md +6 -6
- examples/sft/configs/crafter_fft_qwen0p6b.toml +7 -2
- examples/sft/configs/crafter_lora_qwen0p6b.toml +7 -3
- examples/sft/evaluate.py +2 -4
- examples/sft/export_dataset.py +7 -4
- examples/swe/task_app/README.md +33 -3
- examples/swe/task_app/grpo_swe_mini.py +4 -1
- examples/swe/task_app/grpo_swe_mini_task_app.py +0 -12
- examples/swe/task_app/hosted/envs/crafter/react_agent.py +1 -1
- examples/swe/task_app/hosted/envs/mini_swe/environment.py +50 -23
- examples/swe/task_app/hosted/inference/openai_client.py +4 -4
- examples/swe/task_app/hosted/policy_routes.py +0 -2
- examples/swe/task_app/hosted/rollout.py +0 -8
- examples/swe/task_app/morph_backend.py +178 -0
- examples/task_apps/crafter/task_app/README.md +1 -1
- examples/task_apps/crafter/task_app/grpo_crafter.py +70 -10
- examples/task_apps/crafter/task_app/grpo_crafter_task_app.py +1 -1
- examples/task_apps/crafter/task_app/synth_envs_hosted/envs/crafter/policy.py +63 -27
- examples/task_apps/crafter/task_app/synth_envs_hosted/envs/crafter/react_agent.py +1 -2
- examples/task_apps/crafter/task_app/synth_envs_hosted/inference/openai_client.py +48 -50
- examples/task_apps/crafter/task_app/synth_envs_hosted/policy_routes.py +75 -36
- examples/task_apps/crafter/task_app/synth_envs_hosted/rollout.py +31 -15
- examples/task_apps/enron/__init__.py +1 -0
- examples/task_apps/enron/task_app/grpo_enron_task_app.py +1 -1
- examples/task_apps/math/README.md +1 -2
- examples/task_apps/pokemon_red/README.md +3 -4
- examples/task_apps/pokemon_red/eval_image_only_gpt4o.toml +6 -5
- examples/task_apps/pokemon_red/eval_pokemon_red_policy.py +1 -2
- examples/task_apps/pokemon_red/task_app.py +36 -5
- examples/task_apps/sokoban/README.md +2 -3
- examples/task_apps/verilog/eval_groq_qwen32b.toml +12 -14
- examples/task_apps/verilog/task_app/grpo_verilog_task_app.py +1 -1
- examples/vlm/README.md +3 -3
- examples/vlm/configs/crafter_vlm_gpt4o.toml +5 -0
- examples/vlm/crafter_openai_vlm_agent.py +3 -5
- examples/vlm/filter_image_rows.py +1 -1
- examples/vlm/run_crafter_vlm_benchmark.py +2 -2
- examples/warming_up_to_rl/_utils.py +92 -0
- examples/warming_up_to_rl/analyze_trace_db.py +1 -1
- examples/warming_up_to_rl/configs/crafter_fft.toml +5 -0
- examples/warming_up_to_rl/configs/eval_fft_qwen4b.toml +2 -0
- examples/warming_up_to_rl/configs/eval_groq_qwen32b.toml +2 -0
- examples/warming_up_to_rl/configs/eval_modal_qwen4b.toml +2 -1
- examples/warming_up_to_rl/configs/rl_from_base_qwen4b.toml +2 -1
- examples/warming_up_to_rl/configs/rl_from_ft.toml +2 -0
- examples/warming_up_to_rl/export_trace_sft.py +174 -60
- examples/warming_up_to_rl/readme.md +63 -132
- examples/warming_up_to_rl/run_fft_and_save.py +1 -1
- examples/warming_up_to_rl/run_local_rollout_traced.py +1 -1
- examples/warming_up_to_rl/run_rl_and_save.py +1 -1
- examples/warming_up_to_rl/task_app/README.md +42 -0
- examples/warming_up_to_rl/task_app/grpo_crafter.py +827 -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 +204 -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 +618 -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 +1084 -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 +1861 -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 +62 -0
- examples/workflows/math_rl/configs/rl_from_base_qwen.toml +27 -0
- examples/workflows/math_rl/configs/rl_from_base_qwen17.toml +5 -0
- synth_ai/__init__.py +44 -30
- 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 +144 -7
- synth_ai/api/train/__init__.py +13 -1
- synth_ai/api/train/builders.py +9 -3
- synth_ai/api/train/cli.py +155 -17
- synth_ai/api/train/config_finder.py +18 -11
- synth_ai/api/train/configs/__init__.py +8 -1
- synth_ai/api/train/configs/rl.py +32 -7
- synth_ai/api/train/configs/sft.py +6 -2
- synth_ai/api/train/configs/shared.py +59 -2
- synth_ai/api/train/env_resolver.py +13 -10
- synth_ai/auth/credentials.py +119 -0
- synth_ai/cli/__init__.py +61 -69
- synth_ai/cli/_modal_wrapper.py +7 -5
- synth_ai/cli/_typer_patch.py +0 -2
- synth_ai/cli/_validate_task_app.py +22 -4
- synth_ai/cli/commands/__init__.py +17 -0
- synth_ai/cli/commands/demo/__init__.py +6 -0
- synth_ai/cli/commands/demo/core.py +163 -0
- synth_ai/cli/commands/deploy/__init__.py +23 -0
- synth_ai/cli/commands/deploy/core.py +614 -0
- synth_ai/cli/commands/deploy/errors.py +72 -0
- synth_ai/cli/commands/deploy/validation.py +11 -0
- synth_ai/cli/commands/eval/__init__.py +19 -0
- synth_ai/cli/commands/eval/core.py +1109 -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 +388 -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 +177 -0
- synth_ai/cli/commands/help/core.py +73 -0
- synth_ai/cli/commands/status/__init__.py +64 -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/runs.py +81 -0
- synth_ai/cli/commands/status/subcommands/summary.py +47 -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 +199 -0
- synth_ai/cli/commands/train/judge_validation.py +304 -0
- synth_ai/cli/commands/train/validation.py +443 -0
- synth_ai/cli/demo.py +2 -162
- synth_ai/cli/deploy/__init__.py +28 -0
- synth_ai/cli/deploy/core.py +5 -0
- synth_ai/cli/deploy/errors.py +23 -0
- synth_ai/cli/deploy/validation.py +5 -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 +3 -1
- 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/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/recent.py +2 -1
- 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 +21 -0
- synth_ai/cli/status.py +7 -126
- 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 +110 -1499
- synth_ai/cli/traces.py +1 -1
- 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 +1 -1
- synth_ai/cli/watch.py +1 -1
- synth_ai/demos/__init__.py +10 -0
- synth_ai/demos/core/__init__.py +28 -1
- 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/crafter/grpo_crafter_task_app.py +1 -1
- 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 +702 -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 +0 -1
- synth_ai/environments/examples/bandit/environment.py +0 -1
- synth_ai/environments/examples/red/engine.py +33 -12
- synth_ai/environments/examples/red/engine_helpers/reward_components.py +151 -179
- synth_ai/environments/examples/red/environment.py +26 -0
- synth_ai/environments/examples/red/trace_hooks_v3.py +168 -0
- synth_ai/environments/examples/wordle/environment.py +0 -1
- synth_ai/evals/base.py +16 -5
- synth_ai/evals/client.py +1 -1
- synth_ai/http.py +8 -22
- synth_ai/inference/client.py +1 -1
- synth_ai/judge_schemas.py +4 -5
- synth_ai/learning/client.py +1 -1
- synth_ai/learning/health.py +1 -1
- synth_ai/learning/jobs.py +1 -1
- synth_ai/learning/rl/client.py +4 -2
- synth_ai/learning/rl/env_keys.py +1 -1
- synth_ai/learning/rl/secrets.py +1 -1
- synth_ai/learning/sft/client.py +1 -1
- synth_ai/learning/sft/data.py +407 -4
- synth_ai/learning/validators.py +4 -1
- synth_ai/streaming/__init__.py +29 -0
- synth_ai/streaming/config.py +94 -0
- synth_ai/streaming/handlers.py +469 -0
- synth_ai/streaming/streamer.py +301 -0
- synth_ai/streaming/types.py +95 -0
- synth_ai/task/apps/__init__.py +4 -2
- synth_ai/task/config.py +6 -4
- synth_ai/task/rubrics/__init__.py +1 -2
- synth_ai/task/rubrics/loaders.py +14 -10
- synth_ai/task/rubrics.py +219 -0
- synth_ai/task/trace_correlation_helpers.py +24 -11
- synth_ai/task/tracing_utils.py +14 -3
- synth_ai/task/validators.py +0 -1
- synth_ai/tracing_v3/abstractions.py +3 -3
- synth_ai/tracing_v3/config.py +15 -13
- synth_ai/tracing_v3/constants.py +21 -0
- synth_ai/tracing_v3/db_config.py +3 -1
- synth_ai/tracing_v3/decorators.py +10 -7
- synth_ai/tracing_v3/llm_call_record_helpers.py +5 -5
- synth_ai/tracing_v3/migration_helper.py +1 -2
- synth_ai/tracing_v3/session_tracer.py +7 -7
- synth_ai/tracing_v3/storage/base.py +29 -29
- synth_ai/tracing_v3/storage/config.py +3 -3
- synth_ai/tracing_v3/turso/daemon.py +8 -9
- synth_ai/tracing_v3/turso/native_manager.py +80 -72
- synth_ai/tracing_v3/utils.py +2 -2
- synth_ai/utils/__init__.py +101 -0
- synth_ai/utils/base_url.py +94 -0
- synth_ai/utils/cli.py +131 -0
- synth_ai/utils/env.py +294 -0
- synth_ai/utils/http.py +172 -0
- synth_ai/utils/modal.py +308 -0
- synth_ai/utils/process.py +212 -0
- synth_ai/utils/prompts.py +39 -0
- synth_ai/utils/sqld.py +122 -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/user_config.py +137 -0
- synth_ai/v0/config/__init__.py +1 -5
- synth_ai/v0/config/base_url.py +1 -7
- synth_ai/v0/tracing/config.py +1 -1
- synth_ai/v0/tracing/decorators.py +1 -1
- synth_ai/v0/tracing/upload.py +1 -1
- synth_ai/v0/tracing_v1/config.py +1 -1
- synth_ai/v0/tracing_v1/decorators.py +1 -1
- synth_ai/v0/tracing_v1/upload.py +1 -1
- {synth_ai-0.2.14.dist-info → synth_ai-0.2.17.dist-info}/METADATA +91 -32
- {synth_ai-0.2.14.dist-info → synth_ai-0.2.17.dist-info}/RECORD +341 -154
- synth_ai/cli/man.py +0 -106
- synth_ai/cli/tui.py +0 -57
- synth_ai/compound/cais.py +0 -0
- synth_ai/core/experiment.py +0 -13
- synth_ai/core/system.py +0 -15
- synth_ai/demo_registry.py +0 -295
- synth_ai/handshake.py +0 -109
- synth_ai/tui/__init__.py +0 -5
- synth_ai/tui/__main__.py +0 -13
- synth_ai/tui/cli/__init__.py +0 -1
- synth_ai/tui/cli/query_experiments.py +0 -164
- synth_ai/tui/cli/query_experiments_v3.py +0 -164
- synth_ai/tui/dashboard.py +0 -906
- {synth_ai-0.2.14.dist-info → synth_ai-0.2.17.dist-info}/WHEEL +0 -0
- {synth_ai-0.2.14.dist-info → synth_ai-0.2.17.dist-info}/entry_points.txt +0 -0
- {synth_ai-0.2.14.dist-info → synth_ai-0.2.17.dist-info}/licenses/LICENSE +0 -0
- {synth_ai-0.2.14.dist-info → synth_ai-0.2.17.dist-info}/top_level.txt +0 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from abc import ABC, abstractmethod
|
|
4
4
|
from datetime import datetime
|
|
5
|
-
from typing import Any
|
|
5
|
+
from typing import Any
|
|
6
6
|
|
|
7
7
|
from ..abstractions import SessionTrace
|
|
8
8
|
|
|
@@ -28,7 +28,7 @@ class TraceStorage(ABC):
|
|
|
28
28
|
pass
|
|
29
29
|
|
|
30
30
|
@abstractmethod
|
|
31
|
-
async def get_session_trace(self, session_id: str) ->
|
|
31
|
+
async def get_session_trace(self, session_id: str) -> dict[str, Any] | None:
|
|
32
32
|
"""Retrieve a session trace by ID.
|
|
33
33
|
|
|
34
34
|
Args:
|
|
@@ -40,7 +40,7 @@ class TraceStorage(ABC):
|
|
|
40
40
|
pass
|
|
41
41
|
|
|
42
42
|
@abstractmethod
|
|
43
|
-
async def query_traces(self, query: str, params:
|
|
43
|
+
async def query_traces(self, query: str, params: dict[str, Any] | None = None) -> Any:
|
|
44
44
|
"""Execute a query and return results.
|
|
45
45
|
|
|
46
46
|
Args:
|
|
@@ -55,9 +55,9 @@ class TraceStorage(ABC):
|
|
|
55
55
|
@abstractmethod
|
|
56
56
|
async def get_model_usage(
|
|
57
57
|
self,
|
|
58
|
-
start_date:
|
|
59
|
-
end_date:
|
|
60
|
-
model_name:
|
|
58
|
+
start_date: datetime | None = None,
|
|
59
|
+
end_date: datetime | None = None,
|
|
60
|
+
model_name: str | None = None,
|
|
61
61
|
) -> Any:
|
|
62
62
|
"""Get model usage statistics.
|
|
63
63
|
|
|
@@ -95,8 +95,8 @@ class TraceStorage(ABC):
|
|
|
95
95
|
self,
|
|
96
96
|
session_id: str,
|
|
97
97
|
*,
|
|
98
|
-
created_at:
|
|
99
|
-
metadata:
|
|
98
|
+
created_at: datetime | None = None,
|
|
99
|
+
metadata: dict[str, Any] | None = None,
|
|
100
100
|
) -> None:
|
|
101
101
|
"""Ensure a session row exists for the given session id."""
|
|
102
102
|
pass
|
|
@@ -108,10 +108,10 @@ class TraceStorage(ABC):
|
|
|
108
108
|
*,
|
|
109
109
|
step_id: str,
|
|
110
110
|
step_index: int,
|
|
111
|
-
turn_number:
|
|
112
|
-
started_at:
|
|
113
|
-
completed_at:
|
|
114
|
-
metadata:
|
|
111
|
+
turn_number: int | None = None,
|
|
112
|
+
started_at: datetime | None = None,
|
|
113
|
+
completed_at: datetime | None = None,
|
|
114
|
+
metadata: dict[str, Any] | None = None,
|
|
115
115
|
) -> int:
|
|
116
116
|
"""Ensure a timestep row exists and return its database id."""
|
|
117
117
|
pass
|
|
@@ -121,9 +121,9 @@ class TraceStorage(ABC):
|
|
|
121
121
|
self,
|
|
122
122
|
session_id: str,
|
|
123
123
|
*,
|
|
124
|
-
timestep_db_id:
|
|
124
|
+
timestep_db_id: int | None,
|
|
125
125
|
event: Any,
|
|
126
|
-
metadata_override:
|
|
126
|
+
metadata_override: dict[str, Any] | None = None,
|
|
127
127
|
) -> int:
|
|
128
128
|
"""Insert an event and return its database id."""
|
|
129
129
|
pass
|
|
@@ -133,12 +133,12 @@ class TraceStorage(ABC):
|
|
|
133
133
|
self,
|
|
134
134
|
session_id: str,
|
|
135
135
|
*,
|
|
136
|
-
timestep_db_id:
|
|
136
|
+
timestep_db_id: int | None,
|
|
137
137
|
message_type: str,
|
|
138
138
|
content: Any,
|
|
139
|
-
event_time:
|
|
140
|
-
message_time:
|
|
141
|
-
metadata:
|
|
139
|
+
event_time: float | None = None,
|
|
140
|
+
message_time: int | None = None,
|
|
141
|
+
metadata: dict[str, Any] | None = None,
|
|
142
142
|
) -> int:
|
|
143
143
|
"""Insert a message row linked to a session/timestep."""
|
|
144
144
|
pass
|
|
@@ -151,7 +151,7 @@ class TraceStorage(ABC):
|
|
|
151
151
|
total_reward: int,
|
|
152
152
|
achievements_count: int,
|
|
153
153
|
total_steps: int,
|
|
154
|
-
reward_metadata:
|
|
154
|
+
reward_metadata: dict | None = None,
|
|
155
155
|
) -> int:
|
|
156
156
|
"""Record an outcome reward for a session."""
|
|
157
157
|
pass
|
|
@@ -162,13 +162,13 @@ class TraceStorage(ABC):
|
|
|
162
162
|
session_id: str,
|
|
163
163
|
*,
|
|
164
164
|
event_id: int,
|
|
165
|
-
message_id:
|
|
166
|
-
turn_number:
|
|
165
|
+
message_id: int | None = None,
|
|
166
|
+
turn_number: int | None = None,
|
|
167
167
|
reward_value: float = 0.0,
|
|
168
|
-
reward_type:
|
|
169
|
-
key:
|
|
170
|
-
annotation:
|
|
171
|
-
source:
|
|
168
|
+
reward_type: str | None = None,
|
|
169
|
+
key: str | None = None,
|
|
170
|
+
annotation: dict[str, Any] | None = None,
|
|
171
|
+
source: str | None = None,
|
|
172
172
|
) -> int:
|
|
173
173
|
"""Record a reward tied to a specific event."""
|
|
174
174
|
pass
|
|
@@ -178,8 +178,8 @@ class TraceStorage(ABC):
|
|
|
178
178
|
self,
|
|
179
179
|
experiment_id: str,
|
|
180
180
|
name: str,
|
|
181
|
-
description:
|
|
182
|
-
configuration:
|
|
181
|
+
description: str | None = None,
|
|
182
|
+
configuration: dict[str, Any] | None = None,
|
|
183
183
|
) -> str:
|
|
184
184
|
"""Create a new experiment."""
|
|
185
185
|
raise NotImplementedError("Experiment management not supported by this backend")
|
|
@@ -189,14 +189,14 @@ class TraceStorage(ABC):
|
|
|
189
189
|
raise NotImplementedError("Experiment management not supported by this backend")
|
|
190
190
|
|
|
191
191
|
async def get_sessions_by_experiment(
|
|
192
|
-
self, experiment_id: str, limit:
|
|
192
|
+
self, experiment_id: str, limit: int | None = None
|
|
193
193
|
) -> list[dict[str, Any]]:
|
|
194
194
|
"""Get all sessions for an experiment."""
|
|
195
195
|
raise NotImplementedError("Experiment management not supported by this backend")
|
|
196
196
|
|
|
197
197
|
# Batch operations
|
|
198
198
|
async def batch_insert_sessions(
|
|
199
|
-
self, traces: list[SessionTrace], batch_size:
|
|
199
|
+
self, traces: list[SessionTrace], batch_size: int | None = 1000
|
|
200
200
|
) -> list[str]:
|
|
201
201
|
"""Batch insert multiple session traces.
|
|
202
202
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import os
|
|
4
4
|
from dataclasses import dataclass
|
|
5
5
|
from enum import Enum
|
|
6
|
-
from typing import Any
|
|
6
|
+
from typing import Any
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class StorageBackend(str, Enum):
|
|
@@ -14,7 +14,7 @@ class StorageBackend(str, Enum):
|
|
|
14
14
|
POSTGRES = "postgres" # Future support
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
def _is_enabled(value:
|
|
17
|
+
def _is_enabled(value: str | None) -> bool:
|
|
18
18
|
if value is None:
|
|
19
19
|
return False
|
|
20
20
|
return value.lower() in {"1", "true", "yes", "on"}
|
|
@@ -25,7 +25,7 @@ class StorageConfig:
|
|
|
25
25
|
"""Configuration for storage backend."""
|
|
26
26
|
|
|
27
27
|
backend: StorageBackend = StorageBackend.TURSO_NATIVE
|
|
28
|
-
connection_string:
|
|
28
|
+
connection_string: str | None = None
|
|
29
29
|
|
|
30
30
|
# Turso-specific settings
|
|
31
31
|
turso_url: str = os.getenv("TURSO_DATABASE_URL", "sqlite+libsql://http://127.0.0.1:8080")
|
|
@@ -7,7 +7,6 @@ import time
|
|
|
7
7
|
|
|
8
8
|
import requests
|
|
9
9
|
from requests import RequestException
|
|
10
|
-
from typing import Any, Optional
|
|
11
10
|
|
|
12
11
|
from ..config import CONFIG
|
|
13
12
|
|
|
@@ -17,9 +16,9 @@ class SqldDaemon:
|
|
|
17
16
|
|
|
18
17
|
def __init__(
|
|
19
18
|
self,
|
|
20
|
-
db_path:
|
|
21
|
-
http_port:
|
|
22
|
-
binary_path:
|
|
19
|
+
db_path: str | None = None,
|
|
20
|
+
http_port: int | None = None,
|
|
21
|
+
binary_path: str | None = None,
|
|
23
22
|
):
|
|
24
23
|
"""Initialize sqld daemon manager.
|
|
25
24
|
|
|
@@ -31,7 +30,7 @@ class SqldDaemon:
|
|
|
31
30
|
self.db_path = db_path or CONFIG.sqld_db_path
|
|
32
31
|
self.http_port = http_port or CONFIG.sqld_http_port
|
|
33
32
|
self.binary_path = binary_path or self._find_binary()
|
|
34
|
-
self.process:
|
|
33
|
+
self.process: subprocess.Popen[str] | None = None
|
|
35
34
|
|
|
36
35
|
def _find_binary(self) -> str:
|
|
37
36
|
"""Find sqld binary in PATH."""
|
|
@@ -85,7 +84,7 @@ class SqldDaemon:
|
|
|
85
84
|
pass
|
|
86
85
|
|
|
87
86
|
# Check if process crashed
|
|
88
|
-
if self.process.poll() is not None:
|
|
87
|
+
if self.process and self.process.poll() is not None:
|
|
89
88
|
stdout, stderr = self.process.communicate()
|
|
90
89
|
raise RuntimeError(
|
|
91
90
|
f"sqld daemon failed to start:\nstdout: {stdout}\nstderr: {stderr}"
|
|
@@ -124,10 +123,10 @@ class SqldDaemon:
|
|
|
124
123
|
|
|
125
124
|
|
|
126
125
|
# Convenience functions
|
|
127
|
-
_daemon:
|
|
126
|
+
_daemon: SqldDaemon | None = None
|
|
128
127
|
|
|
129
128
|
|
|
130
|
-
def start_sqld(db_path:
|
|
129
|
+
def start_sqld(db_path: str | None = None, port: int | None = None) -> SqldDaemon:
|
|
131
130
|
"""Start a global sqld daemon instance."""
|
|
132
131
|
global _daemon
|
|
133
132
|
if _daemon and _daemon.is_running():
|
|
@@ -146,6 +145,6 @@ def stop_sqld():
|
|
|
146
145
|
_daemon = None
|
|
147
146
|
|
|
148
147
|
|
|
149
|
-
def get_daemon() ->
|
|
148
|
+
def get_daemon() -> SqldDaemon | None:
|
|
150
149
|
"""Get the global daemon instance."""
|
|
151
150
|
return _daemon
|
|
@@ -13,7 +13,7 @@ import logging
|
|
|
13
13
|
import re
|
|
14
14
|
from collections.abc import Callable
|
|
15
15
|
from dataclasses import asdict, dataclass
|
|
16
|
-
from datetime import
|
|
16
|
+
from datetime import UTC, datetime
|
|
17
17
|
from typing import TYPE_CHECKING, Any, cast
|
|
18
18
|
|
|
19
19
|
import libsql
|
|
@@ -378,8 +378,10 @@ class NativeLibsqlTraceManager(TraceStorage):
|
|
|
378
378
|
session_exists = await self._session_exists(trace.session_id)
|
|
379
379
|
_logger.info(f"[TRACE_DEBUG] Session exists: {session_exists}")
|
|
380
380
|
|
|
381
|
+
step_id_map: dict[str, int] = {}
|
|
382
|
+
|
|
381
383
|
if session_exists:
|
|
382
|
-
_logger.warning(f"[TRACE_DEBUG] Session {trace.session_id} already exists,
|
|
384
|
+
_logger.warning(f"[TRACE_DEBUG] Session {trace.session_id} already exists, skipping events/timesteps, only updating messages!")
|
|
383
385
|
# Don't return early - we need to save messages!
|
|
384
386
|
# Just update metadata
|
|
385
387
|
async with self._op_lock:
|
|
@@ -390,10 +392,9 @@ class NativeLibsqlTraceManager(TraceStorage):
|
|
|
390
392
|
(_json_dumps(trace.metadata or {}), trace.session_id),
|
|
391
393
|
)
|
|
392
394
|
conn.commit()
|
|
393
|
-
#
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
created_at = trace.created_at or datetime.now(timezone.utc)
|
|
395
|
+
# Skip events and timesteps to ensure idempotency
|
|
396
|
+
else:
|
|
397
|
+
created_at = trace.created_at or datetime.now(UTC)
|
|
397
398
|
|
|
398
399
|
async with self._op_lock:
|
|
399
400
|
conn = self._conn
|
|
@@ -417,73 +418,76 @@ class NativeLibsqlTraceManager(TraceStorage):
|
|
|
417
418
|
),
|
|
418
419
|
)
|
|
419
420
|
conn.commit()
|
|
420
|
-
_logger.info(
|
|
421
|
-
|
|
422
|
-
step_id_map: dict[str, int] = {}
|
|
421
|
+
_logger.info("[TRACE_DEBUG] Session row inserted")
|
|
423
422
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
423
|
+
# Only insert timesteps and events if this is a new session
|
|
424
|
+
for step in trace.session_time_steps:
|
|
425
|
+
step_db_id = await self.ensure_timestep(
|
|
426
|
+
trace.session_id,
|
|
427
|
+
step_id=step.step_id,
|
|
428
|
+
step_index=step.step_index,
|
|
429
|
+
turn_number=step.turn_number,
|
|
430
|
+
started_at=step.timestamp,
|
|
431
|
+
completed_at=step.completed_at,
|
|
432
|
+
metadata=step.step_metadata or {},
|
|
433
|
+
)
|
|
434
|
+
step_id_map[step.step_id] = step_db_id
|
|
435
|
+
|
|
436
|
+
for event in trace.event_history:
|
|
437
|
+
step_ref = None
|
|
438
|
+
metadata = event.metadata or {}
|
|
439
|
+
if isinstance(metadata, dict):
|
|
440
|
+
step_ref = metadata.get("step_id")
|
|
441
|
+
timestep_db_id = step_id_map.get(step_ref) if step_ref else None
|
|
442
|
+
await self.insert_event_row(
|
|
443
|
+
trace.session_id,
|
|
444
|
+
timestep_db_id=timestep_db_id,
|
|
445
|
+
event=event,
|
|
446
|
+
metadata_override=event.metadata or {},
|
|
447
|
+
)
|
|
448
448
|
|
|
449
449
|
import logging as _logging
|
|
450
450
|
_logger = _logging.getLogger(__name__)
|
|
451
|
-
_logger.info(f"[TRACE_DEBUG] insert_session_trace: saving {len(trace.markov_blanket_message_history)} messages")
|
|
451
|
+
_logger.info(f"[TRACE_DEBUG] insert_session_trace: saving {len(trace.markov_blanket_message_history)} messages (session_exists={session_exists})")
|
|
452
452
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
453
|
+
# Only insert messages if this is a new session (for idempotency)
|
|
454
|
+
if not session_exists:
|
|
455
|
+
for idx, msg in enumerate(trace.markov_blanket_message_history):
|
|
456
|
+
metadata = dict(getattr(msg, "metadata", {}) or {})
|
|
457
|
+
step_ref = metadata.get("step_id")
|
|
458
|
+
content_value = msg.content
|
|
459
|
+
if isinstance(msg.content, SessionMessageContent):
|
|
460
|
+
if msg.content.json_payload:
|
|
461
|
+
metadata.setdefault("json_payload", msg.content.json_payload)
|
|
462
|
+
content_value = msg.content.json_payload
|
|
463
|
+
else:
|
|
464
|
+
content_value = msg.content.as_text()
|
|
465
|
+
if msg.content.text:
|
|
466
|
+
metadata.setdefault("text", msg.content.text)
|
|
467
|
+
elif not isinstance(content_value, str):
|
|
468
|
+
try:
|
|
469
|
+
content_value = json.dumps(content_value, ensure_ascii=False)
|
|
470
|
+
except (TypeError, ValueError):
|
|
471
|
+
content_value = str(content_value)
|
|
472
|
+
|
|
473
|
+
_logger.info(f"[TRACE_DEBUG] Message {idx+1}: type={msg.message_type}, content_len={len(str(content_value))}")
|
|
474
|
+
|
|
466
475
|
try:
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
)
|
|
483
|
-
_logger.info(f"[TRACE_DEBUG] Message {idx+1}: saved successfully")
|
|
484
|
-
except Exception as exc:
|
|
485
|
-
_logger.error(f"[TRACE_DEBUG] Message {idx+1}: FAILED TO SAVE: {exc}", exc_info=True)
|
|
486
|
-
raise
|
|
476
|
+
await self.insert_message_row(
|
|
477
|
+
trace.session_id,
|
|
478
|
+
timestep_db_id=step_id_map.get(step_ref) if step_ref else None,
|
|
479
|
+
message_type=msg.message_type,
|
|
480
|
+
content=content_value,
|
|
481
|
+
event_time=msg.time_record.event_time,
|
|
482
|
+
message_time=msg.time_record.message_time,
|
|
483
|
+
metadata=metadata,
|
|
484
|
+
)
|
|
485
|
+
_logger.info(f"[TRACE_DEBUG] Message {idx+1}: saved successfully")
|
|
486
|
+
except Exception as exc:
|
|
487
|
+
_logger.error(f"[TRACE_DEBUG] Message {idx+1}: FAILED TO SAVE: {exc}", exc_info=True)
|
|
488
|
+
raise
|
|
489
|
+
else:
|
|
490
|
+
_logger.info("[TRACE_DEBUG] Skipping message insertion for existing session (idempotency)")
|
|
487
491
|
|
|
488
492
|
async with self._op_lock:
|
|
489
493
|
conn = self._conn
|
|
@@ -806,7 +810,7 @@ class NativeLibsqlTraceManager(TraceStorage):
|
|
|
806
810
|
) -> None:
|
|
807
811
|
await self.initialize()
|
|
808
812
|
|
|
809
|
-
created_at_val = (created_at or datetime.now(
|
|
813
|
+
created_at_val = (created_at or datetime.now(UTC)).isoformat()
|
|
810
814
|
metadata_json = _json_dumps(metadata or {})
|
|
811
815
|
|
|
812
816
|
async with self._op_lock:
|
|
@@ -838,7 +842,7 @@ class NativeLibsqlTraceManager(TraceStorage):
|
|
|
838
842
|
) -> int:
|
|
839
843
|
await self.initialize()
|
|
840
844
|
|
|
841
|
-
started_at_val = (started_at or datetime.now(
|
|
845
|
+
started_at_val = (started_at or datetime.now(UTC)).isoformat()
|
|
842
846
|
completed_at_val = completed_at.isoformat() if completed_at else None
|
|
843
847
|
metadata_json = _json_dumps(metadata or {})
|
|
844
848
|
|
|
@@ -927,7 +931,11 @@ class NativeLibsqlTraceManager(TraceStorage):
|
|
|
927
931
|
if isinstance(event, LMCAISEvent):
|
|
928
932
|
call_records = None
|
|
929
933
|
if getattr(event, "call_records", None):
|
|
930
|
-
|
|
934
|
+
# Handle both dataclass instances and dicts (from deserialization)
|
|
935
|
+
call_records = [
|
|
936
|
+
asdict(record) if not isinstance(record, dict) else record
|
|
937
|
+
for record in event.call_records
|
|
938
|
+
]
|
|
931
939
|
payload.update(
|
|
932
940
|
{
|
|
933
941
|
"event_type": "cais",
|
|
@@ -1150,7 +1158,7 @@ class NativeLibsqlTraceManager(TraceStorage):
|
|
|
1150
1158
|
total_reward,
|
|
1151
1159
|
achievements_count,
|
|
1152
1160
|
total_steps,
|
|
1153
|
-
datetime.now(
|
|
1161
|
+
datetime.now(UTC).isoformat(),
|
|
1154
1162
|
_json_dumps(reward_metadata),
|
|
1155
1163
|
),
|
|
1156
1164
|
)
|
|
@@ -1202,7 +1210,7 @@ class NativeLibsqlTraceManager(TraceStorage):
|
|
|
1202
1210
|
key,
|
|
1203
1211
|
_json_dumps(annotation),
|
|
1204
1212
|
source,
|
|
1205
|
-
datetime.now(
|
|
1213
|
+
datetime.now(UTC).isoformat(),
|
|
1206
1214
|
),
|
|
1207
1215
|
)
|
|
1208
1216
|
conn.commit()
|
synth_ai/tracing_v3/utils.py
CHANGED
|
@@ -5,13 +5,13 @@ from __future__ import annotations
|
|
|
5
5
|
import hashlib
|
|
6
6
|
import json
|
|
7
7
|
import uuid
|
|
8
|
-
from datetime import
|
|
8
|
+
from datetime import UTC, datetime
|
|
9
9
|
from typing import Any
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
def iso_now() -> str:
|
|
13
13
|
"""Get current timezone.utc time as ISO format string."""
|
|
14
|
-
return datetime.now(
|
|
14
|
+
return datetime.now(UTC).isoformat()
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
def json_dumps(obj: Any) -> str:
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
from . import task_app_state
|
|
2
|
+
from .base_url import PROD_BASE_URL_DEFAULT, get_backend_from_env, get_learning_v2_base_url
|
|
3
|
+
from .cli import PromptedChoiceOption, PromptedChoiceType, print_next_step
|
|
4
|
+
from .env import mask_str, resolve_env_var, write_env_var_to_dotenv, write_env_var_to_json
|
|
5
|
+
from .http import AsyncHttpClient, HTTPError, http_request
|
|
6
|
+
from .modal import (
|
|
7
|
+
ensure_modal_installed,
|
|
8
|
+
ensure_task_app_ready,
|
|
9
|
+
find_asgi_apps,
|
|
10
|
+
is_local_demo_url,
|
|
11
|
+
is_modal_public_url,
|
|
12
|
+
normalize_endpoint_url,
|
|
13
|
+
)
|
|
14
|
+
from .process import ensure_local_port_available, popen_capture, popen_stream, popen_stream_capture
|
|
15
|
+
from .sqld import SQLD_VERSION, find_sqld_binary, install_sqld
|
|
16
|
+
from .task_app_discovery import AppChoice, discover_eval_config_paths, select_app_choice
|
|
17
|
+
from .task_app_env import ensure_env_credentials, ensure_port_free, preflight_env_key
|
|
18
|
+
from .task_app_state import (
|
|
19
|
+
DEFAULT_TASK_APP_SECRET_NAME,
|
|
20
|
+
current_task_app_id,
|
|
21
|
+
load_demo_dir,
|
|
22
|
+
load_template_id,
|
|
23
|
+
now_iso,
|
|
24
|
+
persist_api_key,
|
|
25
|
+
persist_demo_dir,
|
|
26
|
+
persist_env_api_key,
|
|
27
|
+
persist_task_url,
|
|
28
|
+
persist_template_id,
|
|
29
|
+
read_task_app_config,
|
|
30
|
+
record_task_app,
|
|
31
|
+
resolve_task_app_entry,
|
|
32
|
+
task_app_config_path,
|
|
33
|
+
task_app_id_from_path,
|
|
34
|
+
update_task_app_entry,
|
|
35
|
+
write_task_app_config,
|
|
36
|
+
)
|
|
37
|
+
from .user_config import (
|
|
38
|
+
USER_CONFIG_PATH,
|
|
39
|
+
load_user_config,
|
|
40
|
+
load_user_env,
|
|
41
|
+
save_user_config,
|
|
42
|
+
update_user_config,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
__all__ = [
|
|
46
|
+
"AppChoice",
|
|
47
|
+
"AsyncHttpClient",
|
|
48
|
+
"DEFAULT_TASK_APP_SECRET_NAME",
|
|
49
|
+
"HTTPError",
|
|
50
|
+
"PROD_BASE_URL_DEFAULT",
|
|
51
|
+
"PromptedChoiceOption",
|
|
52
|
+
"PromptedChoiceType",
|
|
53
|
+
"SQLD_VERSION",
|
|
54
|
+
"USER_CONFIG_PATH",
|
|
55
|
+
"current_task_app_id",
|
|
56
|
+
"discover_eval_config_paths",
|
|
57
|
+
"ensure_env_credentials",
|
|
58
|
+
"ensure_local_port_available",
|
|
59
|
+
"ensure_modal_installed",
|
|
60
|
+
"ensure_port_free",
|
|
61
|
+
"ensure_task_app_ready",
|
|
62
|
+
"find_asgi_apps",
|
|
63
|
+
"find_sqld_binary",
|
|
64
|
+
"get_backend_from_env",
|
|
65
|
+
"get_learning_v2_base_url",
|
|
66
|
+
"http_request",
|
|
67
|
+
"install_sqld",
|
|
68
|
+
"is_local_demo_url",
|
|
69
|
+
"is_modal_public_url",
|
|
70
|
+
"load_demo_dir",
|
|
71
|
+
"load_template_id",
|
|
72
|
+
"load_user_config",
|
|
73
|
+
"load_user_env",
|
|
74
|
+
"mask_str",
|
|
75
|
+
"normalize_endpoint_url",
|
|
76
|
+
"now_iso",
|
|
77
|
+
"persist_api_key",
|
|
78
|
+
"persist_demo_dir",
|
|
79
|
+
"persist_env_api_key",
|
|
80
|
+
"persist_task_url",
|
|
81
|
+
"persist_template_id",
|
|
82
|
+
"popen_capture",
|
|
83
|
+
"popen_stream",
|
|
84
|
+
"popen_stream_capture",
|
|
85
|
+
"preflight_env_key",
|
|
86
|
+
"print_next_step",
|
|
87
|
+
"read_task_app_config",
|
|
88
|
+
"record_task_app",
|
|
89
|
+
"resolve_env_var",
|
|
90
|
+
"resolve_task_app_entry",
|
|
91
|
+
"save_user_config",
|
|
92
|
+
"select_app_choice",
|
|
93
|
+
"task_app_config_path",
|
|
94
|
+
"task_app_id_from_path",
|
|
95
|
+
"task_app_state",
|
|
96
|
+
"update_task_app_entry",
|
|
97
|
+
"update_user_config",
|
|
98
|
+
"write_env_var_to_dotenv",
|
|
99
|
+
"write_env_var_to_json",
|
|
100
|
+
"write_task_app_config",
|
|
101
|
+
]
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base URL resolution for learning-v2 and related backend APIs.
|
|
3
|
+
|
|
4
|
+
Default to production, allow overrides via environment variables:
|
|
5
|
+
- LEARNING_V2_BASE_URL (highest precedence)
|
|
6
|
+
- SYNTH_BASE_URL (legacy)
|
|
7
|
+
- SYNTH_LOCAL_BASE_URL
|
|
8
|
+
- SYNTH_DEV_BASE_URL
|
|
9
|
+
- SYNTH_PROD_BASE_URL (fallback if none provided)
|
|
10
|
+
|
|
11
|
+
Normalization: ensure the returned URL ends with "/api".
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import os
|
|
15
|
+
from typing import Literal
|
|
16
|
+
|
|
17
|
+
PROD_BASE_URL_DEFAULT = "https://agent-learning.onrender.com"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _normalize_base(url: str) -> str:
|
|
21
|
+
url = url.strip()
|
|
22
|
+
if url.endswith("/v1"):
|
|
23
|
+
url = url[:-3]
|
|
24
|
+
url = url.rstrip("/")
|
|
25
|
+
if not url.endswith("/api"):
|
|
26
|
+
url = f"{url}/api"
|
|
27
|
+
return url
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def get_learning_v2_base_url(mode: Literal["dev", "prod"] = "prod") -> str:
|
|
31
|
+
if mode == "prod":
|
|
32
|
+
prod = os.getenv("SYNTH_PROD_BASE_URL") or PROD_BASE_URL_DEFAULT
|
|
33
|
+
return _normalize_base(prod)
|
|
34
|
+
env_url = os.getenv("LEARNING_V2_BASE_URL")
|
|
35
|
+
if env_url:
|
|
36
|
+
return _normalize_base(env_url)
|
|
37
|
+
|
|
38
|
+
legacy = os.getenv("SYNTH_BASE_URL")
|
|
39
|
+
if legacy:
|
|
40
|
+
return _normalize_base(legacy)
|
|
41
|
+
|
|
42
|
+
local = os.getenv("SYNTH_LOCAL_BASE_URL")
|
|
43
|
+
if local:
|
|
44
|
+
return _normalize_base(local)
|
|
45
|
+
|
|
46
|
+
dev = os.getenv("SYNTH_DEV_BASE_URL")
|
|
47
|
+
if dev:
|
|
48
|
+
return _normalize_base(dev)
|
|
49
|
+
|
|
50
|
+
raise ValueError("No base URL configured. Set one of: LEARNING_V2_BASE_URL, SYNTH_BASE_URL, SYNTH_LOCAL_BASE_URL, SYNTH_DEV_BASE_URL")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _resolve_override_mode() -> str:
|
|
54
|
+
ov = (os.getenv("SYNTH_BACKEND_URL_OVERRIDE", "") or "").strip().lower()
|
|
55
|
+
if ov in {"local", "dev", "prod"}:
|
|
56
|
+
return ov
|
|
57
|
+
return "prod"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def get_backend_from_env() -> tuple[str, str]:
|
|
61
|
+
direct_override = (os.getenv("BACKEND_OVERRIDE") or "").strip()
|
|
62
|
+
if direct_override:
|
|
63
|
+
base = direct_override.rstrip("/")
|
|
64
|
+
if base.endswith("/api"):
|
|
65
|
+
base = base[: -len("/api")]
|
|
66
|
+
api_key = os.getenv("SYNTH_API_KEY", "").strip()
|
|
67
|
+
return base, api_key
|
|
68
|
+
|
|
69
|
+
mode = _resolve_override_mode()
|
|
70
|
+
if mode == "local":
|
|
71
|
+
base = os.getenv("LOCAL_BACKEND_URL", "http://localhost:8000")
|
|
72
|
+
key = os.getenv("TESTING_LOCAL_SYNTH_API_KEY", "")
|
|
73
|
+
return base.rstrip("/"), key
|
|
74
|
+
if mode == "dev":
|
|
75
|
+
base = os.getenv("DEV_BACKEND_URL", "") or "http://localhost:8000"
|
|
76
|
+
key = os.getenv("DEV_SYNTH_API_KEY", "")
|
|
77
|
+
return base.rstrip("/"), key
|
|
78
|
+
base = os.getenv("PROD_BACKEND_URL", f"{PROD_BASE_URL_DEFAULT}")
|
|
79
|
+
base = base.rstrip("/")
|
|
80
|
+
if base.endswith("/api"):
|
|
81
|
+
base = base[: -len("/api")]
|
|
82
|
+
key = (
|
|
83
|
+
os.getenv("PROD_SYNTH_API_KEY", "")
|
|
84
|
+
or os.getenv("TESTING_PROD_SYNTH_API_KEY", "")
|
|
85
|
+
or os.getenv("SYNTH_API_KEY", "")
|
|
86
|
+
)
|
|
87
|
+
return base, key
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
__all__ = [
|
|
91
|
+
"PROD_BASE_URL_DEFAULT",
|
|
92
|
+
"get_backend_from_env",
|
|
93
|
+
"get_learning_v2_base_url",
|
|
94
|
+
]
|