lightning-sdk 0.2.9__py3-none-any.whl → 0.2.10__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.
- lightning_sdk/__init__.py +1 -1
- lightning_sdk/api/deployment_api.py +1 -0
- lightning_sdk/api/lit_container_api.py +19 -2
- lightning_sdk/api/teamspace_api.py +20 -16
- lightning_sdk/api/utils.py +1 -1
- lightning_sdk/cli/entrypoint.py +2 -2
- lightning_sdk/cli/serve.py +141 -16
- lightning_sdk/cli/upload.py +4 -1
- lightning_sdk/deployment/deployment.py +5 -2
- lightning_sdk/lightning_cloud/openapi/__init__.py +5 -0
- lightning_sdk/lightning_cloud/openapi/api/cloud_space_service_api.py +98 -1
- lightning_sdk/lightning_cloud/openapi/models/__init__.py +5 -0
- lightning_sdk/lightning_cloud/openapi/models/cluster_id_capacityreservations_body.py +55 -3
- lightning_sdk/lightning_cloud/openapi/models/create.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/orgs_id_body.py +55 -3
- lightning_sdk/lightning_cloud/openapi/models/update.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_cloud_space.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_cloud_space_cold_start_metrics_stats.py +357 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_cloud_space_environment_template_config.py +29 -3
- lightning_sdk/lightning_cloud/openapi/models/v1_cloudflare_v1.py +227 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_cluster_accelerator.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_cluster_capacity_reservation.py +55 -3
- lightning_sdk/lightning_cloud/openapi/models/v1_cluster_security_options.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_cluster_spec.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_create_cloud_space_environment_template_request.py +29 -3
- lightning_sdk/lightning_cloud/openapi/models/v1_create_cluster_capacity_reservation_response.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_data_connection.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_gcp_direct_vpc.py +149 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_get_cloud_space_cold_start_metrics_stats_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_get_user_response.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_google_cloud_direct_v1.py +17 -17
- lightning_sdk/lightning_cloud/openapi/models/v1_organization.py +55 -3
- lightning_sdk/lightning_cloud/openapi/models/v1_project_cluster_binding.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_r2_data_connection.py +253 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_update_user_request.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_user_features.py +50 -24
- lightning_sdk/lightning_cloud/openapi/models/v1_validate_data_connection_response.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_weka_data_connection.py +29 -55
- lightning_sdk/lightning_cloud/openapi/models/validate.py +27 -1
- lightning_sdk/lit_container.py +12 -2
- lightning_sdk/models.py +22 -9
- lightning_sdk/serve.py +3 -0
- lightning_sdk/teamspace.py +2 -0
- lightning_sdk/utils/resolve.py +11 -4
- {lightning_sdk-0.2.9.dist-info → lightning_sdk-0.2.10.dist-info}/METADATA +1 -1
- {lightning_sdk-0.2.9.dist-info → lightning_sdk-0.2.10.dist-info}/RECORD +50 -45
- {lightning_sdk-0.2.9.dist-info → lightning_sdk-0.2.10.dist-info}/LICENSE +0 -0
- {lightning_sdk-0.2.9.dist-info → lightning_sdk-0.2.10.dist-info}/WHEEL +0 -0
- {lightning_sdk-0.2.9.dist-info → lightning_sdk-0.2.10.dist-info}/entry_points.txt +0 -0
- {lightning_sdk-0.2.9.dist-info → lightning_sdk-0.2.10.dist-info}/top_level.txt +0 -0
|
@@ -42,38 +42,33 @@ class V1WekaDataConnection(object):
|
|
|
42
42
|
"""
|
|
43
43
|
swagger_types = {
|
|
44
44
|
'backend_ips': 'list[str]',
|
|
45
|
-
'cores': 'int',
|
|
46
45
|
'file_system_name': 'str',
|
|
47
|
-
'
|
|
48
|
-
'
|
|
46
|
+
'memory_mb': 'int',
|
|
47
|
+
'readonly': 'bool'
|
|
49
48
|
}
|
|
50
49
|
|
|
51
50
|
attribute_map = {
|
|
52
51
|
'backend_ips': 'backendIps',
|
|
53
|
-
'cores': 'cores',
|
|
54
52
|
'file_system_name': 'fileSystemName',
|
|
55
|
-
'
|
|
56
|
-
'
|
|
53
|
+
'memory_mb': 'memoryMb',
|
|
54
|
+
'readonly': 'readonly'
|
|
57
55
|
}
|
|
58
56
|
|
|
59
|
-
def __init__(self, backend_ips: 'list[str]' =None,
|
|
57
|
+
def __init__(self, backend_ips: 'list[str]' =None, file_system_name: 'str' =None, memory_mb: 'int' =None, readonly: 'bool' =None): # noqa: E501
|
|
60
58
|
"""V1WekaDataConnection - a model defined in Swagger""" # noqa: E501
|
|
61
59
|
self._backend_ips = None
|
|
62
|
-
self._cores = None
|
|
63
60
|
self._file_system_name = None
|
|
64
|
-
self._management_ip = None
|
|
65
61
|
self._memory_mb = None
|
|
62
|
+
self._readonly = None
|
|
66
63
|
self.discriminator = None
|
|
67
64
|
if backend_ips is not None:
|
|
68
65
|
self.backend_ips = backend_ips
|
|
69
|
-
if cores is not None:
|
|
70
|
-
self.cores = cores
|
|
71
66
|
if file_system_name is not None:
|
|
72
67
|
self.file_system_name = file_system_name
|
|
73
|
-
if management_ip is not None:
|
|
74
|
-
self.management_ip = management_ip
|
|
75
68
|
if memory_mb is not None:
|
|
76
69
|
self.memory_mb = memory_mb
|
|
70
|
+
if readonly is not None:
|
|
71
|
+
self.readonly = readonly
|
|
77
72
|
|
|
78
73
|
@property
|
|
79
74
|
def backend_ips(self) -> 'list[str]':
|
|
@@ -96,27 +91,6 @@ class V1WekaDataConnection(object):
|
|
|
96
91
|
|
|
97
92
|
self._backend_ips = backend_ips
|
|
98
93
|
|
|
99
|
-
@property
|
|
100
|
-
def cores(self) -> 'int':
|
|
101
|
-
"""Gets the cores of this V1WekaDataConnection. # noqa: E501
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
:return: The cores of this V1WekaDataConnection. # noqa: E501
|
|
105
|
-
:rtype: int
|
|
106
|
-
"""
|
|
107
|
-
return self._cores
|
|
108
|
-
|
|
109
|
-
@cores.setter
|
|
110
|
-
def cores(self, cores: 'int'):
|
|
111
|
-
"""Sets the cores of this V1WekaDataConnection.
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
:param cores: The cores of this V1WekaDataConnection. # noqa: E501
|
|
115
|
-
:type: int
|
|
116
|
-
"""
|
|
117
|
-
|
|
118
|
-
self._cores = cores
|
|
119
|
-
|
|
120
94
|
@property
|
|
121
95
|
def file_system_name(self) -> 'str':
|
|
122
96
|
"""Gets the file_system_name of this V1WekaDataConnection. # noqa: E501
|
|
@@ -138,27 +112,6 @@ class V1WekaDataConnection(object):
|
|
|
138
112
|
|
|
139
113
|
self._file_system_name = file_system_name
|
|
140
114
|
|
|
141
|
-
@property
|
|
142
|
-
def management_ip(self) -> 'str':
|
|
143
|
-
"""Gets the management_ip of this V1WekaDataConnection. # noqa: E501
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
:return: The management_ip of this V1WekaDataConnection. # noqa: E501
|
|
147
|
-
:rtype: str
|
|
148
|
-
"""
|
|
149
|
-
return self._management_ip
|
|
150
|
-
|
|
151
|
-
@management_ip.setter
|
|
152
|
-
def management_ip(self, management_ip: 'str'):
|
|
153
|
-
"""Sets the management_ip of this V1WekaDataConnection.
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
:param management_ip: The management_ip of this V1WekaDataConnection. # noqa: E501
|
|
157
|
-
:type: str
|
|
158
|
-
"""
|
|
159
|
-
|
|
160
|
-
self._management_ip = management_ip
|
|
161
|
-
|
|
162
115
|
@property
|
|
163
116
|
def memory_mb(self) -> 'int':
|
|
164
117
|
"""Gets the memory_mb of this V1WekaDataConnection. # noqa: E501
|
|
@@ -180,6 +133,27 @@ class V1WekaDataConnection(object):
|
|
|
180
133
|
|
|
181
134
|
self._memory_mb = memory_mb
|
|
182
135
|
|
|
136
|
+
@property
|
|
137
|
+
def readonly(self) -> 'bool':
|
|
138
|
+
"""Gets the readonly of this V1WekaDataConnection. # noqa: E501
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
:return: The readonly of this V1WekaDataConnection. # noqa: E501
|
|
142
|
+
:rtype: bool
|
|
143
|
+
"""
|
|
144
|
+
return self._readonly
|
|
145
|
+
|
|
146
|
+
@readonly.setter
|
|
147
|
+
def readonly(self, readonly: 'bool'):
|
|
148
|
+
"""Sets the readonly of this V1WekaDataConnection.
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
:param readonly: The readonly of this V1WekaDataConnection. # noqa: E501
|
|
152
|
+
:type: bool
|
|
153
|
+
"""
|
|
154
|
+
|
|
155
|
+
self._readonly = readonly
|
|
156
|
+
|
|
183
157
|
def to_dict(self) -> dict:
|
|
184
158
|
"""Returns the model properties as a dict"""
|
|
185
159
|
result = {}
|
|
@@ -48,6 +48,7 @@ class Validate(object):
|
|
|
48
48
|
'filestore': 'V1FilestoreDataConnection',
|
|
49
49
|
'gcp': 'V1GcpDataConnection',
|
|
50
50
|
'gcs_folder': 'V1GCSFolderDataConnection',
|
|
51
|
+
'r2': 'V1R2DataConnection',
|
|
51
52
|
's3_folder': 'V1S3FolderDataConnection'
|
|
52
53
|
}
|
|
53
54
|
|
|
@@ -59,10 +60,11 @@ class Validate(object):
|
|
|
59
60
|
'filestore': 'filestore',
|
|
60
61
|
'gcp': 'gcp',
|
|
61
62
|
'gcs_folder': 'gcsFolder',
|
|
63
|
+
'r2': 'r2',
|
|
62
64
|
's3_folder': 's3Folder'
|
|
63
65
|
}
|
|
64
66
|
|
|
65
|
-
def __init__(self, aws: 'V1AwsDataConnection' =None, check_is_public: 'bool' =None, cluster_ids: 'list[str]' =None, efs: 'V1EfsConfig' =None, filestore: 'V1FilestoreDataConnection' =None, gcp: 'V1GcpDataConnection' =None, gcs_folder: 'V1GCSFolderDataConnection' =None, s3_folder: 'V1S3FolderDataConnection' =None): # noqa: E501
|
|
67
|
+
def __init__(self, aws: 'V1AwsDataConnection' =None, check_is_public: 'bool' =None, cluster_ids: 'list[str]' =None, efs: 'V1EfsConfig' =None, filestore: 'V1FilestoreDataConnection' =None, gcp: 'V1GcpDataConnection' =None, gcs_folder: 'V1GCSFolderDataConnection' =None, r2: 'V1R2DataConnection' =None, s3_folder: 'V1S3FolderDataConnection' =None): # noqa: E501
|
|
66
68
|
"""Validate - a model defined in Swagger""" # noqa: E501
|
|
67
69
|
self._aws = None
|
|
68
70
|
self._check_is_public = None
|
|
@@ -71,6 +73,7 @@ class Validate(object):
|
|
|
71
73
|
self._filestore = None
|
|
72
74
|
self._gcp = None
|
|
73
75
|
self._gcs_folder = None
|
|
76
|
+
self._r2 = None
|
|
74
77
|
self._s3_folder = None
|
|
75
78
|
self.discriminator = None
|
|
76
79
|
if aws is not None:
|
|
@@ -87,6 +90,8 @@ class Validate(object):
|
|
|
87
90
|
self.gcp = gcp
|
|
88
91
|
if gcs_folder is not None:
|
|
89
92
|
self.gcs_folder = gcs_folder
|
|
93
|
+
if r2 is not None:
|
|
94
|
+
self.r2 = r2
|
|
90
95
|
if s3_folder is not None:
|
|
91
96
|
self.s3_folder = s3_folder
|
|
92
97
|
|
|
@@ -237,6 +242,27 @@ class Validate(object):
|
|
|
237
242
|
|
|
238
243
|
self._gcs_folder = gcs_folder
|
|
239
244
|
|
|
245
|
+
@property
|
|
246
|
+
def r2(self) -> 'V1R2DataConnection':
|
|
247
|
+
"""Gets the r2 of this Validate. # noqa: E501
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
:return: The r2 of this Validate. # noqa: E501
|
|
251
|
+
:rtype: V1R2DataConnection
|
|
252
|
+
"""
|
|
253
|
+
return self._r2
|
|
254
|
+
|
|
255
|
+
@r2.setter
|
|
256
|
+
def r2(self, r2: 'V1R2DataConnection'):
|
|
257
|
+
"""Sets the r2 of this Validate.
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
:param r2: The r2 of this Validate. # noqa: E501
|
|
261
|
+
:type: V1R2DataConnection
|
|
262
|
+
"""
|
|
263
|
+
|
|
264
|
+
self._r2 = r2
|
|
265
|
+
|
|
240
266
|
@property
|
|
241
267
|
def s3_folder(self) -> 'V1S3FolderDataConnection':
|
|
242
268
|
"""Gets the s3_folder of this Validate. # noqa: E501
|
lightning_sdk/lit_container.py
CHANGED
|
@@ -80,7 +80,8 @@ class LitContainer:
|
|
|
80
80
|
tag: str = "latest",
|
|
81
81
|
cloud_account: Optional[str] = None,
|
|
82
82
|
platform: Optional[str] = "linux/amd64",
|
|
83
|
-
|
|
83
|
+
return_final_dict: bool = False,
|
|
84
|
+
) -> Optional[Dict]:
|
|
84
85
|
"""Upload a container to the docker registry.
|
|
85
86
|
|
|
86
87
|
Args:
|
|
@@ -91,15 +92,24 @@ class LitContainer:
|
|
|
91
92
|
tag: The tag to use for the container.
|
|
92
93
|
cloud_account: The cloud account where the container is stored.
|
|
93
94
|
platform: The platform the container is meant to run on.
|
|
95
|
+
return_final_dict: Instructs function to return metadata about container location in platform.
|
|
94
96
|
"""
|
|
95
97
|
try:
|
|
96
98
|
teamspace = _resolve_teamspace(teamspace=teamspace, org=org, user=user)
|
|
97
99
|
except Exception as e:
|
|
98
100
|
raise ValueError(f"Could not resolve teamspace: {e}") from e
|
|
99
101
|
|
|
100
|
-
resp = self._api.upload_container(
|
|
102
|
+
resp = self._api.upload_container(
|
|
103
|
+
container, teamspace, tag, cloud_account, platform=platform, return_final_dict=return_final_dict
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
final_dict = None
|
|
101
107
|
for line in resp:
|
|
102
108
|
print(line)
|
|
109
|
+
if return_final_dict and isinstance(line, dict) and line.get("finish") is True:
|
|
110
|
+
final_dict = line
|
|
111
|
+
|
|
112
|
+
return final_dict if return_final_dict else None
|
|
103
113
|
|
|
104
114
|
def download_container(
|
|
105
115
|
self,
|
lightning_sdk/models.py
CHANGED
|
@@ -85,7 +85,7 @@ def _extend_model_name_with_teamspace(name: str) -> str:
|
|
|
85
85
|
return f"{teamspace.owner.name}/{teamspace.name}/{name}"
|
|
86
86
|
|
|
87
87
|
|
|
88
|
-
def
|
|
88
|
+
def _parse_org_teamspace_model_version(name: str) -> Tuple[str, str, str, Optional[str]]:
|
|
89
89
|
"""Parse the name argument into its components."""
|
|
90
90
|
try:
|
|
91
91
|
org_name, teamspace_name, model_name = name.split("/")
|
|
@@ -119,19 +119,14 @@ def download_model(
|
|
|
119
119
|
progress_bar: Whether to show a progress bar when downloading.
|
|
120
120
|
"""
|
|
121
121
|
name = _extend_model_name_with_teamspace(name)
|
|
122
|
-
teamspace_owner_name, teamspace_name, model_name, version =
|
|
123
|
-
if version is None:
|
|
124
|
-
version = "default"
|
|
125
|
-
|
|
126
|
-
download_dir = Path(download_dir)
|
|
127
|
-
|
|
122
|
+
teamspace_owner_name, teamspace_name, model_name, version = _parse_org_teamspace_model_version(name)
|
|
128
123
|
api = TeamspaceApi()
|
|
129
124
|
|
|
130
125
|
try:
|
|
131
126
|
return api.download_model_files(
|
|
132
127
|
name=model_name,
|
|
133
128
|
version=version,
|
|
134
|
-
download_dir=download_dir,
|
|
129
|
+
download_dir=Path(download_dir),
|
|
135
130
|
teamspace_name=teamspace_name,
|
|
136
131
|
teamspace_owner_name=teamspace_owner_name,
|
|
137
132
|
progress_bar=progress_bar,
|
|
@@ -160,9 +155,11 @@ def upload_model(
|
|
|
160
155
|
cloud_account: The name of the cloud account to store the Model in.
|
|
161
156
|
If not provided, the default cloud account for the Teamspace will be used.
|
|
162
157
|
progress_bar: Whether to show a progress bar for the upload.
|
|
158
|
+
metadata: Metadata to attach to the uploaded model.
|
|
159
|
+
If not provided, an empty dictionary will be used.
|
|
163
160
|
"""
|
|
164
161
|
name = _extend_model_name_with_teamspace(name)
|
|
165
|
-
org_name, teamspace_name, model_name, version =
|
|
162
|
+
org_name, teamspace_name, model_name, version = _parse_org_teamspace_model_version(name)
|
|
166
163
|
teamspace = _get_teamspace(name=teamspace_name, organization=org_name)
|
|
167
164
|
return teamspace.upload_model(
|
|
168
165
|
path=path,
|
|
@@ -172,3 +169,19 @@ def upload_model(
|
|
|
172
169
|
progress_bar=progress_bar,
|
|
173
170
|
metadata=metadata,
|
|
174
171
|
)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def delete_model(
|
|
175
|
+
name: str,
|
|
176
|
+
) -> None:
|
|
177
|
+
"""Delete a model or a version of model.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
name: The name of the model you want to delete or with specified version it deltes only that version.
|
|
181
|
+
This should have the format <ORGANIZATION-NAME>/<TEAMSPACE-NAME>/<MODEL-NAME> for full model deletion
|
|
182
|
+
or <ORGANIZATION-NAME>/<TEAMSPACE-NAME>/<MODEL-NAME>:<VERSION> for version deletion.
|
|
183
|
+
"""
|
|
184
|
+
name = _extend_model_name_with_teamspace(name)
|
|
185
|
+
org_name, teamspace_name, model_name, version = _parse_org_teamspace_model_version(name)
|
|
186
|
+
teamspace = _get_teamspace(name=teamspace_name, organization=org_name)
|
|
187
|
+
teamspace.delete_model(name=model_name)
|
lightning_sdk/serve.py
CHANGED
|
@@ -248,6 +248,7 @@ Update [underline]{os.path.abspath("Dockerfile")}[/underline] to add any additio
|
|
|
248
248
|
cloud_account: Optional[str] = None,
|
|
249
249
|
port: Optional[int] = 8000,
|
|
250
250
|
include_credentials: Optional[bool] = True,
|
|
251
|
+
cloudspace_id: Optional[str] = None,
|
|
251
252
|
) -> dict:
|
|
252
253
|
"""Run a deployment on the cloud. If the deployment already exists, it will be updated.
|
|
253
254
|
|
|
@@ -264,6 +265,7 @@ Update [underline]{os.path.abspath("Dockerfile")}[/underline] to add any additio
|
|
|
264
265
|
cloud_account: The cloud account to run the deployment on. Defaults to None.
|
|
265
266
|
port: The port to run the deployment on. Defaults to 8000.
|
|
266
267
|
include_credentials: Whether to include credentials in the deployment. Defaults to True.
|
|
268
|
+
cloudspace_id: Connect to a Studio.
|
|
267
269
|
|
|
268
270
|
Returns:
|
|
269
271
|
dict: The deployment and the URL of the deployment.
|
|
@@ -297,6 +299,7 @@ Update [underline]{os.path.abspath("Dockerfile")}[/underline] to add any additio
|
|
|
297
299
|
cloud_account=cloud_account,
|
|
298
300
|
ports=[port],
|
|
299
301
|
include_credentials=include_credentials,
|
|
302
|
+
cloudspace_id=cloudspace_id,
|
|
300
303
|
)
|
|
301
304
|
|
|
302
305
|
return {"deployment": deployment, "url": url}
|
lightning_sdk/teamspace.py
CHANGED
|
@@ -262,6 +262,8 @@ class Teamspace:
|
|
|
262
262
|
Args:
|
|
263
263
|
path: Path to the model file or folder to upload.
|
|
264
264
|
name: Name tag of the model to upload.
|
|
265
|
+
version: Version tag of the model to upload.
|
|
266
|
+
If not provided, the ``vX`` version will be used where X is running index.
|
|
265
267
|
cloud_account: The name of the cloud account to store the Model in.
|
|
266
268
|
If not provided, the default cloud account for the Teamspace will be used.
|
|
267
269
|
progress_bar: Whether to show a progress bar for the upload.
|
lightning_sdk/utils/resolve.py
CHANGED
|
@@ -179,16 +179,23 @@ def skip_studio_init() -> Generator[None, None, None]:
|
|
|
179
179
|
Studio._skip_init = prev_studio_init_state
|
|
180
180
|
|
|
181
181
|
|
|
182
|
-
def _parse_model_and_version(name: str) -> Tuple[str, str]:
|
|
182
|
+
def _parse_model_and_version(name: str) -> Tuple[str, Optional[str]]:
|
|
183
|
+
"""Parse the model name and version from the given string.
|
|
184
|
+
|
|
185
|
+
>>> _parse_model_and_version("org/teamspace/modelname")
|
|
186
|
+
('org/teamspace/modelname', None)
|
|
187
|
+
>>> _parse_model_and_version("org/teamspace/modelname:version")
|
|
188
|
+
('org/teamspace/modelname', 'version')
|
|
189
|
+
"""
|
|
183
190
|
parts = name.split(":")
|
|
184
191
|
if len(parts) == 1:
|
|
185
|
-
return parts[0],
|
|
192
|
+
return parts[0], None
|
|
186
193
|
if len(parts) == 2:
|
|
187
194
|
return parts[0], parts[1]
|
|
188
195
|
# The rest of the validation for name and version happens in the backend
|
|
189
196
|
raise ValueError(
|
|
190
|
-
"Model version is expected to be in the format `entity/modelname:version` separated by a"
|
|
191
|
-
f"
|
|
197
|
+
"Model version is expected to be in the format `entity/modelname:version` separated by a single colon,"
|
|
198
|
+
f" but got: {name}"
|
|
192
199
|
)
|
|
193
200
|
|
|
194
201
|
|