From bbd32e9e1eaa0138fa9f84fcf381f2fc160ec1d1 Mon Sep 17 00:00:00 2001 From: Andrey Cheptsov Date: Thu, 5 Mar 2026 12:48:06 +0100 Subject: [PATCH 1/2] Enable Vast community cloud by default and make it configurable. Set Vast to include community offers by default for broader discovery, keep explicit opt-out support, and point uv to the matching gpuhunt branch. Made-with: Cursor --- docs/docs/concepts/backends.md | 19 ++++++++ pyproject.toml | 1 + .../_internal/core/backends/vastai/compute.py | 1 + .../_internal/core/backends/vastai/models.py | 19 ++++++++ .../core/backends/vastai/test_compute.py | 47 +++++++++++++++++++ .../core/backends/vastai/test_configurator.py | 14 ++++++ 6 files changed, 101 insertions(+) create mode 100644 src/tests/_internal/core/backends/vastai/test_compute.py diff --git a/docs/docs/concepts/backends.md b/docs/docs/concepts/backends.md index 620d5723cb..dcb43387ed 100644 --- a/docs/docs/concepts/backends.md +++ b/docs/docs/concepts/backends.md @@ -1211,4 +1211,23 @@ projects: +??? info "Community Cloud" + By default, `dstack` includes both Server Cloud (datacenter) and Community Cloud offers. + To restrict offers to Server Cloud only, set `community_cloud: false` in the backend settings. + +
+ + ```yaml + projects: + - name: main + backends: + - type: vastai + creds: + type: api_key + api_key: d75789f22f1908e0527c78a283b523dd73051c8c7d05456516fc91e9d4efd8c5 + community_cloud: false + ``` + +
+ Also, the `vastai` backend supports on-demand instances only. Spot instance support coming soon. diff --git a/pyproject.toml b/pyproject.toml index 259cbf7b25..55df6aa3d8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -80,6 +80,7 @@ ignore-case = true [tool.uv.sources] dstack-plugin-server = { path = "examples/plugins/example_plugin_server", editable = true } +gpuhunt = { git = "https://github.com/dstackai/gpuhunt.git", branch = "feat/vastai-community-default" } [tool.ruff] target-version = "py39" diff --git a/src/dstack/_internal/core/backends/vastai/compute.py b/src/dstack/_internal/core/backends/vastai/compute.py index abd0ee5bb6..dae64c67c4 100644 --- a/src/dstack/_internal/core/backends/vastai/compute.py +++ b/src/dstack/_internal/core/backends/vastai/compute.py @@ -43,6 +43,7 @@ def __init__(self, config: VastAIConfig): self.catalog = gpuhunt.Catalog(balance_resources=False, auto_reload=False) self.catalog.add_provider( VastAIProvider( + community_cloud=config.allow_community_cloud, extra_filters={ "direct_port_count": {"gte": 1}, "reliability2": {"gte": 0.9}, diff --git a/src/dstack/_internal/core/backends/vastai/models.py b/src/dstack/_internal/core/backends/vastai/models.py index a9df8c9c5c..c712bce51b 100644 --- a/src/dstack/_internal/core/backends/vastai/models.py +++ b/src/dstack/_internal/core/backends/vastai/models.py @@ -4,6 +4,10 @@ from dstack._internal.core.models.common import CoreModel +# TODO: Re-evaluate this default once Vast Server Cloud inventory improves for +# CUDA-sensitive GPU families (e.g. H100 with strict cuda_max_good filtering). +VASTAI_COMMUNITY_CLOUD_DEFAULT = True + class VastAIAPIKeyCreds(CoreModel): type: Annotated[Literal["api_key"], Field(description="The type of credentials")] = "api_key" @@ -20,6 +24,15 @@ class VastAIBackendConfig(CoreModel): Optional[List[str]], Field(description="The list of VastAI regions. Omit to use all regions"), ] = None + community_cloud: Annotated[ + Optional[bool], + Field( + description=( + "Whether Community Cloud offers can be suggested in addition to Server Cloud." + f" Defaults to `{str(VASTAI_COMMUNITY_CLOUD_DEFAULT).lower()}`" + ) + ), + ] = None class VastAIBackendConfigWithCreds(VastAIBackendConfig): @@ -35,3 +48,9 @@ class VastAIStoredConfig(VastAIBackendConfig): class VastAIConfig(VastAIStoredConfig): creds: AnyVastAICreds + + @property + def allow_community_cloud(self) -> bool: + if self.community_cloud is not None: + return self.community_cloud + return VASTAI_COMMUNITY_CLOUD_DEFAULT diff --git a/src/tests/_internal/core/backends/vastai/test_compute.py b/src/tests/_internal/core/backends/vastai/test_compute.py new file mode 100644 index 0000000000..227f9ccfec --- /dev/null +++ b/src/tests/_internal/core/backends/vastai/test_compute.py @@ -0,0 +1,47 @@ +from unittest.mock import patch + +from dstack._internal.core.backends.vastai.compute import VastAICompute +from dstack._internal.core.backends.vastai.models import VastAIConfig, VastAICreds + + +def _config(community_cloud=None) -> VastAIConfig: + return VastAIConfig(creds=VastAICreds(api_key="test"), community_cloud=community_cloud) + + +def test_vastai_compute_enables_community_cloud_by_default(): + with patch( + "dstack._internal.core.backends.vastai.compute.VastAIProvider" + ) as vast_provider_cls, patch( + "dstack._internal.core.backends.vastai.compute.gpuhunt.Catalog" + ) as catalog_cls: + catalog_instance = catalog_cls.return_value + VastAICompute(_config()) + vast_provider_cls.assert_called_once() + assert vast_provider_cls.call_args.kwargs["community_cloud"] is True + catalog_instance.add_provider.assert_called_once() + + +def test_vastai_compute_can_enable_community_cloud(): + with patch( + "dstack._internal.core.backends.vastai.compute.VastAIProvider" + ) as vast_provider_cls, patch( + "dstack._internal.core.backends.vastai.compute.gpuhunt.Catalog" + ) as catalog_cls: + catalog_instance = catalog_cls.return_value + VastAICompute(_config(community_cloud=True)) + vast_provider_cls.assert_called_once() + assert vast_provider_cls.call_args.kwargs["community_cloud"] is True + catalog_instance.add_provider.assert_called_once() + + +def test_vastai_compute_can_disable_community_cloud(): + with patch( + "dstack._internal.core.backends.vastai.compute.VastAIProvider" + ) as vast_provider_cls, patch( + "dstack._internal.core.backends.vastai.compute.gpuhunt.Catalog" + ) as catalog_cls: + catalog_instance = catalog_cls.return_value + VastAICompute(_config(community_cloud=False)) + vast_provider_cls.assert_called_once() + assert vast_provider_cls.call_args.kwargs["community_cloud"] is False + catalog_instance.add_provider.assert_called_once() diff --git a/src/tests/_internal/core/backends/vastai/test_configurator.py b/src/tests/_internal/core/backends/vastai/test_configurator.py index 47c9a523ca..16c3c82edb 100644 --- a/src/tests/_internal/core/backends/vastai/test_configurator.py +++ b/src/tests/_internal/core/backends/vastai/test_configurator.py @@ -8,6 +8,20 @@ class TestVastAIConfigurator: + def test_allow_community_cloud_default(self): + config = VastAIBackendConfigWithCreds(creds=VastAICreds(api_key="valid")) + backend = VastAIConfigurator().create_backend(project_name="main", config=config) + loaded_config = VastAIConfigurator()._get_config(backend) + assert loaded_config.allow_community_cloud is True + + def test_allow_community_cloud_enabled(self): + config = VastAIBackendConfigWithCreds( + creds=VastAICreds(api_key="valid"), community_cloud=True + ) + backend = VastAIConfigurator().create_backend(project_name="main", config=config) + loaded_config = VastAIConfigurator()._get_config(backend) + assert loaded_config.allow_community_cloud is True + def test_validate_config_valid(self): config = VastAIBackendConfigWithCreds( creds=VastAICreds(api_key="valid"), From 43860279c4af38e013df8b0c49d5e45b17cbb275 Mon Sep 17 00:00:00 2001 From: Andrey Cheptsov Date: Thu, 5 Mar 2026 12:54:56 +0100 Subject: [PATCH 2/2] Fix CI lint failure in VastAI backend changes. Made-with: Cursor --- .../_internal/core/backends/vastai/compute.py | 2 +- .../core/backends/vastai/test_compute.py | 27 +++++++++---------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/dstack/_internal/core/backends/vastai/compute.py b/src/dstack/_internal/core/backends/vastai/compute.py index dae64c67c4..254da38e39 100644 --- a/src/dstack/_internal/core/backends/vastai/compute.py +++ b/src/dstack/_internal/core/backends/vastai/compute.py @@ -51,7 +51,7 @@ def __init__(self, config: VastAIConfig): "verified": {"eq": True}, "cuda_max_good": {"gte": 12.8}, "compute_cap": {"gte": 600}, - } + }, ) ) diff --git a/src/tests/_internal/core/backends/vastai/test_compute.py b/src/tests/_internal/core/backends/vastai/test_compute.py index 227f9ccfec..e6f5c0a5c8 100644 --- a/src/tests/_internal/core/backends/vastai/test_compute.py +++ b/src/tests/_internal/core/backends/vastai/test_compute.py @@ -9,11 +9,10 @@ def _config(community_cloud=None) -> VastAIConfig: def test_vastai_compute_enables_community_cloud_by_default(): - with patch( - "dstack._internal.core.backends.vastai.compute.VastAIProvider" - ) as vast_provider_cls, patch( - "dstack._internal.core.backends.vastai.compute.gpuhunt.Catalog" - ) as catalog_cls: + with ( + patch("dstack._internal.core.backends.vastai.compute.VastAIProvider") as vast_provider_cls, + patch("dstack._internal.core.backends.vastai.compute.gpuhunt.Catalog") as catalog_cls, + ): catalog_instance = catalog_cls.return_value VastAICompute(_config()) vast_provider_cls.assert_called_once() @@ -22,11 +21,10 @@ def test_vastai_compute_enables_community_cloud_by_default(): def test_vastai_compute_can_enable_community_cloud(): - with patch( - "dstack._internal.core.backends.vastai.compute.VastAIProvider" - ) as vast_provider_cls, patch( - "dstack._internal.core.backends.vastai.compute.gpuhunt.Catalog" - ) as catalog_cls: + with ( + patch("dstack._internal.core.backends.vastai.compute.VastAIProvider") as vast_provider_cls, + patch("dstack._internal.core.backends.vastai.compute.gpuhunt.Catalog") as catalog_cls, + ): catalog_instance = catalog_cls.return_value VastAICompute(_config(community_cloud=True)) vast_provider_cls.assert_called_once() @@ -35,11 +33,10 @@ def test_vastai_compute_can_enable_community_cloud(): def test_vastai_compute_can_disable_community_cloud(): - with patch( - "dstack._internal.core.backends.vastai.compute.VastAIProvider" - ) as vast_provider_cls, patch( - "dstack._internal.core.backends.vastai.compute.gpuhunt.Catalog" - ) as catalog_cls: + with ( + patch("dstack._internal.core.backends.vastai.compute.VastAIProvider") as vast_provider_cls, + patch("dstack._internal.core.backends.vastai.compute.gpuhunt.Catalog") as catalog_cls, + ): catalog_instance = catalog_cls.return_value VastAICompute(_config(community_cloud=False)) vast_provider_cls.assert_called_once()