dstack 0.19.26__py3-none-any.whl → 0.19.28__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 dstack might be problematic. Click here for more details.
- dstack/_internal/cli/commands/__init__.py +11 -8
- dstack/_internal/cli/commands/apply.py +6 -3
- dstack/_internal/cli/commands/completion.py +3 -1
- dstack/_internal/cli/commands/config.py +1 -0
- dstack/_internal/cli/commands/init.py +4 -4
- dstack/_internal/cli/commands/offer.py +1 -1
- dstack/_internal/cli/commands/project.py +1 -0
- dstack/_internal/cli/commands/server.py +2 -2
- dstack/_internal/cli/main.py +1 -1
- dstack/_internal/cli/services/configurators/base.py +2 -4
- dstack/_internal/cli/services/configurators/fleet.py +4 -5
- dstack/_internal/cli/services/configurators/gateway.py +3 -5
- dstack/_internal/cli/services/configurators/run.py +165 -43
- dstack/_internal/cli/services/configurators/volume.py +3 -5
- dstack/_internal/cli/services/repos.py +1 -18
- dstack/_internal/core/backends/amddevcloud/__init__.py +1 -0
- dstack/_internal/core/backends/amddevcloud/backend.py +16 -0
- dstack/_internal/core/backends/amddevcloud/compute.py +5 -0
- dstack/_internal/core/backends/amddevcloud/configurator.py +29 -0
- dstack/_internal/core/backends/aws/compute.py +6 -1
- dstack/_internal/core/backends/base/compute.py +33 -5
- dstack/_internal/core/backends/base/offers.py +2 -0
- dstack/_internal/core/backends/configurators.py +15 -0
- dstack/_internal/core/backends/digitalocean/__init__.py +1 -0
- dstack/_internal/core/backends/digitalocean/backend.py +16 -0
- dstack/_internal/core/backends/digitalocean/compute.py +5 -0
- dstack/_internal/core/backends/digitalocean/configurator.py +31 -0
- dstack/_internal/core/backends/digitalocean_base/__init__.py +1 -0
- dstack/_internal/core/backends/digitalocean_base/api_client.py +104 -0
- dstack/_internal/core/backends/digitalocean_base/backend.py +5 -0
- dstack/_internal/core/backends/digitalocean_base/compute.py +173 -0
- dstack/_internal/core/backends/digitalocean_base/configurator.py +57 -0
- dstack/_internal/core/backends/digitalocean_base/models.py +43 -0
- dstack/_internal/core/backends/gcp/compute.py +32 -8
- dstack/_internal/core/backends/hotaisle/api_client.py +25 -33
- dstack/_internal/core/backends/hotaisle/compute.py +1 -6
- dstack/_internal/core/backends/models.py +7 -0
- dstack/_internal/core/backends/nebius/compute.py +0 -7
- dstack/_internal/core/backends/oci/compute.py +4 -5
- dstack/_internal/core/backends/vultr/compute.py +1 -5
- dstack/_internal/core/compatibility/fleets.py +5 -0
- dstack/_internal/core/compatibility/runs.py +10 -1
- dstack/_internal/core/models/backends/base.py +5 -1
- dstack/_internal/core/models/common.py +67 -43
- dstack/_internal/core/models/configurations.py +109 -69
- dstack/_internal/core/models/files.py +1 -1
- dstack/_internal/core/models/fleets.py +115 -25
- dstack/_internal/core/models/instances.py +5 -5
- dstack/_internal/core/models/profiles.py +66 -47
- dstack/_internal/core/models/repos/remote.py +21 -16
- dstack/_internal/core/models/resources.py +69 -65
- dstack/_internal/core/models/runs.py +41 -14
- dstack/_internal/core/services/repos.py +85 -80
- dstack/_internal/server/app.py +5 -0
- dstack/_internal/server/background/tasks/process_fleets.py +117 -13
- dstack/_internal/server/background/tasks/process_instances.py +12 -71
- dstack/_internal/server/background/tasks/process_running_jobs.py +2 -0
- dstack/_internal/server/background/tasks/process_runs.py +2 -0
- dstack/_internal/server/background/tasks/process_submitted_jobs.py +48 -16
- dstack/_internal/server/migrations/versions/2498ab323443_add_fleetmodel_consolidation_attempt_.py +44 -0
- dstack/_internal/server/models.py +11 -7
- dstack/_internal/server/schemas/gateways.py +10 -9
- dstack/_internal/server/schemas/runner.py +1 -0
- dstack/_internal/server/services/backends/handlers.py +2 -0
- dstack/_internal/server/services/docker.py +8 -7
- dstack/_internal/server/services/fleets.py +23 -25
- dstack/_internal/server/services/instances.py +3 -3
- dstack/_internal/server/services/jobs/configurators/base.py +46 -6
- dstack/_internal/server/services/jobs/configurators/dev.py +4 -4
- dstack/_internal/server/services/jobs/configurators/extensions/cursor.py +3 -5
- dstack/_internal/server/services/jobs/configurators/extensions/vscode.py +4 -6
- dstack/_internal/server/services/jobs/configurators/service.py +0 -3
- dstack/_internal/server/services/jobs/configurators/task.py +0 -3
- dstack/_internal/server/services/projects.py +52 -1
- dstack/_internal/server/services/runs.py +16 -0
- dstack/_internal/server/settings.py +46 -0
- dstack/_internal/server/statics/index.html +1 -1
- dstack/_internal/server/statics/{main-aec4762350e34d6fbff9.css → main-5e0d56245c4bd241ec27.css} +1 -1
- dstack/_internal/server/statics/{main-d151b300fcac3933213d.js → main-a2a16772fbf11a14d191.js} +1215 -998
- dstack/_internal/server/statics/{main-d151b300fcac3933213d.js.map → main-a2a16772fbf11a14d191.js.map} +1 -1
- dstack/_internal/server/testing/common.py +6 -3
- dstack/_internal/utils/env.py +85 -11
- dstack/_internal/utils/path.py +8 -1
- dstack/_internal/utils/ssh.py +7 -0
- dstack/api/_public/repos.py +41 -6
- dstack/api/_public/runs.py +14 -1
- dstack/version.py +1 -1
- {dstack-0.19.26.dist-info → dstack-0.19.28.dist-info}/METADATA +2 -2
- {dstack-0.19.26.dist-info → dstack-0.19.28.dist-info}/RECORD +92 -78
- dstack/_internal/server/statics/static/media/github.1f7102513534c83a9d8d735d2b8c12a2.svg +0 -3
- {dstack-0.19.26.dist-info → dstack-0.19.28.dist-info}/WHEEL +0 -0
- {dstack-0.19.26.dist-info → dstack-0.19.28.dist-info}/entry_points.txt +0 -0
- {dstack-0.19.26.dist-info → dstack-0.19.28.dist-info}/licenses/LICENSE.md +0 -0
|
@@ -6,7 +6,12 @@ from pydantic import Field, root_validator, validator
|
|
|
6
6
|
from typing_extensions import Annotated, Literal
|
|
7
7
|
|
|
8
8
|
from dstack._internal.core.models.backends.base import BackendType
|
|
9
|
-
from dstack._internal.core.models.common import
|
|
9
|
+
from dstack._internal.core.models.common import (
|
|
10
|
+
CoreConfig,
|
|
11
|
+
CoreModel,
|
|
12
|
+
Duration,
|
|
13
|
+
generate_dual_core_model,
|
|
14
|
+
)
|
|
10
15
|
from dstack._internal.utils.common import list_enum_values_for_annotation
|
|
11
16
|
from dstack._internal.utils.cron import validate_cron
|
|
12
17
|
from dstack._internal.utils.json_schema import add_extra_schema_types
|
|
@@ -112,7 +117,16 @@ class RetryEvent(str, Enum):
|
|
|
112
117
|
ERROR = "error"
|
|
113
118
|
|
|
114
119
|
|
|
115
|
-
class
|
|
120
|
+
class ProfileRetryConfig(CoreConfig):
|
|
121
|
+
@staticmethod
|
|
122
|
+
def schema_extra(schema: Dict[str, Any]):
|
|
123
|
+
add_extra_schema_types(
|
|
124
|
+
schema["properties"]["duration"],
|
|
125
|
+
extra_types=[{"type": "string"}],
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class ProfileRetry(generate_dual_core_model(ProfileRetryConfig)):
|
|
116
130
|
on_events: Annotated[
|
|
117
131
|
Optional[List[RetryEvent]],
|
|
118
132
|
Field(
|
|
@@ -128,14 +142,6 @@ class ProfileRetry(CoreModel):
|
|
|
128
142
|
Field(description="The maximum period of retrying the run, e.g., `4h` or `1d`"),
|
|
129
143
|
] = None
|
|
130
144
|
|
|
131
|
-
class Config(CoreModel.Config):
|
|
132
|
-
@staticmethod
|
|
133
|
-
def schema_extra(schema: Dict[str, Any]):
|
|
134
|
-
add_extra_schema_types(
|
|
135
|
-
schema["properties"]["duration"],
|
|
136
|
-
extra_types=[{"type": "string"}],
|
|
137
|
-
)
|
|
138
|
-
|
|
139
145
|
_validate_duration = validator("duration", pre=True, allow_reuse=True)(parse_duration)
|
|
140
146
|
|
|
141
147
|
@root_validator
|
|
@@ -146,7 +152,16 @@ class ProfileRetry(CoreModel):
|
|
|
146
152
|
return values
|
|
147
153
|
|
|
148
154
|
|
|
149
|
-
class
|
|
155
|
+
class UtilizationPolicyConfig(CoreConfig):
|
|
156
|
+
@staticmethod
|
|
157
|
+
def schema_extra(schema: Dict[str, Any]):
|
|
158
|
+
add_extra_schema_types(
|
|
159
|
+
schema["properties"]["time_window"],
|
|
160
|
+
extra_types=[{"type": "string"}],
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
class UtilizationPolicy(generate_dual_core_model(UtilizationPolicyConfig)):
|
|
150
165
|
_min_time_window = "5m"
|
|
151
166
|
|
|
152
167
|
min_gpu_utilization: Annotated[
|
|
@@ -171,14 +186,6 @@ class UtilizationPolicy(CoreModel):
|
|
|
171
186
|
),
|
|
172
187
|
]
|
|
173
188
|
|
|
174
|
-
class Config(CoreModel.Config):
|
|
175
|
-
@staticmethod
|
|
176
|
-
def schema_extra(schema: Dict[str, Any]):
|
|
177
|
-
add_extra_schema_types(
|
|
178
|
-
schema["properties"]["time_window"],
|
|
179
|
-
extra_types=[{"type": "string"}],
|
|
180
|
-
)
|
|
181
|
-
|
|
182
189
|
@validator("time_window", pre=True)
|
|
183
190
|
def validate_time_window(cls, v: Union[int, str]) -> int:
|
|
184
191
|
v = parse_duration(v)
|
|
@@ -219,6 +226,28 @@ class Schedule(CoreModel):
|
|
|
219
226
|
return self.cron
|
|
220
227
|
|
|
221
228
|
|
|
229
|
+
class ProfileParamsConfig(CoreConfig):
|
|
230
|
+
@staticmethod
|
|
231
|
+
def schema_extra(schema: Dict[str, Any]):
|
|
232
|
+
del schema["properties"]["pool_name"]
|
|
233
|
+
del schema["properties"]["instance_name"]
|
|
234
|
+
del schema["properties"]["retry_policy"]
|
|
235
|
+
del schema["properties"]["termination_policy"]
|
|
236
|
+
del schema["properties"]["termination_idle_time"]
|
|
237
|
+
add_extra_schema_types(
|
|
238
|
+
schema["properties"]["max_duration"],
|
|
239
|
+
extra_types=[{"type": "boolean"}, {"type": "string"}],
|
|
240
|
+
)
|
|
241
|
+
add_extra_schema_types(
|
|
242
|
+
schema["properties"]["stop_duration"],
|
|
243
|
+
extra_types=[{"type": "boolean"}, {"type": "string"}],
|
|
244
|
+
)
|
|
245
|
+
add_extra_schema_types(
|
|
246
|
+
schema["properties"]["idle_duration"],
|
|
247
|
+
extra_types=[{"type": "string"}],
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
|
|
222
251
|
class ProfileParams(CoreModel):
|
|
223
252
|
backends: Annotated[
|
|
224
253
|
Optional[List[BackendType]],
|
|
@@ -358,27 +387,6 @@ class ProfileParams(CoreModel):
|
|
|
358
387
|
termination_policy: Annotated[Optional[TerminationPolicy], Field(exclude=True)] = None
|
|
359
388
|
termination_idle_time: Annotated[Optional[Union[str, int]], Field(exclude=True)] = None
|
|
360
389
|
|
|
361
|
-
class Config(CoreModel.Config):
|
|
362
|
-
@staticmethod
|
|
363
|
-
def schema_extra(schema: Dict[str, Any]) -> None:
|
|
364
|
-
del schema["properties"]["pool_name"]
|
|
365
|
-
del schema["properties"]["instance_name"]
|
|
366
|
-
del schema["properties"]["retry_policy"]
|
|
367
|
-
del schema["properties"]["termination_policy"]
|
|
368
|
-
del schema["properties"]["termination_idle_time"]
|
|
369
|
-
add_extra_schema_types(
|
|
370
|
-
schema["properties"]["max_duration"],
|
|
371
|
-
extra_types=[{"type": "boolean"}, {"type": "string"}],
|
|
372
|
-
)
|
|
373
|
-
add_extra_schema_types(
|
|
374
|
-
schema["properties"]["stop_duration"],
|
|
375
|
-
extra_types=[{"type": "boolean"}, {"type": "string"}],
|
|
376
|
-
)
|
|
377
|
-
add_extra_schema_types(
|
|
378
|
-
schema["properties"]["idle_duration"],
|
|
379
|
-
extra_types=[{"type": "string"}],
|
|
380
|
-
)
|
|
381
|
-
|
|
382
390
|
_validate_max_duration = validator("max_duration", pre=True, allow_reuse=True)(
|
|
383
391
|
parse_max_duration
|
|
384
392
|
)
|
|
@@ -403,17 +411,28 @@ class ProfileProps(CoreModel):
|
|
|
403
411
|
] = False
|
|
404
412
|
|
|
405
413
|
|
|
406
|
-
class
|
|
414
|
+
class ProfileConfig(ProfileParamsConfig):
|
|
415
|
+
@staticmethod
|
|
416
|
+
def schema_extra(schema: Dict[str, Any]):
|
|
417
|
+
ProfileParamsConfig.schema_extra(schema)
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
class Profile(
|
|
421
|
+
ProfileProps,
|
|
422
|
+
ProfileParams,
|
|
423
|
+
generate_dual_core_model(ProfileConfig),
|
|
424
|
+
):
|
|
407
425
|
pass
|
|
408
426
|
|
|
409
427
|
|
|
410
|
-
class
|
|
411
|
-
|
|
428
|
+
class ProfilesConfigConfig(CoreConfig):
|
|
429
|
+
json_loads = orjson.loads
|
|
430
|
+
json_dumps = pydantic_orjson_dumps_with_indent
|
|
431
|
+
schema_extra = {"$schema": "http://json-schema.org/draft-07/schema#"}
|
|
412
432
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
schema_extra = {"$schema": "http://json-schema.org/draft-07/schema#"}
|
|
433
|
+
|
|
434
|
+
class ProfilesConfig(generate_dual_core_model(ProfilesConfigConfig)):
|
|
435
|
+
profiles: List[Profile]
|
|
417
436
|
|
|
418
437
|
def default(self) -> Optional[Profile]:
|
|
419
438
|
for p in self.profiles:
|
|
@@ -11,7 +11,7 @@ from pydantic import Field
|
|
|
11
11
|
from typing_extensions import Literal
|
|
12
12
|
|
|
13
13
|
from dstack._internal.core.errors import DstackError
|
|
14
|
-
from dstack._internal.core.models.common import
|
|
14
|
+
from dstack._internal.core.models.common import CoreConfig, generate_dual_core_model
|
|
15
15
|
from dstack._internal.core.models.repos.base import BaseRepoInfo, Repo
|
|
16
16
|
from dstack._internal.utils.hash import get_sha256, slugify
|
|
17
17
|
from dstack._internal.utils.path import PathLike
|
|
@@ -24,21 +24,33 @@ class RepoError(DstackError):
|
|
|
24
24
|
pass
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
class
|
|
27
|
+
class RemoteRepoCredsConfig(CoreConfig):
|
|
28
|
+
@staticmethod
|
|
29
|
+
def schema_extra(schema: Dict[str, Any]):
|
|
30
|
+
del schema["properties"]["protocol"]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class RemoteRepoCreds(generate_dual_core_model(RemoteRepoCredsConfig)):
|
|
28
34
|
clone_url: str
|
|
29
|
-
private_key: Optional[str]
|
|
30
|
-
oauth_token: Optional[str]
|
|
35
|
+
private_key: Optional[str] = None
|
|
36
|
+
oauth_token: Optional[str] = None
|
|
31
37
|
|
|
32
38
|
# TODO: remove in 0.20. Left for compatibility with CLI <=0.18.44
|
|
33
39
|
protocol: Annotated[Optional[str], Field(exclude=True)] = None
|
|
34
40
|
|
|
35
|
-
class Config(CoreModel.Config):
|
|
36
|
-
@staticmethod
|
|
37
|
-
def schema_extra(schema: Dict[str, Any]) -> None:
|
|
38
|
-
del schema["properties"]["protocol"]
|
|
39
41
|
|
|
42
|
+
class RemoteRepoInfoConfig(CoreConfig):
|
|
43
|
+
@staticmethod
|
|
44
|
+
def schema_extra(schema: Dict[str, Any]):
|
|
45
|
+
del schema["properties"]["repo_host_name"]
|
|
46
|
+
del schema["properties"]["repo_port"]
|
|
47
|
+
del schema["properties"]["repo_user_name"]
|
|
40
48
|
|
|
41
|
-
|
|
49
|
+
|
|
50
|
+
class RemoteRepoInfo(
|
|
51
|
+
BaseRepoInfo,
|
|
52
|
+
generate_dual_core_model(RemoteRepoInfoConfig),
|
|
53
|
+
):
|
|
42
54
|
repo_type: Literal["remote"] = "remote"
|
|
43
55
|
repo_name: str
|
|
44
56
|
|
|
@@ -47,13 +59,6 @@ class RemoteRepoInfo(BaseRepoInfo):
|
|
|
47
59
|
repo_port: Annotated[Optional[int], Field(exclude=True)] = None
|
|
48
60
|
repo_user_name: Annotated[Optional[str], Field(exclude=True)] = None
|
|
49
61
|
|
|
50
|
-
class Config(BaseRepoInfo.Config):
|
|
51
|
-
@staticmethod
|
|
52
|
-
def schema_extra(schema: Dict[str, Any]) -> None:
|
|
53
|
-
del schema["properties"]["repo_host_name"]
|
|
54
|
-
del schema["properties"]["repo_port"]
|
|
55
|
-
del schema["properties"]["repo_user_name"]
|
|
56
|
-
|
|
57
62
|
|
|
58
63
|
class RemoteRunRepoData(RemoteRepoInfo):
|
|
59
64
|
repo_branch: Optional[str] = None
|
|
@@ -7,7 +7,7 @@ from pydantic import Field, parse_obj_as, root_validator, validator
|
|
|
7
7
|
from pydantic.generics import GenericModel
|
|
8
8
|
from typing_extensions import Annotated
|
|
9
9
|
|
|
10
|
-
from dstack._internal.core.models.common import CoreModel
|
|
10
|
+
from dstack._internal.core.models.common import CoreConfig, CoreModel, generate_dual_core_model
|
|
11
11
|
from dstack._internal.utils.common import pretty_resources
|
|
12
12
|
from dstack._internal.utils.json_schema import add_extra_schema_types
|
|
13
13
|
from dstack._internal.utils.logging import get_logger
|
|
@@ -129,21 +129,22 @@ DEFAULT_MEMORY_SIZE = Range[Memory](min=Memory.parse("8GB"))
|
|
|
129
129
|
DEFAULT_GPU_COUNT = Range[int](min=1)
|
|
130
130
|
|
|
131
131
|
|
|
132
|
-
class
|
|
132
|
+
class CPUSpecConfig(CoreConfig):
|
|
133
|
+
@staticmethod
|
|
134
|
+
def schema_extra(schema: Dict[str, Any]):
|
|
135
|
+
add_extra_schema_types(
|
|
136
|
+
schema["properties"]["count"],
|
|
137
|
+
extra_types=[{"type": "integer"}, {"type": "string"}],
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class CPUSpec(generate_dual_core_model(CPUSpecConfig)):
|
|
133
142
|
arch: Annotated[
|
|
134
143
|
Optional[gpuhunt.CPUArchitecture],
|
|
135
144
|
Field(description="The CPU architecture, one of: `x86`, `arm`"),
|
|
136
145
|
] = None
|
|
137
146
|
count: Annotated[Range[int], Field(description="The number of CPU cores")] = DEFAULT_CPU_COUNT
|
|
138
147
|
|
|
139
|
-
class Config(CoreModel.Config):
|
|
140
|
-
@staticmethod
|
|
141
|
-
def schema_extra(schema: Dict[str, Any]):
|
|
142
|
-
add_extra_schema_types(
|
|
143
|
-
schema["properties"]["count"],
|
|
144
|
-
extra_types=[{"type": "integer"}, {"type": "string"}],
|
|
145
|
-
)
|
|
146
|
-
|
|
147
148
|
@classmethod
|
|
148
149
|
def __get_validators__(cls):
|
|
149
150
|
yield cls.parse
|
|
@@ -190,7 +191,28 @@ class CPUSpec(CoreModel):
|
|
|
190
191
|
return v
|
|
191
192
|
|
|
192
193
|
|
|
193
|
-
class
|
|
194
|
+
class GPUSpecConfig(CoreConfig):
|
|
195
|
+
@staticmethod
|
|
196
|
+
def schema_extra(schema: Dict[str, Any]):
|
|
197
|
+
add_extra_schema_types(
|
|
198
|
+
schema["properties"]["count"],
|
|
199
|
+
extra_types=[{"type": "integer"}, {"type": "string"}],
|
|
200
|
+
)
|
|
201
|
+
add_extra_schema_types(
|
|
202
|
+
schema["properties"]["name"],
|
|
203
|
+
extra_types=[{"type": "string"}],
|
|
204
|
+
)
|
|
205
|
+
add_extra_schema_types(
|
|
206
|
+
schema["properties"]["memory"],
|
|
207
|
+
extra_types=[{"type": "integer"}, {"type": "string"}],
|
|
208
|
+
)
|
|
209
|
+
add_extra_schema_types(
|
|
210
|
+
schema["properties"]["total_memory"],
|
|
211
|
+
extra_types=[{"type": "integer"}, {"type": "string"}],
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
class GPUSpec(generate_dual_core_model(GPUSpecConfig)):
|
|
194
216
|
vendor: Annotated[
|
|
195
217
|
Optional[gpuhunt.AcceleratorVendor],
|
|
196
218
|
Field(
|
|
@@ -218,26 +240,6 @@ class GPUSpec(CoreModel):
|
|
|
218
240
|
Field(description="The minimum compute capability of the GPU (e.g., `7.5`)"),
|
|
219
241
|
] = None
|
|
220
242
|
|
|
221
|
-
class Config(CoreModel.Config):
|
|
222
|
-
@staticmethod
|
|
223
|
-
def schema_extra(schema: Dict[str, Any]):
|
|
224
|
-
add_extra_schema_types(
|
|
225
|
-
schema["properties"]["count"],
|
|
226
|
-
extra_types=[{"type": "integer"}, {"type": "string"}],
|
|
227
|
-
)
|
|
228
|
-
add_extra_schema_types(
|
|
229
|
-
schema["properties"]["name"],
|
|
230
|
-
extra_types=[{"type": "string"}],
|
|
231
|
-
)
|
|
232
|
-
add_extra_schema_types(
|
|
233
|
-
schema["properties"]["memory"],
|
|
234
|
-
extra_types=[{"type": "integer"}, {"type": "string"}],
|
|
235
|
-
)
|
|
236
|
-
add_extra_schema_types(
|
|
237
|
-
schema["properties"]["total_memory"],
|
|
238
|
-
extra_types=[{"type": "integer"}, {"type": "string"}],
|
|
239
|
-
)
|
|
240
|
-
|
|
241
243
|
@classmethod
|
|
242
244
|
def __get_validators__(cls):
|
|
243
245
|
yield cls.parse
|
|
@@ -317,16 +319,17 @@ class GPUSpec(CoreModel):
|
|
|
317
319
|
return gpuhunt.AcceleratorVendor.cast(v)
|
|
318
320
|
|
|
319
321
|
|
|
320
|
-
class
|
|
321
|
-
|
|
322
|
+
class DiskSpecConfig(CoreConfig):
|
|
323
|
+
@staticmethod
|
|
324
|
+
def schema_extra(schema: Dict[str, Any]):
|
|
325
|
+
add_extra_schema_types(
|
|
326
|
+
schema["properties"]["size"],
|
|
327
|
+
extra_types=[{"type": "integer"}, {"type": "string"}],
|
|
328
|
+
)
|
|
322
329
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
add_extra_schema_types(
|
|
327
|
-
schema["properties"]["size"],
|
|
328
|
-
extra_types=[{"type": "integer"}, {"type": "string"}],
|
|
329
|
-
)
|
|
330
|
+
|
|
331
|
+
class DiskSpec(generate_dual_core_model(DiskSpecConfig)):
|
|
332
|
+
size: Annotated[Range[Memory], Field(description="Disk size")]
|
|
330
333
|
|
|
331
334
|
@classmethod
|
|
332
335
|
def __get_validators__(cls):
|
|
@@ -343,7 +346,32 @@ class DiskSpec(CoreModel):
|
|
|
343
346
|
DEFAULT_DISK = DiskSpec(size=Range[Memory](min=Memory.parse("100GB"), max=None))
|
|
344
347
|
|
|
345
348
|
|
|
346
|
-
class
|
|
349
|
+
class ResourcesSpecConfig(CoreConfig):
|
|
350
|
+
@staticmethod
|
|
351
|
+
def schema_extra(schema: Dict[str, Any]):
|
|
352
|
+
add_extra_schema_types(
|
|
353
|
+
schema["properties"]["cpu"],
|
|
354
|
+
extra_types=[{"type": "integer"}, {"type": "string"}],
|
|
355
|
+
)
|
|
356
|
+
add_extra_schema_types(
|
|
357
|
+
schema["properties"]["memory"],
|
|
358
|
+
extra_types=[{"type": "integer"}, {"type": "string"}],
|
|
359
|
+
)
|
|
360
|
+
add_extra_schema_types(
|
|
361
|
+
schema["properties"]["shm_size"],
|
|
362
|
+
extra_types=[{"type": "integer"}, {"type": "string"}],
|
|
363
|
+
)
|
|
364
|
+
add_extra_schema_types(
|
|
365
|
+
schema["properties"]["gpu"],
|
|
366
|
+
extra_types=[{"type": "integer"}, {"type": "string"}],
|
|
367
|
+
)
|
|
368
|
+
add_extra_schema_types(
|
|
369
|
+
schema["properties"]["disk"],
|
|
370
|
+
extra_types=[{"type": "integer"}, {"type": "string"}],
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
class ResourcesSpec(generate_dual_core_model(ResourcesSpecConfig)):
|
|
347
375
|
# TODO: Remove Range[int] in 0.20. Range[int] for backward compatibility only.
|
|
348
376
|
cpu: Annotated[Union[CPUSpec, Range[int]], Field(description="The CPU requirements")] = (
|
|
349
377
|
CPUSpec()
|
|
@@ -362,30 +390,6 @@ class ResourcesSpec(CoreModel):
|
|
|
362
390
|
gpu: Annotated[Optional[GPUSpec], Field(description="The GPU requirements")] = None
|
|
363
391
|
disk: Annotated[Optional[DiskSpec], Field(description="The disk resources")] = DEFAULT_DISK
|
|
364
392
|
|
|
365
|
-
class Config(CoreModel.Config):
|
|
366
|
-
@staticmethod
|
|
367
|
-
def schema_extra(schema: Dict[str, Any]):
|
|
368
|
-
add_extra_schema_types(
|
|
369
|
-
schema["properties"]["cpu"],
|
|
370
|
-
extra_types=[{"type": "integer"}, {"type": "string"}],
|
|
371
|
-
)
|
|
372
|
-
add_extra_schema_types(
|
|
373
|
-
schema["properties"]["memory"],
|
|
374
|
-
extra_types=[{"type": "integer"}, {"type": "string"}],
|
|
375
|
-
)
|
|
376
|
-
add_extra_schema_types(
|
|
377
|
-
schema["properties"]["shm_size"],
|
|
378
|
-
extra_types=[{"type": "integer"}, {"type": "string"}],
|
|
379
|
-
)
|
|
380
|
-
add_extra_schema_types(
|
|
381
|
-
schema["properties"]["gpu"],
|
|
382
|
-
extra_types=[{"type": "integer"}, {"type": "string"}],
|
|
383
|
-
)
|
|
384
|
-
add_extra_schema_types(
|
|
385
|
-
schema["properties"]["disk"],
|
|
386
|
-
extra_types=[{"type": "integer"}, {"type": "string"}],
|
|
387
|
-
)
|
|
388
|
-
|
|
389
393
|
def pretty_format(self) -> str:
|
|
390
394
|
# TODO: Remove in 0.20. Use self.cpu directly
|
|
391
395
|
cpu = parse_obj_as(CPUSpec, self.cpu)
|
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
from datetime import datetime, timedelta
|
|
2
2
|
from enum import Enum
|
|
3
|
-
from typing import Any, Dict, List, Literal, Optional
|
|
3
|
+
from typing import Any, Dict, List, Literal, Optional
|
|
4
4
|
from urllib.parse import urlparse
|
|
5
5
|
|
|
6
6
|
from pydantic import UUID4, Field, root_validator
|
|
7
7
|
from typing_extensions import Annotated
|
|
8
8
|
|
|
9
9
|
from dstack._internal.core.models.backends.base import BackendType
|
|
10
|
-
from dstack._internal.core.models.common import
|
|
10
|
+
from dstack._internal.core.models.common import (
|
|
11
|
+
ApplyAction,
|
|
12
|
+
CoreConfig,
|
|
13
|
+
CoreModel,
|
|
14
|
+
NetworkMode,
|
|
15
|
+
RegistryAuth,
|
|
16
|
+
generate_dual_core_model,
|
|
17
|
+
)
|
|
11
18
|
from dstack._internal.core.models.configurations import (
|
|
12
19
|
DEFAULT_PROBE_METHOD,
|
|
13
|
-
|
|
20
|
+
LEGACY_REPO_DIR,
|
|
14
21
|
AnyRunConfiguration,
|
|
15
22
|
HTTPHeaderSpec,
|
|
16
23
|
HTTPMethod,
|
|
@@ -259,6 +266,7 @@ class JobSpec(CoreModel):
|
|
|
259
266
|
retry: Optional[Retry]
|
|
260
267
|
volumes: Optional[List[MountPoint]] = None
|
|
261
268
|
ssh_key: Optional[JobSSHKey] = None
|
|
269
|
+
# `working_dir` is always absolute (if not None) since 0.19.27
|
|
262
270
|
working_dir: Optional[str]
|
|
263
271
|
# `repo_data` is optional for client compatibility with pre-0.19.17 servers and for compatibility
|
|
264
272
|
# with jobs submitted before 0.19.17. All new jobs are expected to have non-None `repo_data`.
|
|
@@ -268,6 +276,8 @@ class JobSpec(CoreModel):
|
|
|
268
276
|
# submitted before 0.19.17. See `_get_repo_code_hash` on how to get the correct `repo_code_hash`
|
|
269
277
|
# TODO: drop this comment when supporting jobs submitted before 0.19.17 is no longer relevant.
|
|
270
278
|
repo_code_hash: Optional[str] = None
|
|
279
|
+
# `repo_dir` was added in 0.19.27. Default value is set for backward compatibility
|
|
280
|
+
repo_dir: str = LEGACY_REPO_DIR
|
|
271
281
|
file_archives: list[FileArchiveMapping] = []
|
|
272
282
|
# None for non-services and pre-0.19.19 services. See `get_service_port`
|
|
273
283
|
service_port: Optional[int] = None
|
|
@@ -382,7 +392,14 @@ class Job(CoreModel):
|
|
|
382
392
|
job_submissions: List[JobSubmission]
|
|
383
393
|
|
|
384
394
|
|
|
385
|
-
class
|
|
395
|
+
class RunSpecConfig(CoreConfig):
|
|
396
|
+
@staticmethod
|
|
397
|
+
def schema_extra(schema: Dict[str, Any]):
|
|
398
|
+
prop = schema.get("properties", {})
|
|
399
|
+
prop.pop("merged_profile", None)
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
class RunSpec(generate_dual_core_model(RunSpecConfig)):
|
|
386
403
|
# TODO: run_name, working_dir are redundant here since they already passed in configuration
|
|
387
404
|
run_name: Annotated[
|
|
388
405
|
Optional[str],
|
|
@@ -409,17 +426,27 @@ class RunSpec(CoreModel):
|
|
|
409
426
|
Optional[str],
|
|
410
427
|
Field(description="The hash of the repo diff. Can be omitted if there is no repo diff."),
|
|
411
428
|
] = None
|
|
429
|
+
repo_dir: Annotated[
|
|
430
|
+
Optional[str],
|
|
431
|
+
Field(
|
|
432
|
+
description=(
|
|
433
|
+
"The repo path inside the container. Relative paths are resolved"
|
|
434
|
+
f" relative to the working directory. Defaults to `{LEGACY_REPO_DIR}`."
|
|
435
|
+
)
|
|
436
|
+
),
|
|
437
|
+
] = None
|
|
412
438
|
file_archives: Annotated[
|
|
413
439
|
list[FileArchiveMapping],
|
|
414
|
-
Field(description="The list of file archive ID to container path mappings"),
|
|
440
|
+
Field(description="The list of file archive ID to container path mappings."),
|
|
415
441
|
] = []
|
|
442
|
+
# Server uses configuration.working_dir instead of this field since 0.19.27, but
|
|
443
|
+
# the field still exists for compatibility with older servers
|
|
416
444
|
working_dir: Annotated[
|
|
417
445
|
Optional[str],
|
|
418
446
|
Field(
|
|
419
447
|
description=(
|
|
420
|
-
"The path to the working directory inside the container."
|
|
421
|
-
|
|
422
|
-
' Defaults to `"."`.'
|
|
448
|
+
"The absolute path to the working directory inside the container."
|
|
449
|
+
" Defaults to the default working directory from the `image`."
|
|
423
450
|
)
|
|
424
451
|
),
|
|
425
452
|
] = None
|
|
@@ -445,12 +472,6 @@ class RunSpec(CoreModel):
|
|
|
445
472
|
# TODO: make merged_profile a computed field after migrating to pydanticV2
|
|
446
473
|
merged_profile: Annotated[Profile, Field(exclude=True)] = None
|
|
447
474
|
|
|
448
|
-
class Config(CoreModel.Config):
|
|
449
|
-
@staticmethod
|
|
450
|
-
def schema_extra(schema: Dict[str, Any], model: Type) -> None:
|
|
451
|
-
prop = schema.get("properties", {})
|
|
452
|
-
prop.pop("merged_profile", None)
|
|
453
|
-
|
|
454
475
|
@root_validator
|
|
455
476
|
def _merged_profile(cls, values) -> Dict:
|
|
456
477
|
if values.get("profile") is None:
|
|
@@ -506,10 +527,16 @@ class RunStatus(str, Enum):
|
|
|
506
527
|
return self in self.finished_statuses()
|
|
507
528
|
|
|
508
529
|
|
|
530
|
+
class RunFleet(CoreModel):
|
|
531
|
+
id: UUID4
|
|
532
|
+
name: str
|
|
533
|
+
|
|
534
|
+
|
|
509
535
|
class Run(CoreModel):
|
|
510
536
|
id: UUID4
|
|
511
537
|
project_name: str
|
|
512
538
|
user: str
|
|
539
|
+
fleet: Optional[RunFleet] = None
|
|
513
540
|
submitted_at: datetime
|
|
514
541
|
last_processed_at: datetime
|
|
515
542
|
status: RunStatus
|