synth-ai 0.2.9.dev7__py3-none-any.whl → 0.2.9.dev9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of synth-ai might be problematic. Click here for more details.
- examples/__init__.py +16 -0
- examples/crafter_debug_render.py +8 -11
- examples/qwen_coder/README.md +102 -0
- examples/qwen_coder/_shared.py +113 -0
- examples/qwen_coder/configs/coder_lora_30b.toml +61 -0
- examples/qwen_coder/configs/coder_lora_4b.toml +57 -0
- examples/qwen_coder/configs/coder_lora_small.toml +58 -0
- examples/qwen_coder/generate_dataset.py +98 -0
- examples/qwen_coder/infer_ft_smoke.py +64 -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 +18 -0
- examples/qwen_coder/scripts/train_coder_30b.sh +21 -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 +38 -0
- examples/qwen_coder/validate_jsonl.py +59 -0
- examples/rl/run_eval.py +36 -37
- examples/rl/run_rl_and_save.py +5 -5
- examples/rl/task_app/math_single_step.py +65 -43
- examples/rl/task_app/math_task_app.py +3 -3
- examples/sft/README.md +139 -0
- examples/sft/configs/crafter_fft_qwen0p6b.toml +44 -0
- examples/sft/configs/crafter_lora_qwen0p6b.toml +45 -0
- examples/sft/evaluate.py +117 -0
- examples/sft/export_dataset.py +117 -0
- examples/sft/generate_traces.py +162 -0
- examples/swe/__init__.py +12 -0
- examples/swe/task_app/README.md +105 -0
- examples/swe/task_app/__init__.py +2 -0
- examples/swe/task_app/grpo_swe_mini.py +571 -0
- examples/swe/task_app/grpo_swe_mini_task_app.py +136 -0
- examples/swe/task_app/hosted/README.md +173 -0
- examples/swe/task_app/hosted/__init__.py +5 -0
- examples/swe/task_app/hosted/branching.py +143 -0
- examples/swe/task_app/hosted/environment_routes.py +1289 -0
- examples/swe/task_app/hosted/envs/__init__.py +1 -0
- examples/swe/task_app/hosted/envs/crafter/__init__.py +6 -0
- examples/swe/task_app/hosted/envs/crafter/app.py +1 -0
- examples/swe/task_app/hosted/envs/crafter/environment.py +522 -0
- examples/swe/task_app/hosted/envs/crafter/policy.py +478 -0
- examples/swe/task_app/hosted/envs/crafter/react_agent.py +108 -0
- examples/swe/task_app/hosted/envs/crafter/shared.py +305 -0
- examples/swe/task_app/hosted/envs/crafter/tools.py +47 -0
- examples/swe/task_app/hosted/envs/mini_swe/__init__.py +8 -0
- examples/swe/task_app/hosted/envs/mini_swe/environment.py +1164 -0
- examples/swe/task_app/hosted/envs/mini_swe/policy.py +355 -0
- examples/swe/task_app/hosted/envs/mini_swe/shared.py +83 -0
- examples/swe/task_app/hosted/envs/mini_swe/tools.py +96 -0
- examples/swe/task_app/hosted/hosted_app.py +204 -0
- examples/swe/task_app/hosted/inference/__init__.py +5 -0
- examples/swe/task_app/hosted/inference/openai_client.py +618 -0
- examples/swe/task_app/hosted/main.py +100 -0
- examples/swe/task_app/hosted/policy_routes.py +1079 -0
- examples/swe/task_app/hosted/registry.py +195 -0
- examples/swe/task_app/hosted/rollout.py +1869 -0
- examples/swe/task_app/hosted/storage/__init__.py +5 -0
- examples/swe/task_app/hosted/storage/volume.py +211 -0
- examples/swe/task_app/hosted/test_agents.py +161 -0
- examples/swe/task_app/hosted/test_service.py +137 -0
- examples/swe/task_app/hosted/utils.py +62 -0
- examples/vlm/README.md +68 -0
- examples/vlm/configs/crafter_vlm_gpt4o.toml +44 -0
- examples/vlm/crafter_image_only_agent.py +207 -0
- examples/vlm/crafter_openai_vlm_agent.py +277 -0
- examples/vlm/filter_image_rows.py +63 -0
- examples/vlm/run_crafter_vlm_benchmark.py +316 -0
- examples/warming_up_to_rl/analyze_trace_db.py +5 -5
- examples/warming_up_to_rl/configs/rl_from_base_qwen4b.toml +11 -1
- examples/warming_up_to_rl/export_trace_sft.py +78 -21
- examples/warming_up_to_rl/groq_test.py +4 -4
- examples/warming_up_to_rl/manage_secrets.py +13 -18
- examples/warming_up_to_rl/run_eval.py +42 -44
- examples/warming_up_to_rl/run_fft_and_save.py +11 -16
- examples/warming_up_to_rl/run_local_rollout.py +1 -3
- examples/warming_up_to_rl/run_local_rollout_modal.py +2 -4
- examples/warming_up_to_rl/run_local_rollout_parallel.py +1 -4
- examples/warming_up_to_rl/run_local_rollout_traced.py +3 -5
- examples/warming_up_to_rl/run_rl_and_save.py +5 -6
- examples/warming_up_to_rl/run_rollout_remote.py +8 -10
- examples/warming_up_to_rl/task_app/README.md +6 -2
- examples/warming_up_to_rl/task_app/grpo_crafter.py +234 -35
- examples/warming_up_to_rl/task_app/grpo_crafter_task_app.py +2 -3
- examples/warming_up_to_rl/task_app/synth_envs_hosted/__init__.py +1 -1
- examples/warming_up_to_rl/task_app/synth_envs_hosted/branching.py +9 -11
- examples/warming_up_to_rl/task_app/synth_envs_hosted/environment_routes.py +131 -114
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/environment.py +101 -41
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/policy.py +73 -51
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/react_agent.py +14 -6
- examples/warming_up_to_rl/task_app/synth_envs_hosted/envs/crafter/shared.py +16 -16
- examples/warming_up_to_rl/task_app/synth_envs_hosted/hosted_app.py +32 -34
- examples/warming_up_to_rl/task_app/synth_envs_hosted/inference/openai_client.py +94 -31
- examples/warming_up_to_rl/task_app/synth_envs_hosted/main.py +0 -2
- examples/warming_up_to_rl/task_app/synth_envs_hosted/policy_routes.py +303 -203
- examples/warming_up_to_rl/task_app/synth_envs_hosted/registry.py +21 -23
- examples/warming_up_to_rl/task_app/synth_envs_hosted/rollout.py +328 -225
- examples/warming_up_to_rl/task_app/synth_envs_hosted/storage/volume.py +13 -13
- examples/warming_up_to_rl/task_app/synth_envs_hosted/test_agents.py +1 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/test_service.py +1 -0
- examples/warming_up_to_rl/task_app/synth_envs_hosted/utils.py +4 -3
- synth/__init__.py +14 -0
- synth_ai/__init__.py +26 -4
- synth_ai/api/models/supported.py +376 -0
- synth_ai/api/train/builders.py +128 -21
- synth_ai/api/train/cli.py +80 -64
- synth_ai/api/train/config_finder.py +7 -2
- synth_ai/api/train/env_resolver.py +1 -1
- synth_ai/api/train/pollers.py +2 -1
- synth_ai/api/train/supported_algos.py +139 -0
- synth_ai/api/train/task_app.py +1 -2
- synth_ai/api/train/utils.py +13 -44
- synth_ai/cli/__init__.py +8 -0
- synth_ai/cli/_modal_wrapper.py +28 -0
- synth_ai/cli/_typer_patch.py +49 -0
- synth_ai/cli/balance.py +1 -2
- synth_ai/cli/calc.py +1 -1
- synth_ai/cli/demo.py +2 -1
- synth_ai/cli/recent.py +2 -2
- synth_ai/cli/rl_demo.py +2 -1
- synth_ai/cli/root.py +11 -13
- synth_ai/cli/status.py +2 -2
- synth_ai/cli/task_apps.py +529 -179
- synth_ai/cli/traces.py +6 -4
- synth_ai/cli/watch.py +12 -18
- synth_ai/demo_registry.py +1 -1
- synth_ai/demos/core/cli.py +36 -43
- synth_ai/demos/demo_task_apps/__init__.py +3 -3
- synth_ai/demos/demo_task_apps/core.py +17 -25
- synth_ai/demos/demo_task_apps/crafter/grpo_crafter_task_app.py +3 -4
- synth_ai/demos/demo_task_apps/math/app.py +2 -1
- synth_ai/demos/demo_task_apps/math/deploy_modal.py +3 -4
- synth_ai/demos/demo_task_apps/math/modal_task_app.py +16 -18
- synth_ai/demos/demo_task_apps/math/task_app_entry.py +0 -1
- synth_ai/environments/examples/crafter_classic/environment.py +76 -1
- synth_ai/environments/reproducibility/tree.py +2 -5
- synth_ai/environments/service/app.py +11 -12
- synth_ai/environments/service/core_routes.py +4 -7
- synth_ai/environments/stateful/engine.py +1 -1
- synth_ai/environments/tasks/core.py +1 -0
- synth_ai/environments/tasks/filters.py +5 -6
- synth_ai/environments/tasks/utils.py +4 -5
- synth_ai/handshake.py +9 -9
- synth_ai/http.py +1 -1
- synth_ai/http_client.py +18 -10
- synth_ai/inference/client.py +15 -5
- synth_ai/jobs/client.py +78 -83
- synth_ai/learning/__init__.py +41 -6
- synth_ai/learning/algorithms.py +14 -0
- synth_ai/learning/client.py +91 -24
- synth_ai/learning/config.py +2 -38
- synth_ai/learning/ft_client.py +4 -59
- synth_ai/learning/health.py +5 -6
- synth_ai/learning/jobs.py +31 -47
- synth_ai/{rl → learning/rl}/__init__.py +14 -4
- synth_ai/learning/rl/client.py +267 -0
- synth_ai/learning/rl/config.py +31 -0
- synth_ai/{rl → learning/rl}/contracts.py +5 -8
- synth_ai/{rl → learning/rl}/env_keys.py +39 -15
- synth_ai/learning/rl/secrets.py +13 -0
- synth_ai/learning/rl_client.py +2 -281
- synth_ai/learning/sft/__init__.py +29 -0
- synth_ai/learning/sft/client.py +68 -0
- synth_ai/learning/sft/config.py +270 -0
- synth_ai/learning/sft/data.py +295 -0
- synth_ai/learning/sse.py +25 -24
- synth_ai/learning/validators.py +25 -28
- synth_ai/lm/__init__.py +21 -47
- synth_ai/main.py +6 -0
- synth_ai/task/__init__.py +25 -27
- synth_ai/task/apps/__init__.py +7 -8
- synth_ai/task/auth.py +8 -8
- synth_ai/task/client.py +14 -14
- synth_ai/task/contracts.py +36 -35
- synth_ai/task/datasets.py +6 -5
- synth_ai/task/errors.py +10 -10
- synth_ai/task/health.py +17 -9
- synth_ai/task/json.py +58 -23
- synth_ai/task/proxy.py +13 -9
- synth_ai/task/rubrics.py +16 -15
- synth_ai/task/server.py +12 -12
- synth_ai/task/tracing_utils.py +4 -4
- synth_ai/task/vendors.py +5 -6
- synth_ai/tracing_v3/__init__.py +2 -0
- synth_ai/tracing_v3/abstractions.py +21 -4
- synth_ai/tracing_v3/decorators.py +18 -16
- synth_ai/tracing_v3/hooks.py +5 -5
- synth_ai/tracing_v3/llm_call_record_helpers.py +6 -6
- synth_ai/tracing_v3/session_tracer.py +40 -14
- synth_ai/tracing_v3/storage/base.py +85 -0
- synth_ai/tracing_v3/storage/config.py +21 -8
- synth_ai/tracing_v3/storage/factory.py +10 -7
- synth_ai/tracing_v3/storage/utils.py +4 -2
- synth_ai/tracing_v3/turso/daemon.py +7 -2
- synth_ai/tracing_v3/turso/models.py +2 -2
- synth_ai/tracing_v3/turso/native_manager.py +1173 -0
- synth_ai/tracing_v3/utils.py +4 -4
- synth_ai/v0/api/__init__.py +8 -0
- synth_ai/v0/api/models/__init__.py +8 -0
- synth_ai/v0/api/models/supported.py +8 -0
- synth_ai/v0/config/__init__.py +15 -0
- synth_ai/v0/config/base_url.py +12 -0
- synth_ai/v0/lm/__init__.py +51 -0
- synth_ai/{lm → v0/lm}/caching/ephemeral.py +2 -2
- synth_ai/{lm → v0/lm}/caching/handler.py +4 -4
- synth_ai/{lm → v0/lm}/caching/initialize.py +1 -1
- synth_ai/{lm → v0/lm}/caching/persistent.py +1 -1
- synth_ai/{lm → v0/lm}/config.py +6 -1
- synth_ai/{lm → v0/lm}/core/all.py +9 -9
- synth_ai/{lm → v0/lm}/core/main.py +6 -6
- synth_ai/{lm → v0/lm}/core/main_v3.py +10 -10
- synth_ai/{lm → v0/lm}/core/synth_models.py +2 -14
- synth_ai/{lm → v0/lm}/core/vendor_clients.py +2 -2
- synth_ai/{lm → v0/lm}/overrides.py +2 -2
- synth_ai/{lm → v0/lm}/provider_support/anthropic.py +4 -4
- synth_ai/{lm → v0/lm}/provider_support/openai.py +5 -5
- synth_ai/{lm → v0/lm}/structured_outputs/handler.py +5 -5
- synth_ai/{lm → v0/lm}/structured_outputs/rehabilitate.py +1 -1
- synth_ai/{lm → v0/lm}/vendors/core/anthropic_api.py +9 -9
- synth_ai/{lm → v0/lm}/vendors/core/gemini_api.py +5 -5
- synth_ai/{lm → v0/lm}/vendors/core/mistral_api.py +5 -5
- synth_ai/{lm → v0/lm}/vendors/core/openai_api.py +10 -10
- synth_ai/{lm → v0/lm}/vendors/openai_standard.py +8 -8
- synth_ai/{lm → v0/lm}/vendors/openai_standard_responses.py +2 -2
- synth_ai/{lm → v0/lm}/vendors/supported/custom_endpoint.py +3 -3
- synth_ai/{lm → v0/lm}/vendors/supported/deepseek.py +2 -2
- synth_ai/{lm → v0/lm}/vendors/supported/grok.py +2 -2
- synth_ai/{lm → v0/lm}/vendors/supported/groq.py +1 -1
- synth_ai/{lm → v0/lm}/vendors/supported/ollama.py +1 -1
- synth_ai/{lm → v0/lm}/vendors/supported/openrouter.py +3 -3
- synth_ai/{lm → v0/lm}/vendors/supported/together.py +1 -1
- synth_ai/{lm → v0/lm}/vendors/synth_client.py +1 -1
- synth_ai/v0/tracing_v3/__init__.py +10 -0
- synth_ai/v0/tracing_v3/abstractions.py +3 -0
- synth_ai/v0/tracing_v3/decorators.py +3 -0
- synth_ai/v0/tracing_v3/llm_call_record_helpers.py +3 -0
- synth_ai/v0/tracing_v3/session_tracer.py +3 -0
- synth_ai-0.2.9.dev9.dist-info/METADATA +191 -0
- {synth_ai-0.2.9.dev7.dist-info → synth_ai-0.2.9.dev9.dist-info}/RECORD +268 -238
- {synth_ai-0.2.9.dev7.dist-info → synth_ai-0.2.9.dev9.dist-info}/top_level.txt +1 -0
- examples/common_old/backend.py +0 -20
- examples/evals_old/README.md +0 -98
- examples/evals_old/__init__.py +0 -6
- examples/evals_old/compare_models.py +0 -1038
- examples/evals_old/example_log.md +0 -145
- examples/evals_old/run_demo.sh +0 -126
- examples/evals_old/trace_analysis.py +0 -270
- examples/finetuning_old/_backup_synth_qwen/config.toml +0 -29
- examples/finetuning_old/_backup_synth_qwen/example_log.md +0 -324
- examples/finetuning_old/_backup_synth_qwen/filter_traces.py +0 -60
- examples/finetuning_old/_backup_synth_qwen/filter_traces_achievements.py +0 -243
- examples/finetuning_old/_backup_synth_qwen/purge_v3_traces.py +0 -109
- examples/finetuning_old/_backup_synth_qwen/react_agent_lm.py +0 -1924
- examples/finetuning_old/_backup_synth_qwen/readme.md +0 -49
- examples/finetuning_old/_backup_synth_qwen/run_crafter_qwen4b.py +0 -114
- examples/finetuning_old/_backup_synth_qwen/run_demo.sh +0 -195
- examples/finetuning_old/_backup_synth_qwen/sft_kickoff.py +0 -119
- examples/finetuning_old/synth_qwen_v1/README.md +0 -68
- examples/finetuning_old/synth_qwen_v1/filter_traces.py +0 -60
- examples/finetuning_old/synth_qwen_v1/filter_traces_achievements.py +0 -243
- examples/finetuning_old/synth_qwen_v1/finetune.py +0 -46
- examples/finetuning_old/synth_qwen_v1/hello_ft_model.py +0 -71
- examples/finetuning_old/synth_qwen_v1/infer.py +0 -36
- examples/finetuning_old/synth_qwen_v1/poll.py +0 -46
- examples/finetuning_old/synth_qwen_v1/prepare_data.py +0 -35
- examples/finetuning_old/synth_qwen_v1/purge_v3_traces.py +0 -109
- examples/finetuning_old/synth_qwen_v1/react_agent_lm.py +0 -1933
- examples/finetuning_old/synth_qwen_v1/run_crafter_sft_job.py +0 -210
- examples/finetuning_old/synth_qwen_v1/run_ft_job.py +0 -237
- examples/finetuning_old/synth_qwen_v1/upload_data.py +0 -34
- examples/finetuning_old/synth_qwen_v1/util.py +0 -152
- examples/rl_old/task_app.py +0 -1131
- examples/warming_up_to_rl/old/event_rewards.md +0 -234
- examples/warming_up_to_rl/old/notes.md +0 -73
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_modal_ft/filter_traces_sft_turso.py +0 -738
- synth_ai/environments/examples/crafter_classic/agent_demos/crafter_openai_ft/filter_traces_sft_turso.py +0 -580
- synth_ai/experimental/synth_oss.py +0 -445
- synth_ai/learning/filtering.py +0 -0
- synth_ai/learning/offline/dpo.py +0 -0
- synth_ai/learning/offline/providers.py +0 -7
- synth_ai/learning/offline/sft.py +0 -0
- synth_ai/learning/offline/shared.py +0 -0
- synth_ai/learning/online/grpo.py +0 -0
- synth_ai/learning/online/irft.py +0 -0
- synth_ai/learning/prompts/banking77_injection_eval.py +0 -168
- synth_ai/learning/prompts/gepa.py +0 -0
- synth_ai/learning/prompts/hello_world_in_context_injection_ex.py +0 -211
- synth_ai/learning/prompts/mipro.py +0 -289
- synth_ai/learning/prompts/random_search.py +0 -249
- synth_ai/learning/prompts/run_mipro_banking77.py +0 -172
- synth_ai/learning/prompts/run_random_search_banking77.py +0 -329
- synth_ai/rl/secrets.py +0 -19
- synth_ai/scripts/verify_rewards.py +0 -100
- synth_ai/tracing/__init__.py +0 -30
- synth_ai/tracing_v1/__init__.py +0 -33
- synth_ai/tracing_v3/turso/__init__.py +0 -25
- synth_ai/tracing_v3/turso/manager.py +0 -838
- synth_ai/zyk/__init__.py +0 -30
- synth_ai-0.2.9.dev7.dist-info/METADATA +0 -131
- /synth_ai/{lm → v0/lm}/caching/__init__.py +0 -0
- /synth_ai/{lm → v0/lm}/caching/constants.py +0 -0
- /synth_ai/{lm → v0/lm}/caching/dbs.py +0 -0
- /synth_ai/{lm → v0/lm}/constants.py +0 -0
- /synth_ai/{lm → v0/lm}/core/__init__.py +0 -0
- /synth_ai/{lm → v0/lm}/core/exceptions.py +0 -0
- /synth_ai/{lm → v0/lm}/cost/__init__.py +0 -0
- /synth_ai/{lm → v0/lm}/cost/monitor.py +0 -0
- /synth_ai/{lm → v0/lm}/cost/statefulness.py +0 -0
- /synth_ai/{lm → v0/lm}/injection.py +0 -0
- /synth_ai/{lm → v0/lm}/provider_support/__init__.py +0 -0
- /synth_ai/{lm → v0/lm}/provider_support/suppress_logging.py +0 -0
- /synth_ai/{lm → v0/lm}/structured_outputs/__init__.py +0 -0
- /synth_ai/{lm → v0/lm}/structured_outputs/inject.py +0 -0
- /synth_ai/{lm → v0/lm}/tools/__init__.py +0 -0
- /synth_ai/{lm → v0/lm}/tools/base.py +0 -0
- /synth_ai/{lm → v0/lm}/unified_interface.py +0 -0
- /synth_ai/{lm → v0/lm}/vendors/__init__.py +0 -0
- /synth_ai/{lm → v0/lm}/vendors/base.py +0 -0
- /synth_ai/{lm → v0/lm}/vendors/core/__init__.py +0 -0
- /synth_ai/{lm → v0/lm}/vendors/core/synth_dev_api.py +0 -0
- /synth_ai/{lm → v0/lm}/vendors/local/__init__.py +0 -0
- /synth_ai/{lm → v0/lm}/vendors/local/ollama.py +0 -0
- /synth_ai/{lm → v0/lm}/vendors/retries.py +0 -0
- /synth_ai/{lm → v0/lm}/vendors/supported/__init__.py +0 -0
- /synth_ai/{lm → v0/lm}/warmup.py +0 -0
- {synth_ai-0.2.9.dev7.dist-info → synth_ai-0.2.9.dev9.dist-info}/WHEEL +0 -0
- {synth_ai-0.2.9.dev7.dist-info → synth_ai-0.2.9.dev9.dist-info}/entry_points.txt +0 -0
- {synth_ai-0.2.9.dev7.dist-info → synth_ai-0.2.9.dev9.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
1
|
"""Modal task app for Hendrycks MATH single-step RL environment."""
|
|
4
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
5
|
import os
|
|
6
|
+
from collections.abc import Iterable
|
|
7
|
+
from functools import lru_cache
|
|
6
8
|
from pathlib import Path
|
|
7
9
|
|
|
8
10
|
from modal import App, Image, Secret, asgi_app
|
|
9
|
-
from functools import lru_cache
|
|
10
|
-
from typing import Iterable
|
|
11
|
-
|
|
12
11
|
from starlette.requests import Request
|
|
13
12
|
|
|
14
13
|
try: # Backward compatibility with older installed SDKs
|
|
@@ -99,8 +98,7 @@ app = App("hendrycks-math-task-app")
|
|
|
99
98
|
@asgi_app()
|
|
100
99
|
def fastapi_app():
|
|
101
100
|
import httpx
|
|
102
|
-
from fastapi import Body, HTTPException, status
|
|
103
|
-
from fastapi import FastAPI
|
|
101
|
+
from fastapi import Body, FastAPI, HTTPException, status
|
|
104
102
|
from fastapi.middleware.cors import CORSMiddleware
|
|
105
103
|
from fastapi.responses import JSONResponse
|
|
106
104
|
|
|
@@ -388,7 +386,7 @@ def fastapi_app():
|
|
|
388
386
|
try:
|
|
389
387
|
hdr = request.headers
|
|
390
388
|
snapshot = {
|
|
391
|
-
"path": str(
|
|
389
|
+
"path": str(request.url.path),
|
|
392
390
|
"have_x_api_key": bool(hdr.get("x-api-key")),
|
|
393
391
|
"have_x_api_keys": bool(hdr.get("x-api-keys")),
|
|
394
392
|
"have_authorization": bool(hdr.get("authorization")),
|
|
@@ -412,32 +410,32 @@ def fastapi_app():
|
|
|
412
410
|
env_key = (
|
|
413
411
|
os.environ.get("ENVIRONMENT_API_KEY")
|
|
414
412
|
or os.environ.get("DEV_ENVIRONMENT_API_KEY")
|
|
415
|
-
or os.environ.get("
|
|
413
|
+
or os.environ.get("DEV_ENVIRONMENT_API_KEY")
|
|
416
414
|
)
|
|
417
415
|
if not env_key:
|
|
418
416
|
raise RuntimeError("ENVIRONMENT_API_KEY missing in task app environment")
|
|
419
417
|
|
|
420
|
-
|
|
418
|
+
openai_remove_fields = (
|
|
421
419
|
"stop_after_tool_calls",
|
|
422
420
|
"thinking_mode",
|
|
423
421
|
"thinking_budget",
|
|
424
422
|
"reasoning",
|
|
425
423
|
)
|
|
426
|
-
|
|
427
|
-
|
|
424
|
+
openai_remove_sampling_fields = ("temperature", "top_p")
|
|
425
|
+
tool_choice_force = {"type": "function", "function": {"name": "submit_answer"}}
|
|
428
426
|
|
|
429
427
|
def _prepare_openai_payload(model: str | None, payload: dict[str, object]) -> dict[str, object]:
|
|
430
428
|
sanitized = dict(payload)
|
|
431
|
-
for key in
|
|
429
|
+
for key in openai_remove_fields:
|
|
432
430
|
sanitized.pop(key, None)
|
|
433
431
|
if model and "gpt-5" in model:
|
|
434
432
|
if "max_tokens" in sanitized and "max_completion_tokens" not in sanitized:
|
|
435
433
|
sanitized["max_completion_tokens"] = sanitized.pop("max_tokens")
|
|
436
434
|
else:
|
|
437
435
|
sanitized.pop("max_tokens", None)
|
|
438
|
-
for field in
|
|
436
|
+
for field in openai_remove_sampling_fields:
|
|
439
437
|
sanitized.pop(field, None)
|
|
440
|
-
sanitized["tool_choice"] =
|
|
438
|
+
sanitized["tool_choice"] = tool_choice_force
|
|
441
439
|
sanitized["parallel_tool_calls"] = False
|
|
442
440
|
return sanitized
|
|
443
441
|
|
|
@@ -470,8 +468,8 @@ def fastapi_app():
|
|
|
470
468
|
# Minimal math rollout endpoint: alternates agent/env; calls inference_url chat/completions
|
|
471
469
|
@api.post("/rollout")
|
|
472
470
|
def rollout(request: dict[str, object] = Body(...)):
|
|
473
|
-
from typing import Any
|
|
474
471
|
import json as _json
|
|
472
|
+
from typing import Any
|
|
475
473
|
|
|
476
474
|
run_id = str(request.get("run_id"))
|
|
477
475
|
data = request if isinstance(request, dict) else {}
|
|
@@ -563,7 +561,7 @@ def fastapi_app():
|
|
|
563
561
|
|
|
564
562
|
user_prompt = (
|
|
565
563
|
str(question)
|
|
566
|
-
if isinstance(question,
|
|
564
|
+
if isinstance(question, str | int | float) and str(question).strip()
|
|
567
565
|
else "Solve the problem. Provide answer steps succinctly."
|
|
568
566
|
)
|
|
569
567
|
payload = {
|
|
@@ -597,7 +595,7 @@ def fastapi_app():
|
|
|
597
595
|
name = fn.get("name")
|
|
598
596
|
if isinstance(name, str):
|
|
599
597
|
tool_names.append(name)
|
|
600
|
-
print(
|
|
598
|
+
print("[math] system: <none>", flush=True)
|
|
601
599
|
print(f"[math] user: {user_prompt}", flush=True)
|
|
602
600
|
print(f"[math] tools: {tool_names}", flush=True)
|
|
603
601
|
except Exception:
|
|
@@ -5,7 +5,6 @@ from __future__ import annotations
|
|
|
5
5
|
from synth_ai.task.apps import ModalDeploymentConfig, TaskAppEntry, register_task_app
|
|
6
6
|
from synth_ai.task.apps.math_single_step import build_config as base_build_config
|
|
7
7
|
|
|
8
|
-
|
|
9
8
|
DEMO_MODAL_CONFIG = ModalDeploymentConfig(
|
|
10
9
|
app_name="hendrycks-math-task-app",
|
|
11
10
|
pip_packages=(
|
|
@@ -2,11 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import base64
|
|
5
6
|
import dataclasses
|
|
6
7
|
import logging
|
|
7
8
|
import time
|
|
9
|
+
from io import BytesIO
|
|
8
10
|
from typing import Any, Dict, List, Optional, Union
|
|
9
11
|
|
|
12
|
+
import numpy as np
|
|
13
|
+
from PIL import Image
|
|
14
|
+
|
|
10
15
|
# Import tracing abstractions
|
|
11
16
|
from synth_ai.tracing_v3.abstractions import (
|
|
12
17
|
RuntimeEvent,
|
|
@@ -43,6 +48,51 @@ from synth_ai.environments.reproducibility.core import ReproducibleEnvironment
|
|
|
43
48
|
from synth_ai.environments.stateful.core import StatefulEnvironment
|
|
44
49
|
|
|
45
50
|
|
|
51
|
+
def _convert_numpy_to_python(obj: Any) -> Any:
|
|
52
|
+
if isinstance(obj, np.integer):
|
|
53
|
+
return int(obj)
|
|
54
|
+
if isinstance(obj, np.floating):
|
|
55
|
+
return float(obj)
|
|
56
|
+
if isinstance(obj, np.ndarray):
|
|
57
|
+
return obj.tolist()
|
|
58
|
+
if isinstance(obj, dict):
|
|
59
|
+
return {k: _convert_numpy_to_python(v) for k, v in obj.items()}
|
|
60
|
+
if isinstance(obj, (list, tuple)):
|
|
61
|
+
return [_convert_numpy_to_python(item) for item in obj]
|
|
62
|
+
return obj
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _encode_image_to_base64(image_array: Any) -> dict[str, Any] | None:
|
|
66
|
+
if not isinstance(image_array, np.ndarray):
|
|
67
|
+
return None
|
|
68
|
+
if image_array.ndim != 3 or image_array.shape[-1] not in (1, 3, 4):
|
|
69
|
+
return None
|
|
70
|
+
try:
|
|
71
|
+
array_uint8 = (
|
|
72
|
+
image_array.astype("uint8")
|
|
73
|
+
if image_array.dtype != np.uint8
|
|
74
|
+
else image_array # pragma: no cover - fast path
|
|
75
|
+
)
|
|
76
|
+
mode = "L" if array_uint8.shape[-1] == 1 else "RGB"
|
|
77
|
+
if array_uint8.shape[-1] == 4:
|
|
78
|
+
mode = "RGBA"
|
|
79
|
+
img = Image.fromarray(array_uint8, mode=mode)
|
|
80
|
+
buffer = BytesIO()
|
|
81
|
+
img.save(buffer, format="PNG")
|
|
82
|
+
encoded = base64.b64encode(buffer.getvalue()).decode("ascii")
|
|
83
|
+
width = int(array_uint8.shape[1])
|
|
84
|
+
height = int(array_uint8.shape[0])
|
|
85
|
+
return {
|
|
86
|
+
"format": "png",
|
|
87
|
+
"width": width,
|
|
88
|
+
"height": height,
|
|
89
|
+
"data": encoded,
|
|
90
|
+
"data_url": f"data:image/png;base64,{encoded}",
|
|
91
|
+
}
|
|
92
|
+
except Exception:
|
|
93
|
+
return None
|
|
94
|
+
|
|
95
|
+
|
|
46
96
|
# --- Tool Definition ---
|
|
47
97
|
class CrafterActionInput(BaseModel):
|
|
48
98
|
action: int = Field(..., description="Integer action for the Crafter environment.")
|
|
@@ -362,7 +412,8 @@ class CrafterClassicEnvironment(StatefulEnvironment, ReproducibleEnvironment[Cra
|
|
|
362
412
|
state_before = {"private_state": priv, "public_state": pub}
|
|
363
413
|
|
|
364
414
|
active_obs_cb = obs_cb or SynthCrafterObservationCallable()
|
|
365
|
-
|
|
415
|
+
raw_observation = await active_obs_cb.get_observation(pub, priv)
|
|
416
|
+
observation = self._prepare_observation(raw_observation)
|
|
366
417
|
if extra_obs and isinstance(observation, dict):
|
|
367
418
|
observation.update(extra_obs)
|
|
368
419
|
|
|
@@ -385,6 +436,30 @@ class CrafterClassicEnvironment(StatefulEnvironment, ReproducibleEnvironment[Cra
|
|
|
385
436
|
|
|
386
437
|
return observation
|
|
387
438
|
|
|
439
|
+
def _prepare_observation(self, observation: Any) -> dict[str, Any]:
|
|
440
|
+
obs_dict: dict[str, Any]
|
|
441
|
+
image_payload: dict[str, Any] | None = None
|
|
442
|
+
|
|
443
|
+
if isinstance(observation, dict):
|
|
444
|
+
image_payload = _encode_image_to_base64(observation.get("observation_image"))
|
|
445
|
+
sanitized = dict(observation)
|
|
446
|
+
sanitized.pop("observation_image", None)
|
|
447
|
+
obs_dict = _convert_numpy_to_python(sanitized) or {}
|
|
448
|
+
else:
|
|
449
|
+
obs_dict = _convert_numpy_to_python(observation) or {}
|
|
450
|
+
|
|
451
|
+
if not isinstance(obs_dict, dict):
|
|
452
|
+
obs_dict = {"value": obs_dict}
|
|
453
|
+
|
|
454
|
+
if image_payload:
|
|
455
|
+
obs_dict["observation_image_base64"] = image_payload["data"]
|
|
456
|
+
obs_dict["observation_image_format"] = image_payload["format"]
|
|
457
|
+
obs_dict["observation_image_width"] = image_payload["width"]
|
|
458
|
+
obs_dict["observation_image_height"] = image_payload["height"]
|
|
459
|
+
obs_dict["observation_image_data_url"] = image_payload["data_url"]
|
|
460
|
+
|
|
461
|
+
return obs_dict
|
|
462
|
+
|
|
388
463
|
# ────────────────────────────────────────────────────────────────────
|
|
389
464
|
# ReproducibleEnvironment plumbing
|
|
390
465
|
# ────────────────────────────────────────────────────────────────────
|
|
@@ -14,8 +14,10 @@ big “backend.production” code-base.
|
|
|
14
14
|
from __future__ import annotations
|
|
15
15
|
|
|
16
16
|
import gzip
|
|
17
|
+
import hashlib
|
|
17
18
|
import json
|
|
18
19
|
import logging
|
|
20
|
+
import os
|
|
19
21
|
import pickle
|
|
20
22
|
import sqlite3
|
|
21
23
|
from collections.abc import Iterable
|
|
@@ -32,11 +34,6 @@ log = logging.getLogger(__name__)
|
|
|
32
34
|
# --------------------------------------------------------------------------- #
|
|
33
35
|
# lightweight metadata record #
|
|
34
36
|
# --------------------------------------------------------------------------- #
|
|
35
|
-
import hashlib
|
|
36
|
-
import logging
|
|
37
|
-
import os
|
|
38
|
-
|
|
39
|
-
log = logging.getLogger(__name__)
|
|
40
37
|
|
|
41
38
|
# Default directory for storing snapshots relative to some base path
|
|
42
39
|
# This could be configured via environment variables or settings later.
|
|
@@ -1,6 +1,17 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
import os # Added to ensure os is available before use
|
|
2
3
|
import sys
|
|
3
4
|
|
|
5
|
+
import synth_ai.environments.examples.crafter_classic.environment as cc
|
|
6
|
+
import synth_ai.environments.examples.crafter_custom.environment as ccustom
|
|
7
|
+
from fastapi import FastAPI
|
|
8
|
+
from synth_ai.environments.service.core_routes import api_router
|
|
9
|
+
from synth_ai.environments.service.external_registry import (
|
|
10
|
+
ExternalRegistryConfig,
|
|
11
|
+
load_external_environments,
|
|
12
|
+
)
|
|
13
|
+
from synth_ai.environments.service.registry import list_supported_env_types, register_environment
|
|
14
|
+
|
|
4
15
|
# Ensure repository root is on PYTHONPATH for dev installs
|
|
5
16
|
# Current file path: <repo>/synth_ai/environments/service/app.py
|
|
6
17
|
# We want sys.path to include <repo>, NOT <repo>/synth_ai to avoid shadowing stdlib 'http'
|
|
@@ -16,15 +27,6 @@ if _repo_root not in sys.path:
|
|
|
16
27
|
sys.path.insert(0, _repo_root)
|
|
17
28
|
|
|
18
29
|
print(f"SYS.PATH IN APP.PY: {sys.path}")
|
|
19
|
-
import logging
|
|
20
|
-
|
|
21
|
-
from fastapi import FastAPI
|
|
22
|
-
from synth_ai.environments.service.core_routes import api_router
|
|
23
|
-
from synth_ai.environments.service.external_registry import (
|
|
24
|
-
ExternalRegistryConfig,
|
|
25
|
-
load_external_environments,
|
|
26
|
-
)
|
|
27
|
-
from synth_ai.environments.service.registry import list_supported_env_types, register_environment
|
|
28
30
|
|
|
29
31
|
# Configure logging with more detail
|
|
30
32
|
logging.basicConfig(
|
|
@@ -38,11 +40,8 @@ logger = logging.getLogger(__name__)
|
|
|
38
40
|
logging.getLogger("uvicorn.access").setLevel(logging.INFO)
|
|
39
41
|
|
|
40
42
|
# Register built-in environments at import time
|
|
41
|
-
import synth_ai.environments.examples.crafter_classic.environment as cc
|
|
42
43
|
|
|
43
44
|
register_environment("CrafterClassic", cc.CrafterClassicEnvironment)
|
|
44
|
-
import synth_ai.environments.examples.crafter_custom.environment as ccustom
|
|
45
|
-
|
|
46
45
|
register_environment("CrafterCustom", ccustom.CrafterCustomEnvironment)
|
|
47
46
|
|
|
48
47
|
# Register Wordle example environment
|
|
@@ -97,15 +97,12 @@ def create_task_instance_for_environment(
|
|
|
97
97
|
task.initial_engine_snapshot["seed"] = config["seed"]
|
|
98
98
|
|
|
99
99
|
# For CrafterClassic, also handle difficulty
|
|
100
|
-
if env_name == "CrafterClassic" and config:
|
|
101
|
-
|
|
102
|
-
task.initial_engine_snapshot["difficulty"] = config["difficulty"]
|
|
100
|
+
if env_name == "CrafterClassic" and config and "difficulty" in config:
|
|
101
|
+
task.initial_engine_snapshot["difficulty"] = config["difficulty"]
|
|
103
102
|
|
|
104
103
|
# For MiniGrid, handle environment selection
|
|
105
|
-
if env_name == "MiniGrid" and config:
|
|
106
|
-
|
|
107
|
-
if "env_name" in config:
|
|
108
|
-
task.initial_engine_snapshot["env_name"] = config["env_name"]
|
|
104
|
+
if env_name == "MiniGrid" and config and "env_name" in config:
|
|
105
|
+
task.initial_engine_snapshot["env_name"] = config["env_name"]
|
|
109
106
|
|
|
110
107
|
return task
|
|
111
108
|
|
|
@@ -29,13 +29,12 @@ class RangeFilter(TaskInstanceMetadataFilter):
|
|
|
29
29
|
# If the attribute doesn't exist on the metadata, it can't be in range.
|
|
30
30
|
return False
|
|
31
31
|
|
|
32
|
-
if not isinstance(instance_value,
|
|
32
|
+
if not isinstance(instance_value, int | float):
|
|
33
33
|
# If the attribute is not a number, it can't be in a numerical range.
|
|
34
34
|
# Or, we could raise an error, depending on desired strictness.
|
|
35
35
|
return False
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return True
|
|
37
|
+
return not (
|
|
38
|
+
(self.min_val is not None and instance_value < self.min_val)
|
|
39
|
+
or (self.max_val is not None and instance_value > self.max_val)
|
|
40
|
+
)
|
|
@@ -54,11 +54,10 @@ class RangeFilter(TaskInstanceMetadataFilter):
|
|
|
54
54
|
|
|
55
55
|
def __call__(self, instance: TaskInstance) -> bool:
|
|
56
56
|
value = getattr(instance.metadata, self.key, None)
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return True
|
|
57
|
+
return not (
|
|
58
|
+
(self.min_value is not None and (value is None or value < self.min_value))
|
|
59
|
+
or (self.max_value is not None and (value is None or value > self.max_value))
|
|
60
|
+
)
|
|
62
61
|
|
|
63
62
|
|
|
64
63
|
def make_taskset(
|
synth_ai/handshake.py
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import contextlib
|
|
2
4
|
import os
|
|
3
5
|
import time
|
|
4
6
|
import webbrowser
|
|
5
|
-
from typing import Any
|
|
7
|
+
from typing import Any
|
|
6
8
|
from urllib.parse import urljoin, urlsplit, urlunsplit
|
|
7
9
|
|
|
8
10
|
import requests
|
|
@@ -43,7 +45,7 @@ def _split_origin(origin: str) -> tuple[str, str]:
|
|
|
43
45
|
return bare, path
|
|
44
46
|
|
|
45
47
|
|
|
46
|
-
def _ensure_verification_uri(data:
|
|
48
|
+
def _ensure_verification_uri(data: dict[str, Any], base_with_path: str) -> None:
|
|
47
49
|
uri = data.get("verification_uri")
|
|
48
50
|
if not isinstance(uri, str) or not uri:
|
|
49
51
|
return
|
|
@@ -52,7 +54,7 @@ def _ensure_verification_uri(data: Dict[str, Any], base_with_path: str) -> None:
|
|
|
52
54
|
data["verification_uri"] = urljoin(base_with_path.rstrip("/") + "/", uri.lstrip("/"))
|
|
53
55
|
|
|
54
56
|
|
|
55
|
-
def start_handshake_session(origin: str | None = None) ->
|
|
57
|
+
def start_handshake_session(origin: str | None = None) -> tuple[str, str, int, int]:
|
|
56
58
|
base = (origin or _origin()).rstrip("/")
|
|
57
59
|
api_origin, _ = _split_origin(base)
|
|
58
60
|
url = urljoin(api_origin.rstrip("/") + "/", "api/sdk/handshake/init")
|
|
@@ -74,7 +76,7 @@ def start_handshake_session(origin: str | None = None) -> Tuple[str, str, int, i
|
|
|
74
76
|
|
|
75
77
|
def poll_handshake_token(
|
|
76
78
|
device_code: str, origin: str | None = None, *, timeout_s: int | None = None
|
|
77
|
-
) ->
|
|
79
|
+
) -> dict[str, Any]:
|
|
78
80
|
base = (origin or _origin()).rstrip("/")
|
|
79
81
|
api_origin, _ = _split_origin(base)
|
|
80
82
|
url = urljoin(api_origin.rstrip("/") + "/", "api/sdk/handshake/token")
|
|
@@ -84,7 +86,7 @@ def poll_handshake_token(
|
|
|
84
86
|
raise HandshakeError("handshake timed out")
|
|
85
87
|
try:
|
|
86
88
|
r = requests.post(url, json={"device_code": device_code}, timeout=10)
|
|
87
|
-
except Exception
|
|
89
|
+
except Exception:
|
|
88
90
|
time.sleep(2)
|
|
89
91
|
continue
|
|
90
92
|
if r.status_code == 200:
|
|
@@ -100,10 +102,8 @@ def poll_handshake_token(
|
|
|
100
102
|
time.sleep(2)
|
|
101
103
|
|
|
102
104
|
|
|
103
|
-
def run_handshake(origin: str | None = None) ->
|
|
105
|
+
def run_handshake(origin: str | None = None) -> dict[str, Any]:
|
|
104
106
|
device_code, verification_uri, expires_in, interval = start_handshake_session(origin)
|
|
105
|
-
|
|
107
|
+
with contextlib.suppress(Exception):
|
|
106
108
|
webbrowser.open(verification_uri)
|
|
107
|
-
except Exception:
|
|
108
|
-
pass
|
|
109
109
|
return poll_handshake_token(device_code, origin, timeout_s=expires_in)
|
synth_ai/http.py
CHANGED
|
@@ -18,7 +18,7 @@ except Exception:
|
|
|
18
18
|
_client_path = _here.parent / "http_client.py"
|
|
19
19
|
_spec = _ilu.spec_from_file_location("http_client", str(_client_path))
|
|
20
20
|
if not _spec or not _spec.loader:
|
|
21
|
-
raise ImportError("Could not load http_client module")
|
|
21
|
+
raise ImportError("Could not load http_client module") from None
|
|
22
22
|
_mod = _ilu.module_from_spec(_spec)
|
|
23
23
|
_spec.loader.exec_module(_mod)
|
|
24
24
|
_sys.modules["synth_ai.http_client"] = _mod
|
synth_ai/http_client.py
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
+
import os
|
|
4
5
|
from dataclasses import dataclass
|
|
5
|
-
from typing import Any
|
|
6
|
+
from typing import Any
|
|
6
7
|
|
|
7
8
|
import aiohttp
|
|
8
9
|
|
|
@@ -27,11 +28,18 @@ class AsyncHttpClient:
|
|
|
27
28
|
self._base_url = base_url.rstrip("/")
|
|
28
29
|
self._api_key = api_key
|
|
29
30
|
self._timeout = aiohttp.ClientTimeout(total=timeout)
|
|
30
|
-
self._session:
|
|
31
|
+
self._session: aiohttp.ClientSession | None = None
|
|
31
32
|
|
|
32
|
-
async def __aenter__(self) ->
|
|
33
|
+
async def __aenter__(self) -> AsyncHttpClient:
|
|
33
34
|
if self._session is None:
|
|
34
35
|
headers = {"authorization": f"Bearer {self._api_key}"}
|
|
36
|
+
# Optional dev overrides for user/org context
|
|
37
|
+
user_id = os.getenv("SYNTH_USER_ID") or os.getenv("X_USER_ID") or os.getenv("USER_ID")
|
|
38
|
+
if user_id:
|
|
39
|
+
headers["X-User-ID"] = user_id
|
|
40
|
+
org_id = os.getenv("SYNTH_ORG_ID") or os.getenv("X_ORG_ID") or os.getenv("ORG_ID")
|
|
41
|
+
if org_id:
|
|
42
|
+
headers["X-Org-ID"] = org_id
|
|
35
43
|
self._session = aiohttp.ClientSession(headers=headers, timeout=self._timeout)
|
|
36
44
|
return self
|
|
37
45
|
|
|
@@ -52,8 +60,8 @@ class AsyncHttpClient:
|
|
|
52
60
|
self,
|
|
53
61
|
path: str,
|
|
54
62
|
*,
|
|
55
|
-
params:
|
|
56
|
-
headers:
|
|
63
|
+
params: dict[str, Any] | None = None,
|
|
64
|
+
headers: dict[str, str] | None = None,
|
|
57
65
|
) -> Any:
|
|
58
66
|
url = self._abs(path)
|
|
59
67
|
assert self._session is not None, "AsyncHttpClient must be used as an async context manager"
|
|
@@ -61,7 +69,7 @@ class AsyncHttpClient:
|
|
|
61
69
|
return await self._handle_response(resp, url)
|
|
62
70
|
|
|
63
71
|
async def post_json(
|
|
64
|
-
self, path: str, *, json:
|
|
72
|
+
self, path: str, *, json: dict[str, Any], headers: dict[str, str] | None = None
|
|
65
73
|
) -> Any:
|
|
66
74
|
url = self._abs(path)
|
|
67
75
|
assert self._session is not None, "AsyncHttpClient must be used as an async context manager"
|
|
@@ -72,9 +80,9 @@ class AsyncHttpClient:
|
|
|
72
80
|
self,
|
|
73
81
|
path: str,
|
|
74
82
|
*,
|
|
75
|
-
data:
|
|
76
|
-
files:
|
|
77
|
-
headers:
|
|
83
|
+
data: dict[str, Any],
|
|
84
|
+
files: dict[str, tuple[str, bytes, str | None]],
|
|
85
|
+
headers: dict[str, str] | None = None,
|
|
78
86
|
) -> Any:
|
|
79
87
|
url = self._abs(path)
|
|
80
88
|
assert self._session is not None, "AsyncHttpClient must be used as an async context manager"
|
|
@@ -91,7 +99,7 @@ class AsyncHttpClient:
|
|
|
91
99
|
async with self._session.post(url, data=form, headers=headers) as resp:
|
|
92
100
|
return await self._handle_response(resp, url)
|
|
93
101
|
|
|
94
|
-
async def delete(self, path: str, *, headers:
|
|
102
|
+
async def delete(self, path: str, *, headers: dict[str, str] | None = None) -> Any:
|
|
95
103
|
url = self._abs(path)
|
|
96
104
|
assert self._session is not None, "AsyncHttpClient must be used as an async context manager"
|
|
97
105
|
async with self._session.delete(url, headers=headers) as resp:
|
synth_ai/inference/client.py
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import Any
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from synth_ai.api.models.supported import (
|
|
6
|
+
UnsupportedModelError,
|
|
7
|
+
normalize_model_identifier,
|
|
8
|
+
)
|
|
4
9
|
|
|
5
10
|
from ..http import AsyncHttpClient
|
|
6
11
|
|
|
@@ -13,12 +18,17 @@ class InferenceClient:
|
|
|
13
18
|
|
|
14
19
|
async def create_chat_completion(
|
|
15
20
|
self, *, model: str, messages: list[dict], **kwargs: Any
|
|
16
|
-
) ->
|
|
17
|
-
|
|
21
|
+
) -> dict[str, Any]:
|
|
22
|
+
try:
|
|
23
|
+
normalized_model = normalize_model_identifier(model)
|
|
24
|
+
except UnsupportedModelError as exc:
|
|
25
|
+
raise ValueError(str(exc)) from exc
|
|
26
|
+
|
|
27
|
+
body: dict[str, Any] = {"model": normalized_model, "messages": messages}
|
|
18
28
|
body.update(kwargs)
|
|
19
29
|
# Backend now expects an explicit thinking_budget; provide a sensible default if omitted
|
|
20
30
|
if "thinking_budget" not in body:
|
|
21
31
|
body["thinking_budget"] = 256
|
|
22
32
|
async with AsyncHttpClient(self._base_url, self._api_key, timeout=self._timeout) as http:
|
|
23
|
-
#
|
|
24
|
-
return await http.post_json("/api/v1/chat/completions", json=body)
|
|
33
|
+
# Route through backend inference proxy to Modal
|
|
34
|
+
return await http.post_json("/api/inference/v1/chat/completions", json=body)
|