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
|
@@ -28,6 +28,7 @@ from dstack._internal.core.models.configurations import (
|
|
|
28
28
|
from dstack._internal.core.models.envs import Env
|
|
29
29
|
from dstack._internal.core.models.fleets import (
|
|
30
30
|
FleetConfiguration,
|
|
31
|
+
FleetNodesSpec,
|
|
31
32
|
FleetSpec,
|
|
32
33
|
FleetStatus,
|
|
33
34
|
InstanceGroupPlacement,
|
|
@@ -60,7 +61,7 @@ from dstack._internal.core.models.profiles import (
|
|
|
60
61
|
)
|
|
61
62
|
from dstack._internal.core.models.repos.base import RepoType
|
|
62
63
|
from dstack._internal.core.models.repos.local import LocalRunRepoData
|
|
63
|
-
from dstack._internal.core.models.resources import CPUSpec, Memory,
|
|
64
|
+
from dstack._internal.core.models.resources import CPUSpec, Memory, ResourcesSpec
|
|
64
65
|
from dstack._internal.core.models.runs import (
|
|
65
66
|
JobProvisioningData,
|
|
66
67
|
JobRuntimeData,
|
|
@@ -271,7 +272,7 @@ def get_run_spec(
|
|
|
271
272
|
repo_id=repo_id,
|
|
272
273
|
repo_data=LocalRunRepoData(repo_dir="/"),
|
|
273
274
|
repo_code_hash=None,
|
|
274
|
-
working_dir=
|
|
275
|
+
working_dir=None,
|
|
275
276
|
configuration_path=configuration_path,
|
|
276
277
|
configuration=configuration or DevEnvironmentConfiguration(ide="vscode"),
|
|
277
278
|
profile=profile,
|
|
@@ -284,6 +285,7 @@ async def create_run(
|
|
|
284
285
|
project: ProjectModel,
|
|
285
286
|
repo: RepoModel,
|
|
286
287
|
user: UserModel,
|
|
288
|
+
fleet: Optional[FleetModel] = None,
|
|
287
289
|
run_name: str = "test-run",
|
|
288
290
|
status: RunStatus = RunStatus.SUBMITTED,
|
|
289
291
|
termination_reason: Optional[RunTerminationReason] = None,
|
|
@@ -309,6 +311,7 @@ async def create_run(
|
|
|
309
311
|
project_id=project.id,
|
|
310
312
|
repo_id=repo.id,
|
|
311
313
|
user_id=user.id,
|
|
314
|
+
fleet_id=fleet.id if fleet else None,
|
|
312
315
|
submitted_at=submitted_at,
|
|
313
316
|
run_name=run_name,
|
|
314
317
|
status=status,
|
|
@@ -579,7 +582,7 @@ def get_fleet_spec(conf: Optional[FleetConfiguration] = None) -> FleetSpec:
|
|
|
579
582
|
|
|
580
583
|
def get_fleet_configuration(
|
|
581
584
|
name: str = "test-fleet",
|
|
582
|
-
nodes:
|
|
585
|
+
nodes: FleetNodesSpec = FleetNodesSpec(min=1, target=1, max=1),
|
|
583
586
|
placement: Optional[InstanceGroupPlacement] = None,
|
|
584
587
|
) -> FleetConfiguration:
|
|
585
588
|
return FleetConfiguration(
|
dstack/_internal/utils/env.py
CHANGED
|
@@ -1,14 +1,88 @@
|
|
|
1
1
|
import os
|
|
2
|
+
from collections.abc import Mapping
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from typing import Optional, TypeVar, Union, overload
|
|
2
5
|
|
|
6
|
+
_Value = Union[str, int]
|
|
7
|
+
_T = TypeVar("_T", bound=Enum)
|
|
3
8
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
|
|
10
|
+
class Environ:
|
|
11
|
+
def __init__(self, environ: Mapping[str, str]):
|
|
12
|
+
self._environ = environ
|
|
13
|
+
|
|
14
|
+
@overload
|
|
15
|
+
def get_bool(self, name: str, *, default: None = None) -> Optional[bool]: ...
|
|
16
|
+
|
|
17
|
+
@overload
|
|
18
|
+
def get_bool(self, name: str, *, default: bool) -> bool: ...
|
|
19
|
+
|
|
20
|
+
def get_bool(self, name: str, *, default: Optional[bool] = None) -> Optional[bool]:
|
|
21
|
+
try:
|
|
22
|
+
raw_value = self._environ[name]
|
|
23
|
+
except KeyError:
|
|
24
|
+
return default
|
|
25
|
+
value = raw_value.lower()
|
|
26
|
+
if value in ["0", "false", "off"]:
|
|
27
|
+
return False
|
|
28
|
+
if value in ["1", "true", "on"]:
|
|
29
|
+
return True
|
|
30
|
+
raise ValueError(f"Invalid bool value: {name}={raw_value}")
|
|
31
|
+
|
|
32
|
+
@overload
|
|
33
|
+
def get_int(self, name: str, *, default: None = None) -> Optional[int]: ...
|
|
34
|
+
|
|
35
|
+
@overload
|
|
36
|
+
def get_int(self, name: str, *, default: int) -> int: ...
|
|
37
|
+
|
|
38
|
+
def get_int(self, name: str, *, default: Optional[int] = None) -> Optional[int]:
|
|
39
|
+
try:
|
|
40
|
+
raw_value = self._environ[name]
|
|
41
|
+
except KeyError:
|
|
42
|
+
return default
|
|
43
|
+
try:
|
|
44
|
+
return int(raw_value)
|
|
45
|
+
except ValueError as e:
|
|
46
|
+
raise ValueError(f"Invalid int value: {e}: {name}={raw_value}") from e
|
|
47
|
+
|
|
48
|
+
@overload
|
|
49
|
+
def get_enum(
|
|
50
|
+
self,
|
|
51
|
+
name: str,
|
|
52
|
+
enum_cls: type[_T],
|
|
53
|
+
*,
|
|
54
|
+
value_type: Optional[type[_Value]] = None,
|
|
55
|
+
default: None = None,
|
|
56
|
+
) -> Optional[_T]: ...
|
|
57
|
+
|
|
58
|
+
@overload
|
|
59
|
+
def get_enum(
|
|
60
|
+
self,
|
|
61
|
+
name: str,
|
|
62
|
+
enum_cls: type[_T],
|
|
63
|
+
*,
|
|
64
|
+
value_type: Optional[type[_Value]] = None,
|
|
65
|
+
default: _T,
|
|
66
|
+
) -> _T: ...
|
|
67
|
+
|
|
68
|
+
def get_enum(
|
|
69
|
+
self,
|
|
70
|
+
name: str,
|
|
71
|
+
enum_cls: type[_T],
|
|
72
|
+
*,
|
|
73
|
+
value_type: Optional[type[_Value]] = None,
|
|
74
|
+
default: Optional[_T] = None,
|
|
75
|
+
) -> Optional[_T]:
|
|
76
|
+
try:
|
|
77
|
+
raw_value = self._environ[name]
|
|
78
|
+
except KeyError:
|
|
79
|
+
return default
|
|
80
|
+
try:
|
|
81
|
+
if value_type is not None:
|
|
82
|
+
raw_value = value_type(raw_value)
|
|
83
|
+
return enum_cls(raw_value)
|
|
84
|
+
except (ValueError, TypeError) as e:
|
|
85
|
+
raise ValueError(f"Invalid {enum_cls.__name__} value: {e}: {name}={raw_value}") from e
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
environ = Environ(os.environ)
|
dstack/_internal/utils/path.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from dataclasses import dataclass
|
|
3
|
-
from pathlib import Path, PurePath
|
|
3
|
+
from pathlib import Path, PurePath, PurePosixPath
|
|
4
4
|
from typing import Union
|
|
5
5
|
|
|
6
6
|
PathLike = Union[str, os.PathLike]
|
|
@@ -48,3 +48,10 @@ def resolve_relative_path(path: PathLike) -> PurePath:
|
|
|
48
48
|
return normalize_path(path)
|
|
49
49
|
except ValueError:
|
|
50
50
|
raise ValueError("Path is outside of the repo")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def is_absolute_posix_path(path: PathLike) -> bool:
|
|
54
|
+
# Passing Windows path leads to undefined behavior
|
|
55
|
+
if str(path).startswith("~"):
|
|
56
|
+
return True
|
|
57
|
+
return PurePosixPath(path).is_absolute()
|
dstack/_internal/utils/ssh.py
CHANGED
|
@@ -50,6 +50,13 @@ def make_ssh_command_for_git(identity_file: PathLike) -> str:
|
|
|
50
50
|
)
|
|
51
51
|
|
|
52
52
|
|
|
53
|
+
def make_git_env(*, identity_file: Optional[PathLike] = None) -> dict[str, str]:
|
|
54
|
+
env: dict[str, str] = {"GIT_TERMINAL_PROMPT": "0"}
|
|
55
|
+
if identity_file is not None:
|
|
56
|
+
env["GIT_SSH_COMMAND"] = make_ssh_command_for_git(identity_file)
|
|
57
|
+
return env
|
|
58
|
+
|
|
59
|
+
|
|
53
60
|
def try_ssh_key_passphrase(identity_file: PathLike, passphrase: str = "") -> bool:
|
|
54
61
|
ssh_keygen = find_ssh_util("ssh-keygen")
|
|
55
62
|
if ssh_keygen is None:
|
dstack/api/_public/repos.py
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
|
-
from typing import Optional, Union
|
|
2
|
+
from typing import Literal, Optional, Union, overload
|
|
3
3
|
|
|
4
4
|
from git import InvalidGitRepositoryError
|
|
5
5
|
|
|
6
6
|
from dstack._internal.core.errors import ConfigurationError, ResourceNotExistsError
|
|
7
|
-
from dstack._internal.core.models.repos import
|
|
7
|
+
from dstack._internal.core.models.repos import (
|
|
8
|
+
LocalRepo,
|
|
9
|
+
RemoteRepo,
|
|
10
|
+
RemoteRepoCreds,
|
|
11
|
+
RepoHead,
|
|
12
|
+
RepoHeadWithCreds,
|
|
13
|
+
)
|
|
8
14
|
from dstack._internal.core.models.repos.base import Repo, RepoType
|
|
9
15
|
from dstack._internal.core.services.configs import ConfigManager
|
|
10
16
|
from dstack._internal.core.services.repos import (
|
|
11
17
|
InvalidRepoCredentialsError,
|
|
12
|
-
|
|
18
|
+
get_repo_creds_and_default_branch,
|
|
13
19
|
load_repo,
|
|
14
20
|
)
|
|
15
21
|
from dstack._internal.utils.crypto import generate_rsa_key_pair
|
|
@@ -34,6 +40,7 @@ class RepoCollection:
|
|
|
34
40
|
repo: Repo,
|
|
35
41
|
git_identity_file: Optional[PathLike] = None,
|
|
36
42
|
oauth_token: Optional[str] = None,
|
|
43
|
+
creds: Optional[RemoteRepoCreds] = None,
|
|
37
44
|
):
|
|
38
45
|
"""
|
|
39
46
|
Initializes the repo and configures its credentials in the project.
|
|
@@ -65,12 +72,13 @@ class RepoCollection:
|
|
|
65
72
|
repo: The repo to initialize.
|
|
66
73
|
git_identity_file: The private SSH key path for accessing the remote repo.
|
|
67
74
|
oauth_token: The GitHub OAuth token to access the remote repo.
|
|
75
|
+
creds: Optional prepared repo credentials. If specified, both `git_identity_file`
|
|
76
|
+
and `oauth_token` are ignored.
|
|
68
77
|
"""
|
|
69
|
-
creds
|
|
70
|
-
if isinstance(repo, RemoteRepo):
|
|
78
|
+
if creds is None and isinstance(repo, RemoteRepo):
|
|
71
79
|
assert repo.repo_url is not None
|
|
72
80
|
try:
|
|
73
|
-
creds =
|
|
81
|
+
creds, _ = get_repo_creds_and_default_branch(
|
|
74
82
|
repo_url=repo.repo_url,
|
|
75
83
|
identity_file=git_identity_file,
|
|
76
84
|
oauth_token=oauth_token,
|
|
@@ -175,6 +183,33 @@ class RepoCollection:
|
|
|
175
183
|
# TODO: add an API method with the same logic returning a bool value?
|
|
176
184
|
return repo_head.repo_creds is not None
|
|
177
185
|
|
|
186
|
+
@overload
|
|
187
|
+
def get(self, repo_id: str, *, with_creds: Literal[False] = False) -> Optional[RepoHead]: ...
|
|
188
|
+
|
|
189
|
+
@overload
|
|
190
|
+
def get(self, repo_id: str, *, with_creds: Literal[True]) -> Optional[RepoHeadWithCreds]: ...
|
|
191
|
+
|
|
192
|
+
def get(
|
|
193
|
+
self, repo_id: str, *, with_creds: bool = False
|
|
194
|
+
) -> Optional[Union[RepoHead, RepoHeadWithCreds]]:
|
|
195
|
+
"""
|
|
196
|
+
Returns the repo by `repo_id`
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
repo_id: The repo ID.
|
|
200
|
+
with_creds: include repo credentials in the response.
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
The repo or `None` if the repo is not found.
|
|
204
|
+
"""
|
|
205
|
+
method = self._api_client.repos.get
|
|
206
|
+
if with_creds:
|
|
207
|
+
method = self._api_client.repos.get_with_creds
|
|
208
|
+
try:
|
|
209
|
+
return method(self._project, repo_id)
|
|
210
|
+
except ResourceNotExistsError:
|
|
211
|
+
return None
|
|
212
|
+
|
|
178
213
|
|
|
179
214
|
def get_ssh_keypair(key_path: Optional[PathLike], dstack_key_path: Path) -> str:
|
|
180
215
|
"""Returns a path to the private key"""
|
dstack/api/_public/runs.py
CHANGED
|
@@ -433,6 +433,7 @@ class RunCollection:
|
|
|
433
433
|
repo: Optional[Repo] = None,
|
|
434
434
|
profile: Optional[Profile] = None,
|
|
435
435
|
configuration_path: Optional[str] = None,
|
|
436
|
+
repo_dir: Optional[str] = None,
|
|
436
437
|
) -> RunPlan:
|
|
437
438
|
"""
|
|
438
439
|
Get a run plan.
|
|
@@ -443,11 +444,17 @@ class RunCollection:
|
|
|
443
444
|
repo (Union[LocalRepo, RemoteRepo, VirtualRepo, None]):
|
|
444
445
|
The repo to use for the run. Pass `None` if repo is not needed.
|
|
445
446
|
profile: The profile to use for the run.
|
|
446
|
-
configuration_path: The path to the configuration file. Omit if the configuration
|
|
447
|
+
configuration_path: The path to the configuration file. Omit if the configuration
|
|
448
|
+
is not loaded from a file.
|
|
449
|
+
repo_dir: The path of the cloned repo inside the run container. If not set,
|
|
450
|
+
defaults first to the `repos[0].path` property of the configuration (for remote
|
|
451
|
+
repos only), then to `/workflow`.
|
|
447
452
|
|
|
448
453
|
Returns:
|
|
449
454
|
Run plan.
|
|
450
455
|
"""
|
|
456
|
+
# XXX: not using the LEGACY_REPO_DIR const in the docstring above, as the docs generator,
|
|
457
|
+
# apparently, doesn't support f-strings (f"""...""").
|
|
451
458
|
if repo is None:
|
|
452
459
|
repo = VirtualRepo()
|
|
453
460
|
repo_code_hash = None
|
|
@@ -455,11 +462,17 @@ class RunCollection:
|
|
|
455
462
|
with _prepare_code_file(repo) as (_, repo_code_hash):
|
|
456
463
|
pass
|
|
457
464
|
|
|
465
|
+
if repo_dir is None and configuration.repos:
|
|
466
|
+
repo_dir = configuration.repos[0].path
|
|
467
|
+
|
|
458
468
|
run_spec = RunSpec(
|
|
459
469
|
run_name=configuration.name,
|
|
460
470
|
repo_id=repo.repo_id,
|
|
461
471
|
repo_data=repo.run_repo_data,
|
|
462
472
|
repo_code_hash=repo_code_hash,
|
|
473
|
+
repo_dir=repo_dir,
|
|
474
|
+
# Server doesn't use this field since 0.19.27, but we still send it for compatibility
|
|
475
|
+
# with older servers
|
|
463
476
|
working_dir=configuration.working_dir,
|
|
464
477
|
configuration_path=configuration_path,
|
|
465
478
|
configuration=configuration,
|
dstack/version.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dstack
|
|
3
|
-
Version: 0.19.
|
|
3
|
+
Version: 0.19.28
|
|
4
4
|
Summary: dstack is an open-source orchestration engine for running AI workloads on any cloud or on-premises.
|
|
5
5
|
Project-URL: Homepage, https://dstack.ai
|
|
6
6
|
Project-URL: Source, https://github.com/dstackai/dstack
|
|
@@ -22,7 +22,7 @@ Requires-Dist: cryptography
|
|
|
22
22
|
Requires-Dist: cursor
|
|
23
23
|
Requires-Dist: filelock
|
|
24
24
|
Requires-Dist: gitpython
|
|
25
|
-
Requires-Dist: gpuhunt==0.1.
|
|
25
|
+
Requires-Dist: gpuhunt==0.1.8
|
|
26
26
|
Requires-Dist: ignore-python>=0.2.0
|
|
27
27
|
Requires-Dist: jsonschema
|
|
28
28
|
Requires-Dist: orjson
|