gmicloud 0.1.0__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.
- examples/__init__.py +0 -0
- examples/example.py +145 -0
- gmicloud/__init__.py +41 -0
- gmicloud/_internal/__init__.py +0 -0
- gmicloud/_internal/_client/__init__.py +0 -0
- gmicloud/_internal/_client/_artifact_client.py +179 -0
- gmicloud/_internal/_client/_decorator.py +22 -0
- gmicloud/_internal/_client/_file_upload_client.py +115 -0
- gmicloud/_internal/_client/_http_client.py +150 -0
- gmicloud/_internal/_client/_iam_client.py +92 -0
- gmicloud/_internal/_client/_task_client.py +142 -0
- gmicloud/_internal/_config.py +3 -0
- gmicloud/_internal/_constants.py +13 -0
- gmicloud/_internal/_enums.py +23 -0
- gmicloud/_internal/_exceptions.py +19 -0
- gmicloud/_internal/_manager/__init__.py +0 -0
- gmicloud/_internal/_manager/_artifact_manager.py +303 -0
- gmicloud/_internal/_manager/_task_manager.py +221 -0
- gmicloud/_internal/_models.py +336 -0
- gmicloud/client.py +51 -0
- gmicloud/tests/__init__.py +0 -0
- gmicloud/tests/test_artifacts.py +274 -0
- gmicloud/tests/test_tasks.py +207 -0
- gmicloud-0.1.0.dist-info/METADATA +208 -0
- gmicloud-0.1.0.dist-info/RECORD +27 -0
- gmicloud-0.1.0.dist-info/WHEEL +5 -0
- gmicloud-0.1.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,92 @@
|
|
1
|
+
import jwt
|
2
|
+
|
3
|
+
from ._http_client import HTTPClient
|
4
|
+
from .._config import IAM_SERVICE_BASE_URL
|
5
|
+
from .._models import *
|
6
|
+
from .._constants import CLIENT_ID_HEADER
|
7
|
+
|
8
|
+
|
9
|
+
class IAMClient:
|
10
|
+
"""
|
11
|
+
Client for interacting with the IAM Service API.
|
12
|
+
"""
|
13
|
+
|
14
|
+
def __init__(self, client_id: str, email: str, password: str):
|
15
|
+
"""
|
16
|
+
Initialize IAMClient with client credentials and an IAMClient instance.
|
17
|
+
|
18
|
+
:param client_id: Client ID for login.
|
19
|
+
:param email: Email for login.
|
20
|
+
:param password: Password for login.
|
21
|
+
"""
|
22
|
+
self._client_id = client_id
|
23
|
+
self._email = email
|
24
|
+
self._password = password
|
25
|
+
self._access_token = ""
|
26
|
+
self._refresh_token = ""
|
27
|
+
self._user_id = ""
|
28
|
+
self.client = HTTPClient(IAM_SERVICE_BASE_URL)
|
29
|
+
|
30
|
+
def login(self):
|
31
|
+
"""
|
32
|
+
Logs in a user with the given username and password.
|
33
|
+
"""
|
34
|
+
custom_headers = {
|
35
|
+
CLIENT_ID_HEADER: self._client_id
|
36
|
+
}
|
37
|
+
req = LoginRequest(email=self._email, password=self._password)
|
38
|
+
result = self.client.post("/me/sessions", custom_headers, req.model_dump())
|
39
|
+
|
40
|
+
resp = LoginResponse.model_validate(result)
|
41
|
+
self._access_token = resp.accessToken
|
42
|
+
self._refresh_token = resp.refreshToken
|
43
|
+
self._user_id = self.parse_user_id()
|
44
|
+
|
45
|
+
def refresh_token(self):
|
46
|
+
"""
|
47
|
+
Refreshes a user's token using a refresh token.
|
48
|
+
"""
|
49
|
+
custom_headers = {
|
50
|
+
CLIENT_ID_HEADER: self._client_id
|
51
|
+
}
|
52
|
+
result = self.client.patch("/me/sessions", custom_headers, {"refreshToken": self.refresh_token})
|
53
|
+
|
54
|
+
resp = LoginResponse.model_validate(result)
|
55
|
+
self._access_token = resp.accessToken
|
56
|
+
self._refresh_token = resp.refreshToken
|
57
|
+
|
58
|
+
def parse_user_id(self) -> str:
|
59
|
+
"""
|
60
|
+
Parses the current access token and returns the user ID.
|
61
|
+
"""
|
62
|
+
return self.parse_token()["userId"]
|
63
|
+
|
64
|
+
def parse_token(self) -> dict:
|
65
|
+
"""
|
66
|
+
Parses the current access token and returns the payload as a dictionary.
|
67
|
+
"""
|
68
|
+
return jwt.decode(self._access_token, options={"verify_signature": False})
|
69
|
+
|
70
|
+
def get_access_token(self) -> str:
|
71
|
+
"""
|
72
|
+
Gets the current access token.
|
73
|
+
"""
|
74
|
+
return self._access_token
|
75
|
+
|
76
|
+
def get_refresh_token(self) -> str:
|
77
|
+
"""
|
78
|
+
Gets the current refresh token.
|
79
|
+
"""
|
80
|
+
return self._refresh_token
|
81
|
+
|
82
|
+
def get_user_id(self) -> str:
|
83
|
+
"""
|
84
|
+
Gets the current user ID.
|
85
|
+
"""
|
86
|
+
return self._user_id
|
87
|
+
|
88
|
+
def get_client_id(self) -> str:
|
89
|
+
"""
|
90
|
+
Gets the current client ID.
|
91
|
+
"""
|
92
|
+
return self._client_id
|
@@ -0,0 +1,142 @@
|
|
1
|
+
from ._http_client import HTTPClient
|
2
|
+
from ._decorator import handle_refresh_token
|
3
|
+
from ._iam_client import IAMClient
|
4
|
+
from .._config import TASK_SERVICE_BASE_URL
|
5
|
+
from .._models import *
|
6
|
+
from .._constants import ACCESS_TOKEN_HEADER, CLIENT_ID_HEADER
|
7
|
+
|
8
|
+
|
9
|
+
class TaskClient:
|
10
|
+
"""
|
11
|
+
A client for interacting with the task service API.
|
12
|
+
|
13
|
+
This client provides methods to retrieve, create, update, and stop tasks
|
14
|
+
through HTTP calls to the task service.
|
15
|
+
"""
|
16
|
+
|
17
|
+
def __init__(self, iam_client: IAMClient):
|
18
|
+
"""
|
19
|
+
Initializes the TaskClient with the given base URL for the task service.
|
20
|
+
"""
|
21
|
+
self.client = HTTPClient(TASK_SERVICE_BASE_URL)
|
22
|
+
self.iam_client = iam_client
|
23
|
+
|
24
|
+
@handle_refresh_token
|
25
|
+
def get_task(self, task_id: str) -> Task:
|
26
|
+
"""
|
27
|
+
Retrieves a task from the task service using the given task ID.
|
28
|
+
|
29
|
+
:param task_id: The ID of the task to be retrieved.
|
30
|
+
:return: An instance of Task containing the details of the retrieved task.
|
31
|
+
:rtype: Task
|
32
|
+
"""
|
33
|
+
custom_headers = {
|
34
|
+
ACCESS_TOKEN_HEADER: self.iam_client.get_access_token(),
|
35
|
+
CLIENT_ID_HEADER: self.iam_client.get_client_id()
|
36
|
+
}
|
37
|
+
result = self.client.get("/get_task", custom_headers, {"task_id": task_id})
|
38
|
+
|
39
|
+
return Task.model_validate(result)
|
40
|
+
|
41
|
+
@handle_refresh_token
|
42
|
+
def get_all_tasks(self, user_id: str) -> GetAllTasksResponse:
|
43
|
+
"""
|
44
|
+
Retrieves all tasks from the task service.
|
45
|
+
|
46
|
+
:return: An instance of GetAllTasksResponse containing the retrieved tasks.
|
47
|
+
:rtype: GetAllTasksResponse
|
48
|
+
"""
|
49
|
+
custom_headers = {
|
50
|
+
ACCESS_TOKEN_HEADER: self.iam_client.get_access_token(),
|
51
|
+
CLIENT_ID_HEADER: self.iam_client.get_client_id()
|
52
|
+
}
|
53
|
+
result = self.client.get("/get_tasks", custom_headers, {"user_id": user_id})
|
54
|
+
if not result:
|
55
|
+
return GetAllTasksResponse(tasks=[])
|
56
|
+
|
57
|
+
return GetAllTasksResponse.model_validate(result)
|
58
|
+
|
59
|
+
@handle_refresh_token
|
60
|
+
def create_task(self, task: Task) -> CreateTaskResponse:
|
61
|
+
"""
|
62
|
+
Creates a new task using the provided task object.
|
63
|
+
|
64
|
+
:param task: The Task object containing the details of the task to be created.
|
65
|
+
"""
|
66
|
+
custom_headers = {
|
67
|
+
ACCESS_TOKEN_HEADER: self.iam_client.get_access_token(),
|
68
|
+
CLIENT_ID_HEADER: self.iam_client.get_client_id()
|
69
|
+
}
|
70
|
+
|
71
|
+
result = self.client.post("/create_task", custom_headers, task.model_dump())
|
72
|
+
|
73
|
+
return CreateTaskResponse.model_validate(result)
|
74
|
+
|
75
|
+
@handle_refresh_token
|
76
|
+
def update_task_schedule(self, task: Task):
|
77
|
+
"""
|
78
|
+
Updates the schedule of an existing task.
|
79
|
+
|
80
|
+
:param task: The Task object containing the updated task details.
|
81
|
+
"""
|
82
|
+
custom_headers = {
|
83
|
+
ACCESS_TOKEN_HEADER: self.iam_client.get_access_token(),
|
84
|
+
CLIENT_ID_HEADER: self.iam_client.get_client_id()
|
85
|
+
}
|
86
|
+
self.client.put("/update_schedule", custom_headers, task.model_dump())
|
87
|
+
|
88
|
+
@handle_refresh_token
|
89
|
+
def start_task(self, task_id: str):
|
90
|
+
"""
|
91
|
+
Starts a task using the given task ID.
|
92
|
+
|
93
|
+
:param task_id: The ID of the task to be started.
|
94
|
+
"""
|
95
|
+
custom_headers = {
|
96
|
+
ACCESS_TOKEN_HEADER: self.iam_client.get_access_token(),
|
97
|
+
CLIENT_ID_HEADER: self.iam_client.get_client_id()
|
98
|
+
}
|
99
|
+
self.client.post("/start_task", custom_headers, {"task_id": task_id})
|
100
|
+
|
101
|
+
@handle_refresh_token
|
102
|
+
def stop_task(self, task_id: str):
|
103
|
+
"""
|
104
|
+
Stops a running task using the given task ID.
|
105
|
+
|
106
|
+
:param task_id: The ID of the task to be stopped.
|
107
|
+
"""
|
108
|
+
custom_headers = {
|
109
|
+
ACCESS_TOKEN_HEADER: self.iam_client.get_access_token(),
|
110
|
+
CLIENT_ID_HEADER: self.iam_client.get_client_id()
|
111
|
+
}
|
112
|
+
self.client.post("/stop_task", custom_headers, {"task_id": task_id})
|
113
|
+
|
114
|
+
@handle_refresh_token
|
115
|
+
def get_usage_data(self, start_timestamp: str, end_timestamp: str) -> GetUsageDataResponse:
|
116
|
+
"""
|
117
|
+
Retrieves the usage data of a task using the given task ID.
|
118
|
+
|
119
|
+
:param start_timestamp: The start timestamp of the usage data.
|
120
|
+
:param end_timestamp: The end timestamp of the usage data.
|
121
|
+
"""
|
122
|
+
custom_headers = {
|
123
|
+
ACCESS_TOKEN_HEADER: self.iam_client.get_access_token(),
|
124
|
+
CLIENT_ID_HEADER: self.iam_client.get_client_id()
|
125
|
+
}
|
126
|
+
result = self.client.get("/get_usage_data", custom_headers,
|
127
|
+
{"start_timestamp": start_timestamp, "end_timestamp": end_timestamp})
|
128
|
+
|
129
|
+
return result
|
130
|
+
|
131
|
+
@handle_refresh_token
|
132
|
+
def archive_task(self, task_id: str):
|
133
|
+
"""
|
134
|
+
Archives a task using the given task ID.
|
135
|
+
|
136
|
+
:param task_id: The ID of the task to be archived.
|
137
|
+
"""
|
138
|
+
custom_headers = {
|
139
|
+
ACCESS_TOKEN_HEADER: self.iam_client.get_access_token(),
|
140
|
+
CLIENT_ID_HEADER: self.iam_client.get_client_id()
|
141
|
+
}
|
142
|
+
self.client.post("/archive_task", custom_headers, {"task_id": task_id})
|
@@ -0,0 +1,13 @@
|
|
1
|
+
ACCEPT_HEADER = 'Accept'
|
2
|
+
CONTENT_TYPE_HEADER = 'Content-Type'
|
3
|
+
CLIENT_ID_HEADER = 'CE-ClientId'
|
4
|
+
ACCESS_TOKEN_HEADER = 'CE-AccessToken'
|
5
|
+
|
6
|
+
HTTP_METHOD_POST = 'POST'
|
7
|
+
HTTP_METHOD_GET = 'GET'
|
8
|
+
HTTP_METHOD_PATCH = 'PATCH'
|
9
|
+
HTTP_METHOD_DELETE = 'DELETE'
|
10
|
+
HTTP_METHOD_PUT = 'PUT'
|
11
|
+
|
12
|
+
JSON_CONTENT_TYPE = 'application/json'
|
13
|
+
TEXT_CONTENT_TYPE = 'text/html'
|
@@ -0,0 +1,23 @@
|
|
1
|
+
from enum import Enum
|
2
|
+
|
3
|
+
|
4
|
+
class BuildStatus(str, Enum):
|
5
|
+
INIT = "INIT"
|
6
|
+
CREATED = "CREATED"
|
7
|
+
BUILDING = "BUILDING"
|
8
|
+
SUCCESS = "SUCCESS"
|
9
|
+
FAILURE = "FAILURE"
|
10
|
+
TIMEOUT = "TIMEOUT"
|
11
|
+
CANCELLED = "CANCELLED"
|
12
|
+
WORKING = "WORKING"
|
13
|
+
QUEUED = "QUEUED"
|
14
|
+
|
15
|
+
|
16
|
+
class TaskEndpointStatus(str, Enum):
|
17
|
+
PENDING = "pending"
|
18
|
+
DEPLOYING = "deploying"
|
19
|
+
SCALING = "scaling"
|
20
|
+
RUNNING = "running"
|
21
|
+
ARCHIVED = "archived"
|
22
|
+
READY = "ready"
|
23
|
+
UNREADY = "unready"
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class APIError(Exception):
|
2
|
+
"""
|
3
|
+
Generic exception for API-related errors.
|
4
|
+
"""
|
5
|
+
pass
|
6
|
+
|
7
|
+
|
8
|
+
class UploadFileError(Exception):
|
9
|
+
"""
|
10
|
+
Exception for file upload errors.
|
11
|
+
"""
|
12
|
+
pass
|
13
|
+
|
14
|
+
|
15
|
+
class UnauthorizedError(Exception):
|
16
|
+
"""
|
17
|
+
Exception for unauthorized access errors.
|
18
|
+
"""
|
19
|
+
pass
|
File without changes
|
@@ -0,0 +1,303 @@
|
|
1
|
+
import os
|
2
|
+
from typing import List
|
3
|
+
import mimetypes
|
4
|
+
|
5
|
+
from .._client._iam_client import IAMClient
|
6
|
+
from .._client._artifact_client import ArtifactClient
|
7
|
+
from .._client._file_upload_client import FileUploadClient
|
8
|
+
from .._models import *
|
9
|
+
|
10
|
+
|
11
|
+
class ArtifactManager:
|
12
|
+
"""
|
13
|
+
Artifact Manager handles creation, retrieval, and file upload associated with artifacts.
|
14
|
+
"""
|
15
|
+
|
16
|
+
def __init__(self, iam_client: IAMClient):
|
17
|
+
"""
|
18
|
+
Initialize the ArtifactManager instance with the user ID and access token.
|
19
|
+
|
20
|
+
:param iam_client: The IAMClient instance to use for authentication.
|
21
|
+
:raises ValueError: If the `user_id` is None or an empty string.
|
22
|
+
"""
|
23
|
+
self.iam_client = iam_client
|
24
|
+
self.artifact_client = ArtifactClient(iam_client)
|
25
|
+
|
26
|
+
def get_artifact(self, artifact_id: str) -> Artifact:
|
27
|
+
"""
|
28
|
+
Retrieve an artifact by its ID.
|
29
|
+
|
30
|
+
:param artifact_id: The ID of the artifact to retrieve.
|
31
|
+
:return: The Artifact object associated with the ID.
|
32
|
+
:rtype: Artifact
|
33
|
+
:raises ValueError: If `artifact_id` is None or empty.
|
34
|
+
"""
|
35
|
+
self._validate_artifact_id(artifact_id)
|
36
|
+
|
37
|
+
return self.artifact_client.get_artifact(artifact_id)
|
38
|
+
|
39
|
+
def get_all_artifacts(self) -> List[Artifact]:
|
40
|
+
"""
|
41
|
+
Retrieve all artifacts for a given user.
|
42
|
+
|
43
|
+
:return: A list of Artifact objects associated with the user.
|
44
|
+
:rtype: List[Artifact]
|
45
|
+
"""
|
46
|
+
return self.artifact_client.get_all_artifacts(self.iam_client.get_user_id())
|
47
|
+
|
48
|
+
def create_artifact(
|
49
|
+
self,
|
50
|
+
artifact_name: str,
|
51
|
+
description: Optional[str] = "",
|
52
|
+
tags: Optional[List[str]] = None
|
53
|
+
) -> CreateArtifactResponse:
|
54
|
+
"""
|
55
|
+
Create a new artifact for a user.
|
56
|
+
|
57
|
+
:param artifact_name: The name of the artifact.
|
58
|
+
:param description: An optional description for the artifact.
|
59
|
+
:param tags: Optional tags associated with the artifact, as a comma-separated string.
|
60
|
+
:return: A `CreateArtifactResponse` object containing information about the created artifact.
|
61
|
+
:rtype: CreateArtifactResponse
|
62
|
+
"""
|
63
|
+
if not artifact_name or not artifact_name.strip():
|
64
|
+
raise ValueError("Artifact name is required and cannot be empty.")
|
65
|
+
|
66
|
+
req = CreateArtifactRequest(user_id=self.iam_client.get_user_id(), artifact_name=artifact_name,
|
67
|
+
artifact_description=description,
|
68
|
+
artifact_tags=tags, )
|
69
|
+
|
70
|
+
return self.artifact_client.create_artifact(req)
|
71
|
+
|
72
|
+
def create_artifact_from_template(self, artifact_template_id: str) -> CreateArtifactFromTemplateResponse:
|
73
|
+
"""
|
74
|
+
Create a new artifact for a user using a template.
|
75
|
+
|
76
|
+
:param artifact_template_id: The ID of the template to use for the artifact.
|
77
|
+
:return: A `CreateArtifactResponse` object containing information about the created artifact.
|
78
|
+
:rtype: CreateArtifactResponse
|
79
|
+
"""
|
80
|
+
if not artifact_template_id or not artifact_template_id.strip():
|
81
|
+
raise ValueError("Artifact template ID is required and cannot be empty.")
|
82
|
+
|
83
|
+
req = CreateArtifactFromTemplateRequest(user_id=self.iam_client.get_user_id(),
|
84
|
+
artifact_template_id=artifact_template_id)
|
85
|
+
|
86
|
+
return self.artifact_client.create_artifact_from_template(req)
|
87
|
+
|
88
|
+
def rebuild_artifact(self, artifact_id: str) -> RebuildArtifactResponse:
|
89
|
+
"""
|
90
|
+
Rebuild an existing artifact.
|
91
|
+
|
92
|
+
:param artifact_id: The ID of the artifact to rebuild.
|
93
|
+
:return: A `RebuildArtifactResponse` object containing information about the rebuilt artifact.
|
94
|
+
:rtype: RebuildArtifactResponse
|
95
|
+
:raises ValueError: If `artifact_id` is None or empty
|
96
|
+
"""
|
97
|
+
self._validate_artifact_id(artifact_id)
|
98
|
+
|
99
|
+
return self.artifact_client.rebuild_artifact(artifact_id)
|
100
|
+
|
101
|
+
def delete_artifact(self, artifact_id: str) -> DeleteArtifactResponse:
|
102
|
+
"""
|
103
|
+
Delete an existing artifact.
|
104
|
+
|
105
|
+
:param artifact_id: The ID of the artifact to delete.
|
106
|
+
:return: A `DeleteArtifactResponse` object containing information about the deleted artifact.
|
107
|
+
:rtype: DeleteArtifactResponse
|
108
|
+
:raises ValueError: If `artifact_id` is None or empty
|
109
|
+
"""
|
110
|
+
self._validate_artifact_id(artifact_id)
|
111
|
+
|
112
|
+
return self.artifact_client.delete_artifact(artifact_id)
|
113
|
+
|
114
|
+
def upload_artifact_file(self, upload_link: str, artifact_file_path: str) -> None:
|
115
|
+
"""
|
116
|
+
Upload a file associated with an artifact.
|
117
|
+
|
118
|
+
:param upload_link: The URL to upload the artifact file.
|
119
|
+
:param artifact_file_path: The path to the artifact file.
|
120
|
+
:raises ValueError: If `file_path` is None or empty.
|
121
|
+
:raises FileNotFoundError: If the provided `file_path` does not exist.
|
122
|
+
"""
|
123
|
+
self._validate_artifact_file_path(artifact_file_path)
|
124
|
+
artifact_file_type = mimetypes.guess_type(artifact_file_path)[0]
|
125
|
+
|
126
|
+
FileUploadClient.upload_small_file(upload_link, artifact_file_path, artifact_file_type)
|
127
|
+
|
128
|
+
def create_artifact_with_file(
|
129
|
+
self,
|
130
|
+
artifact_name: str,
|
131
|
+
artifact_file_path: str,
|
132
|
+
description: Optional[str] = "",
|
133
|
+
tags: Optional[List[str]] = None
|
134
|
+
) -> str:
|
135
|
+
"""
|
136
|
+
Create a new artifact for a user and upload a file associated with the artifact.
|
137
|
+
|
138
|
+
:param artifact_name: The name of the artifact.
|
139
|
+
:param artifact_file_path: The path to the artifact file(Dockerfile+serve.py).
|
140
|
+
:param description: An optional description for the artifact.
|
141
|
+
:param tags: Optional tags associated with the artifact, as a comma-separated string.
|
142
|
+
:return: The `artifact_id` of the created artifact.
|
143
|
+
:rtype: str
|
144
|
+
:raises FileNotFoundError: If the provided `file_path` does not exist.
|
145
|
+
"""
|
146
|
+
self._validate_artifact_file_path(artifact_file_path)
|
147
|
+
|
148
|
+
# Create the artifact
|
149
|
+
create_artifact_resp = self.create_artifact(artifact_name, description, tags)
|
150
|
+
artifact_id = create_artifact_resp.artifact_id
|
151
|
+
|
152
|
+
artifact_file_type = mimetypes.guess_type(artifact_file_path)[0]
|
153
|
+
FileUploadClient.upload_small_file(create_artifact_resp.upload_link, artifact_file_path, artifact_file_type)
|
154
|
+
|
155
|
+
return artifact_id
|
156
|
+
|
157
|
+
def get_bigfile_upload_url(self, artifact_id: str, model_file_path: str) -> str:
|
158
|
+
"""
|
159
|
+
Generate a pre-signed URL for uploading a large file associated with an artifact.
|
160
|
+
|
161
|
+
:param artifact_id: The ID of the artifact for which the file is being uploaded.
|
162
|
+
:param model_file_path: The path to the model file.
|
163
|
+
:return: The pre-signed upload URL for the large file.
|
164
|
+
:rtype: str
|
165
|
+
:raises ValueError: If `artifact_id` is None or empty.
|
166
|
+
"""
|
167
|
+
self._validate_artifact_id(artifact_id)
|
168
|
+
self._validate_file_path(model_file_path)
|
169
|
+
|
170
|
+
model_file_name = os.path.basename(model_file_path)
|
171
|
+
model_file_type = mimetypes.guess_type(model_file_path)[0]
|
172
|
+
|
173
|
+
req = GetBigFileUploadUrlRequest(artifact_id=artifact_id, file_name=model_file_name, file_type=model_file_type)
|
174
|
+
|
175
|
+
return self.artifact_client.get_bigfile_upload_url(req).upload_link
|
176
|
+
|
177
|
+
def delete_bigfile(self, artifact_id: str, file_name: str) -> str:
|
178
|
+
"""
|
179
|
+
Delete a large file associated with an artifact.
|
180
|
+
|
181
|
+
:param artifact_id: The ID of the artifact for which the file is being deleted.
|
182
|
+
:param file_name: The name of the file being deleted.
|
183
|
+
"""
|
184
|
+
self._validate_artifact_id(artifact_id)
|
185
|
+
self._validate_file_name(file_name)
|
186
|
+
|
187
|
+
return self.artifact_client.delete_bigfile(artifact_id, file_name).status
|
188
|
+
|
189
|
+
def upload_large_file(self, upload_link: str, file_path: str) -> None:
|
190
|
+
"""
|
191
|
+
Upload a large file to the specified URL.
|
192
|
+
|
193
|
+
:param upload_link: The URL to upload the file.
|
194
|
+
:param file_path: The path to the file to upload.
|
195
|
+
:raises ValueError: If `file_path` is None or empty.
|
196
|
+
:raises ValueError: If `upload_link` is None or empty.
|
197
|
+
:raises FileNotFoundError: If the provided `file_path` does not exist.
|
198
|
+
"""
|
199
|
+
self._validate_file_path(file_path)
|
200
|
+
self._validate_upload_url(upload_link)
|
201
|
+
|
202
|
+
FileUploadClient.upload_large_file(upload_link, file_path)
|
203
|
+
|
204
|
+
def create_artifact_with_model_files(
|
205
|
+
self,
|
206
|
+
artifact_name: str,
|
207
|
+
artifact_file_path: str,
|
208
|
+
model_file_paths: List[str],
|
209
|
+
description: Optional[str] = "",
|
210
|
+
tags: Optional[str] = None
|
211
|
+
) -> str:
|
212
|
+
"""
|
213
|
+
Create a new artifact for a user and upload model files associated with the artifact.
|
214
|
+
|
215
|
+
:param artifact_name: The name of the artifact.
|
216
|
+
:param artifact_file_path: The path to the artifact file(Dockerfile+serve.py).
|
217
|
+
:param model_file_paths: The paths to the model files.
|
218
|
+
:param description: An optional description for the artifact.
|
219
|
+
:param tags: Optional tags associated with the artifact, as a comma-separated string.
|
220
|
+
:return: The `artifact_id` of the created artifact.
|
221
|
+
:raises FileNotFoundError: If the provided `file_path` does not exist.
|
222
|
+
"""
|
223
|
+
artifact_id = self.create_artifact_with_file(artifact_name, artifact_file_path, description, tags)
|
224
|
+
|
225
|
+
for model_file_path in model_file_paths:
|
226
|
+
self._validate_file_path(model_file_path)
|
227
|
+
bigfile_upload_url_resp = self.artifact_client.get_bigfile_upload_url(
|
228
|
+
GetBigFileUploadUrlRequest(artifact_id=artifact_id, model_file_path=model_file_path)
|
229
|
+
)
|
230
|
+
FileUploadClient.upload_large_file(bigfile_upload_url_resp.upload_link, model_file_path)
|
231
|
+
|
232
|
+
return artifact_id
|
233
|
+
|
234
|
+
def get_artifact_templates(self) -> List[ArtifactTemplate]:
|
235
|
+
"""
|
236
|
+
Fetch all artifact templates.
|
237
|
+
|
238
|
+
:return: A list of ArtifactTemplate objects.
|
239
|
+
:rtype: List[ArtifactTemplate]
|
240
|
+
"""
|
241
|
+
return self.artifact_client.get_artifact_templates().artifact_templates
|
242
|
+
|
243
|
+
@staticmethod
|
244
|
+
def _validate_file_name(file_name: str) -> None:
|
245
|
+
"""
|
246
|
+
Validate the file name.
|
247
|
+
|
248
|
+
:param file_name: The file name to validate.
|
249
|
+
:raises ValueError: If `file_name` is None or empty.
|
250
|
+
"""
|
251
|
+
if not file_name or not file_name.strip():
|
252
|
+
raise ValueError("File name is required and cannot be empty.")
|
253
|
+
|
254
|
+
@staticmethod
|
255
|
+
def _validate_artifact_id(artifact_id: str) -> None:
|
256
|
+
"""
|
257
|
+
Validate the artifact ID.
|
258
|
+
|
259
|
+
:param artifact_id: The artifact ID to validate.
|
260
|
+
:raises ValueError: If `artifact_id` is None or empty.
|
261
|
+
"""
|
262
|
+
if not artifact_id or not artifact_id.strip():
|
263
|
+
raise ValueError("Artifact ID is required and cannot be empty.")
|
264
|
+
|
265
|
+
@staticmethod
|
266
|
+
def _validate_artifact_file_path(artifact_file_path: str) -> None:
|
267
|
+
"""
|
268
|
+
Validate the artifact file path.
|
269
|
+
|
270
|
+
:param artifact_file_path: The file path to validate.
|
271
|
+
:raises ValueError: If `file_path` is None or empty.
|
272
|
+
:raises FileNotFoundError: If the provided `file_path` does not exist.
|
273
|
+
"""
|
274
|
+
ArtifactManager._validate_file_path(artifact_file_path)
|
275
|
+
|
276
|
+
file_type = mimetypes.guess_type(artifact_file_path)[0]
|
277
|
+
if file_type != "application/zip":
|
278
|
+
raise ValueError("File type must be application/zip.")
|
279
|
+
|
280
|
+
@staticmethod
|
281
|
+
def _validate_upload_url(upload_link: str) -> None:
|
282
|
+
"""
|
283
|
+
Validate the upload URL.
|
284
|
+
|
285
|
+
:param upload_link: The upload URL to validate.
|
286
|
+
:raises ValueError: If `upload_link` is None or empty.
|
287
|
+
"""
|
288
|
+
if not upload_link or not upload_link.strip():
|
289
|
+
raise ValueError("Upload link is required and cannot be empty.")
|
290
|
+
|
291
|
+
@staticmethod
|
292
|
+
def _validate_file_path(file_path: str) -> None:
|
293
|
+
"""
|
294
|
+
Validate the file path.
|
295
|
+
|
296
|
+
:param file_path: The file path to validate.
|
297
|
+
:raises ValueError: If `file_path` is None or empty.
|
298
|
+
:raises FileNotFoundError: If the provided `file_path` does not exist.
|
299
|
+
"""
|
300
|
+
if not file_path or not file_path.strip():
|
301
|
+
raise ValueError("File path is required and cannot be empty.")
|
302
|
+
if not os.path.exists(file_path):
|
303
|
+
raise FileNotFoundError(f"File not found: {file_path}")
|