From 3e1f0887fe977dfa4a264605e1184e136c186c88 Mon Sep 17 00:00:00 2001 From: "kevin.li" Date: Wed, 30 Sep 2026 05:41:15 +0000 Subject: [PATCH] Leave GPU region unpinned by default Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- docs/deployment-configs.md | 4 ++-- src/spindle/configuration.py | 2 +- src/spindle/providers/modal/deployment_apps.py | 9 +++++++-- src/spindle/providers/modal/scoped.py | 2 -- tests/providers/test_deployment_apps.py | 5 +++-- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/docs/deployment-configs.md b/docs/deployment-configs.md index ec30706..e169086 100644 --- a/docs/deployment-configs.md +++ b/docs/deployment-configs.md @@ -110,12 +110,12 @@ Validation loads the recipes and checks that they can share one frontend. Backen The checked-in [deploy_models.sh](../scripts/deploy_models.sh) lists the complete active config set. Add a config path there, then run it. The deployment command owns frontend selection and trainer/inference updates: ~~~bash -./scripts/deploy_models.sh --app my-spindle --env dev --region us-west +./scripts/deploy_models.sh --app my-spindle --env dev ./scripts/deploy_models.sh --refresh-trainer qwen35-9b-lora-16k ./scripts/deploy_models.sh --refresh-inference qwen35-9b-lora-16k ~~~ -Frontend, region, environment, secret names, and volume names default in `BaseConfig.platform`. Set them in the same Python config using dotted overrides: +Frontend, region, environment, secret names, and volume names default in `BaseConfig.platform`. `platform.modal.region` defaults to `None`, which leaves GPU placement unpinned (Modal schedules trainers and inference pools in any region); set it only to restrict placement. Set them in the same Python config using dotted overrides: ```python from spindle.configs.gpt_oss_20b_lora_64k import Config as Parent diff --git a/src/spindle/configuration.py b/src/spindle/configuration.py index c7fd15c..338dbbf 100644 --- a/src/spindle/configuration.py +++ b/src/spindle/configuration.py @@ -15,7 +15,7 @@ def _apply_overrides(values, overrides): class BaseConfig: platform = { "frontend": "spindle", - "modal": {"environment": None, "region": "us-west"}, + "modal": {"environment": None, "region": None}, "secrets": { "api": "spindle-api", "sampler_proxy": "spindle-proxy", diff --git a/src/spindle/providers/modal/deployment_apps.py b/src/spindle/providers/modal/deployment_apps.py index ae803dd..ea7d0b2 100644 --- a/src/spindle/providers/modal/deployment_apps.py +++ b/src/spindle/providers/modal/deployment_apps.py @@ -82,6 +82,12 @@ def secrets_for(platform, *, training=False): return result +def server_placement(region): + if region is None: + return {} + return {"routing_region": region, "compute_region": region} + + def deployment_env(values): """Keep user environment overrides separate from Spindle's deployment wiring.""" if any(key.startswith("SPINDLE_") for key in values): @@ -224,8 +230,7 @@ def build_rollout_app(deployment, pool, platform=None, *, image=None): startup_timeout=recipe.inference_startup_timeout_s, exit_grace_period=300, port=8000, - routing_region=platform["modal"]["region"], - compute_region=platform["modal"]["region"], + **server_placement(platform["modal"]["region"]), ) class Server: @modal.enter() diff --git a/src/spindle/providers/modal/scoped.py b/src/spindle/providers/modal/scoped.py index fc113f0..66b5c9c 100644 --- a/src/spindle/providers/modal/scoped.py +++ b/src/spindle/providers/modal/scoped.py @@ -93,7 +93,6 @@ def register_sampler( startup_timeout=1800, exit_grace_period=120, port=8000, - routing_region="us-west", ) class Sampler: sidecar = None @@ -417,7 +416,6 @@ async def gateway(): timeout=1200, secrets=[*telemetry_secrets, api_secret], volumes={"/checkpoints": checkpoints}, - routing_region="us-west", ) @modal.concurrent(max_inputs=128) @modal.asgi_app(requires_proxy_auth=False) diff --git a/tests/providers/test_deployment_apps.py b/tests/providers/test_deployment_apps.py index a178a60..3368e8e 100644 --- a/tests/providers/test_deployment_apps.py +++ b/tests/providers/test_deployment_apps.py @@ -73,7 +73,7 @@ def test_trainer_declaration_and_executor_configuration( app, trainer = deployment_apps.build_trainer_app(row, platform, image=image) declaration, _ = app.functions["trainer"] assert declaration["gpu"] == "H100:4" - assert declaration["region"] == "us-west" + assert declaration["region"] is None assert declaration["max_containers"] is None assert declaration["single_use_containers"] is True assert declaration["image"] is image @@ -127,7 +127,8 @@ def test_pool_starts_native_server_and_correct_sidecar(builders, monkeypatch, ki ) assert settings["min_containers"] == 0 assert settings["target_concurrency"] == 16 - assert settings["compute_region"] == "us-west" + assert "compute_region" not in settings + assert "routing_region" not in settings calls, commands, stops = [], [], [] process = object()