gmicloud 0.1.2__tar.gz → 0.1.4__tar.gz

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.
Files changed (31) hide show
  1. {gmicloud-0.1.2 → gmicloud-0.1.4}/PKG-INFO +83 -45
  2. {gmicloud-0.1.2 → gmicloud-0.1.4}/README.md +83 -45
  3. {gmicloud-0.1.2 → gmicloud-0.1.4}/gmicloud/_internal/_client/_artifact_client.py +11 -46
  4. {gmicloud-0.1.2 → gmicloud-0.1.4}/gmicloud/_internal/_client/_http_client.py +1 -1
  5. {gmicloud-0.1.2 → gmicloud-0.1.4}/gmicloud/_internal/_client/_iam_client.py +40 -8
  6. {gmicloud-0.1.2 → gmicloud-0.1.4}/gmicloud/_internal/_client/_task_client.py +8 -42
  7. {gmicloud-0.1.2 → gmicloud-0.1.4}/gmicloud/_internal/_constants.py +1 -0
  8. {gmicloud-0.1.2 → gmicloud-0.1.4}/gmicloud/_internal/_models.py +26 -1
  9. {gmicloud-0.1.2 → gmicloud-0.1.4}/gmicloud.egg-info/PKG-INFO +83 -45
  10. {gmicloud-0.1.2 → gmicloud-0.1.4}/pyproject.toml +1 -1
  11. {gmicloud-0.1.2 → gmicloud-0.1.4}/examples/__init__.py +0 -0
  12. {gmicloud-0.1.2 → gmicloud-0.1.4}/gmicloud/__init__.py +0 -0
  13. {gmicloud-0.1.2 → gmicloud-0.1.4}/gmicloud/_internal/__init__.py +0 -0
  14. {gmicloud-0.1.2 → gmicloud-0.1.4}/gmicloud/_internal/_client/__init__.py +0 -0
  15. {gmicloud-0.1.2 → gmicloud-0.1.4}/gmicloud/_internal/_client/_decorator.py +0 -0
  16. {gmicloud-0.1.2 → gmicloud-0.1.4}/gmicloud/_internal/_client/_file_upload_client.py +0 -0
  17. {gmicloud-0.1.2 → gmicloud-0.1.4}/gmicloud/_internal/_config.py +0 -0
  18. {gmicloud-0.1.2 → gmicloud-0.1.4}/gmicloud/_internal/_enums.py +0 -0
  19. {gmicloud-0.1.2 → gmicloud-0.1.4}/gmicloud/_internal/_exceptions.py +0 -0
  20. {gmicloud-0.1.2 → gmicloud-0.1.4}/gmicloud/_internal/_manager/__init__.py +0 -0
  21. {gmicloud-0.1.2 → gmicloud-0.1.4}/gmicloud/_internal/_manager/_artifact_manager.py +0 -0
  22. {gmicloud-0.1.2 → gmicloud-0.1.4}/gmicloud/_internal/_manager/_task_manager.py +0 -0
  23. {gmicloud-0.1.2 → gmicloud-0.1.4}/gmicloud/client.py +0 -0
  24. {gmicloud-0.1.2 → gmicloud-0.1.4}/gmicloud/tests/__init__.py +0 -0
  25. {gmicloud-0.1.2 → gmicloud-0.1.4}/gmicloud/tests/test_artifacts.py +0 -0
  26. {gmicloud-0.1.2 → gmicloud-0.1.4}/gmicloud/tests/test_tasks.py +0 -0
  27. {gmicloud-0.1.2 → gmicloud-0.1.4}/gmicloud/utils/uninstall_packages.py +0 -0
  28. {gmicloud-0.1.2 → gmicloud-0.1.4}/gmicloud.egg-info/SOURCES.txt +0 -0
  29. {gmicloud-0.1.2 → gmicloud-0.1.4}/gmicloud.egg-info/dependency_links.txt +0 -0
  30. {gmicloud-0.1.2 → gmicloud-0.1.4}/gmicloud.egg-info/top_level.txt +0 -0
  31. {gmicloud-0.1.2 → gmicloud-0.1.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: gmicloud
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: GMI Cloud Python SDK
5
5
  Author-email: GMI <gmi@gmitec.net>
6
6
  License: MIT
@@ -10,10 +10,13 @@ Classifier: Operating System :: OS Independent
10
10
  Requires-Python: >=3.6
11
11
  Description-Content-Type: text/markdown
12
12
 
13
- # GMICloud SDK
13
+ # GMICloud SDK (Beta)
14
14
 
15
15
  ## Overview
16
16
 
17
+ Before you start: Our service and GPU resource is currenly invite-only so please contact our team (
18
+ getstarted@gmicloud.ai) to get invited if you don't have one yet.
19
+
17
20
  The GMI Inference Engine SDK provides a Python interface for deploying and managing machine learning models in
18
21
  production environments. It allows users to create model artifacts, schedule tasks for serving models, and call
19
22
  inference APIs easily.
@@ -62,7 +65,18 @@ client = Client(client_id="<YOUR_CLIENT_ID>", email="<YOUR_EMAIL>", password="<Y
62
65
 
63
66
  ## Quick Start
64
67
 
65
- ### 1. Create a Task from an Artifact Template
68
+ ### 1. How to run the code in the example folder
69
+ ```bash
70
+ cd path/to/gmicloud-sdk
71
+ # Create a virtual environment
72
+ python -m venv venv
73
+ source venv/bin/activate
74
+
75
+ pip install -r requirements.txt
76
+ python -m examples.<example_name>
77
+ ```
78
+
79
+ ### 2. Create a Task from an Artifact Template
66
80
 
67
81
  This is the simplest example to deploy an existing artifact template:
68
82
 
@@ -91,24 +105,30 @@ response = call_chat_completion(client, task.task_id)
91
105
  print(response)
92
106
  ```
93
107
 
94
- ### 2. Step-by-Step Example: Create Artifact, Task, and Query the Endpoint
108
+ ### 3. Step-by-Step Example: Create Artifact, Task, and Query the Endpoint
95
109
 
96
110
  #### (a) Create an Artifact from a Template
97
111
 
98
112
  First, you’ll retrieve all templates and create an artifact based on the desired template (e.g., "Llama3.1 8B"):
99
113
 
100
114
  ```python
101
- def create_artifact_from_template(client):
115
+ from gmicloud import *
116
+
117
+ def create_artifact_from_template(client: Client) -> str:
102
118
  artifact_manager = client.artifact_manager
103
119
 
104
- # List all available templates
120
+ # Get all artifact templates
105
121
  templates = artifact_manager.get_artifact_templates()
106
122
  for template in templates:
107
123
  if template.artifact_template_id == "qwen_2.5_14b_instruct_template_001":
108
- return artifact_manager.create_artifact_from_template(
109
- artifact_template_id=template.artifact_template_id
124
+ # Create an artifact from a template
125
+ artifact_id = artifact_manager.create_artifact_from_template(
126
+ artifact_template_id=template.artifact_template_id,
110
127
  )
111
- return None
128
+
129
+ return artifact_id
130
+
131
+ return ""
112
132
  ```
113
133
 
114
134
  #### (b) Create a Task from the Artifact
@@ -116,43 +136,55 @@ def create_artifact_from_template(client):
116
136
  Wait until the artifact becomes "ready" and then deploy it using task scheduling:
117
137
 
118
138
  ```python
119
- def create_task_and_start(client, artifact_id):
120
- artifact_manager = client.artifact_manager
139
+ from gmicloud import *
140
+ import time
141
+ from datetime import datetime
121
142
 
122
- # Wait until the artifact is ready
143
+ def create_task_and_start(client: Client, artifact_id: str) -> str:
144
+ artifact_manager = client.artifact_manager
145
+ # Wait for the artifact to be ready
123
146
  while True:
124
- artifact = artifact_manager.get_artifact(artifact_id)
125
- if artifact.build_status == "SUCCESS":
126
- break
127
- print("Waiting for artifact to be ready...")
147
+ try:
148
+ artifact = artifact_manager.get_artifact(artifact_id)
149
+ print(f"Artifact status: {artifact.build_status}")
150
+ # Wait until the artifact is ready
151
+ if artifact.build_status == BuildStatus.SUCCESS:
152
+ break
153
+ except Exception as e:
154
+ raise e
155
+ # Wait for 2 seconds
128
156
  time.sleep(2)
129
-
130
- # Configure and start the task
131
- task_manager = client.task_manager
132
- task = task_manager.create_task(Task(
133
- config=TaskConfig(
134
- ray_task_config=RayTaskConfig(
135
- ray_version="2.40.0-py310-gpu",
136
- file_path="serve",
137
- artifact_id=artifact_id,
138
- deployment_name="app",
139
- replica_resource=ReplicaResource(
140
- cpu=10,
141
- ram_gb=100,
142
- gpu=1,
157
+ try:
158
+ task_manager = client.task_manager
159
+ # Create a task
160
+ task = task_manager.create_task(Task(
161
+ config=TaskConfig(
162
+ ray_task_config=RayTaskConfig(
163
+ ray_version="2.40.0-py310-gpu",
164
+ file_path="serve",
165
+ artifact_id=artifact_id,
166
+ deployment_name="app",
167
+ replica_resource=ReplicaResource(
168
+ cpu=10,
169
+ ram_gb=100,
170
+ gpu=1,
171
+ ),
172
+ ),
173
+ task_scheduling=TaskScheduling(
174
+ scheduling_oneoff=OneOffScheduling(
175
+ trigger_timestamp=int(datetime.now().timestamp()) + 10,
176
+ min_replicas=1,
177
+ max_replicas=10,
178
+ )
143
179
  ),
144
180
  ),
145
- task_scheduling=TaskScheduling(
146
- scheduling_oneoff=OneOffScheduling(
147
- trigger_timestamp=int(datetime.now().timestamp()) + 10,
148
- min_replicas=1,
149
- max_replicas=10,
150
- )
151
- ),
152
- ),
153
- ))
181
+ ))
182
+
183
+ # Start the task
184
+ task_manager.start_task(task.task_id)
185
+ except Exception as e:
186
+ raise e
154
187
 
155
- task_manager.start_task(task.task_id)
156
188
  return task.task_id
157
189
  ```
158
190
 
@@ -161,14 +193,20 @@ def create_task_and_start(client, artifact_id):
161
193
  Once the task is running, use the endpoint for inference:
162
194
 
163
195
  ```python
196
+ from gmicloud import *
164
197
  from examples.completion import call_chat_completion
165
198
 
166
- client = Client()
167
- artifact_id = create_artifact_from_template(client)
168
- task_id = create_task_and_start(client, artifact_id)
199
+ # Initialize the Client
200
+ cli = Client()
169
201
 
170
- response = call_chat_completion(client, task_id)
171
- print(response)
202
+ # Create an artifact from a template
203
+ artifact_id = create_artifact_from_template(cli)
204
+
205
+ # Create a task and start it
206
+ task_id = create_task_and_start(cli, artifact_id)
207
+
208
+ # Call chat completion
209
+ print(call_chat_completion(cli, task_id))
172
210
  ```
173
211
 
174
212
  ## API Reference
@@ -1,7 +1,10 @@
1
- # GMICloud SDK
1
+ # GMICloud SDK (Beta)
2
2
 
3
3
  ## Overview
4
4
 
5
+ Before you start: Our service and GPU resource is currenly invite-only so please contact our team (
6
+ getstarted@gmicloud.ai) to get invited if you don't have one yet.
7
+
5
8
  The GMI Inference Engine SDK provides a Python interface for deploying and managing machine learning models in
6
9
  production environments. It allows users to create model artifacts, schedule tasks for serving models, and call
7
10
  inference APIs easily.
@@ -50,7 +53,18 @@ client = Client(client_id="<YOUR_CLIENT_ID>", email="<YOUR_EMAIL>", password="<Y
50
53
 
51
54
  ## Quick Start
52
55
 
53
- ### 1. Create a Task from an Artifact Template
56
+ ### 1. How to run the code in the example folder
57
+ ```bash
58
+ cd path/to/gmicloud-sdk
59
+ # Create a virtual environment
60
+ python -m venv venv
61
+ source venv/bin/activate
62
+
63
+ pip install -r requirements.txt
64
+ python -m examples.<example_name>
65
+ ```
66
+
67
+ ### 2. Create a Task from an Artifact Template
54
68
 
55
69
  This is the simplest example to deploy an existing artifact template:
56
70
 
@@ -79,24 +93,30 @@ response = call_chat_completion(client, task.task_id)
79
93
  print(response)
80
94
  ```
81
95
 
82
- ### 2. Step-by-Step Example: Create Artifact, Task, and Query the Endpoint
96
+ ### 3. Step-by-Step Example: Create Artifact, Task, and Query the Endpoint
83
97
 
84
98
  #### (a) Create an Artifact from a Template
85
99
 
86
100
  First, you’ll retrieve all templates and create an artifact based on the desired template (e.g., "Llama3.1 8B"):
87
101
 
88
102
  ```python
89
- def create_artifact_from_template(client):
103
+ from gmicloud import *
104
+
105
+ def create_artifact_from_template(client: Client) -> str:
90
106
  artifact_manager = client.artifact_manager
91
107
 
92
- # List all available templates
108
+ # Get all artifact templates
93
109
  templates = artifact_manager.get_artifact_templates()
94
110
  for template in templates:
95
111
  if template.artifact_template_id == "qwen_2.5_14b_instruct_template_001":
96
- return artifact_manager.create_artifact_from_template(
97
- artifact_template_id=template.artifact_template_id
112
+ # Create an artifact from a template
113
+ artifact_id = artifact_manager.create_artifact_from_template(
114
+ artifact_template_id=template.artifact_template_id,
98
115
  )
99
- return None
116
+
117
+ return artifact_id
118
+
119
+ return ""
100
120
  ```
101
121
 
102
122
  #### (b) Create a Task from the Artifact
@@ -104,43 +124,55 @@ def create_artifact_from_template(client):
104
124
  Wait until the artifact becomes "ready" and then deploy it using task scheduling:
105
125
 
106
126
  ```python
107
- def create_task_and_start(client, artifact_id):
108
- artifact_manager = client.artifact_manager
127
+ from gmicloud import *
128
+ import time
129
+ from datetime import datetime
109
130
 
110
- # Wait until the artifact is ready
131
+ def create_task_and_start(client: Client, artifact_id: str) -> str:
132
+ artifact_manager = client.artifact_manager
133
+ # Wait for the artifact to be ready
111
134
  while True:
112
- artifact = artifact_manager.get_artifact(artifact_id)
113
- if artifact.build_status == "SUCCESS":
114
- break
115
- print("Waiting for artifact to be ready...")
135
+ try:
136
+ artifact = artifact_manager.get_artifact(artifact_id)
137
+ print(f"Artifact status: {artifact.build_status}")
138
+ # Wait until the artifact is ready
139
+ if artifact.build_status == BuildStatus.SUCCESS:
140
+ break
141
+ except Exception as e:
142
+ raise e
143
+ # Wait for 2 seconds
116
144
  time.sleep(2)
117
-
118
- # Configure and start the task
119
- task_manager = client.task_manager
120
- task = task_manager.create_task(Task(
121
- config=TaskConfig(
122
- ray_task_config=RayTaskConfig(
123
- ray_version="2.40.0-py310-gpu",
124
- file_path="serve",
125
- artifact_id=artifact_id,
126
- deployment_name="app",
127
- replica_resource=ReplicaResource(
128
- cpu=10,
129
- ram_gb=100,
130
- gpu=1,
145
+ try:
146
+ task_manager = client.task_manager
147
+ # Create a task
148
+ task = task_manager.create_task(Task(
149
+ config=TaskConfig(
150
+ ray_task_config=RayTaskConfig(
151
+ ray_version="2.40.0-py310-gpu",
152
+ file_path="serve",
153
+ artifact_id=artifact_id,
154
+ deployment_name="app",
155
+ replica_resource=ReplicaResource(
156
+ cpu=10,
157
+ ram_gb=100,
158
+ gpu=1,
159
+ ),
160
+ ),
161
+ task_scheduling=TaskScheduling(
162
+ scheduling_oneoff=OneOffScheduling(
163
+ trigger_timestamp=int(datetime.now().timestamp()) + 10,
164
+ min_replicas=1,
165
+ max_replicas=10,
166
+ )
131
167
  ),
132
168
  ),
133
- task_scheduling=TaskScheduling(
134
- scheduling_oneoff=OneOffScheduling(
135
- trigger_timestamp=int(datetime.now().timestamp()) + 10,
136
- min_replicas=1,
137
- max_replicas=10,
138
- )
139
- ),
140
- ),
141
- ))
169
+ ))
170
+
171
+ # Start the task
172
+ task_manager.start_task(task.task_id)
173
+ except Exception as e:
174
+ raise e
142
175
 
143
- task_manager.start_task(task.task_id)
144
176
  return task.task_id
145
177
  ```
146
178
 
@@ -149,14 +181,20 @@ def create_task_and_start(client, artifact_id):
149
181
  Once the task is running, use the endpoint for inference:
150
182
 
151
183
  ```python
184
+ from gmicloud import *
152
185
  from examples.completion import call_chat_completion
153
186
 
154
- client = Client()
155
- artifact_id = create_artifact_from_template(client)
156
- task_id = create_task_and_start(client, artifact_id)
187
+ # Initialize the Client
188
+ cli = Client()
157
189
 
158
- response = call_chat_completion(client, task_id)
159
- print(response)
190
+ # Create an artifact from a template
191
+ artifact_id = create_artifact_from_template(cli)
192
+
193
+ # Create a task and start it
194
+ task_id = create_task_and_start(cli, artifact_id)
195
+
196
+ # Call chat completion
197
+ print(call_chat_completion(cli, task_id))
160
198
  ```
161
199
 
162
200
  ## API Reference
@@ -197,4 +235,4 @@ We welcome contributions to enhance the SDK. Please follow these steps:
197
235
  1. Fork the repository.
198
236
  2. Create a new branch for your feature or bugfix.
199
237
  3. Commit changes with clear messages.
200
- 4. Submit a pull request for review.
238
+ 4. Submit a pull request for review.
@@ -5,7 +5,6 @@ from ._iam_client import IAMClient
5
5
  from ._decorator import handle_refresh_token
6
6
  from .._models import *
7
7
  from .._config import ARTIFACT_SERVICE_BASE_URL
8
- from .._constants import ACCESS_TOKEN_HEADER, CLIENT_ID_HEADER
9
8
 
10
9
 
11
10
  class ArtifactClient:
@@ -33,11 +32,7 @@ class ArtifactClient:
33
32
  :return: The Artifact object.
34
33
  :rtype: Artifact
35
34
  """
36
- custom_headers = {
37
- ACCESS_TOKEN_HEADER: self.iam_client.get_access_token(),
38
- CLIENT_ID_HEADER: self.iam_client.get_client_id()
39
- }
40
- result = self.client.get(f"/get_artifact", custom_headers, {"artifact_id": artifact_id})
35
+ result = self.client.get(f"/get_artifact", self.iam_client.get_custom_headers(), {"artifact_id": artifact_id})
41
36
 
42
37
  return Artifact.model_validate(result)
43
38
 
@@ -49,11 +44,7 @@ class ArtifactClient:
49
44
  :return: A list of Artifact objects.
50
45
  :rtype: List[Artifact]
51
46
  """
52
- custom_headers = {
53
- ACCESS_TOKEN_HEADER: self.iam_client.get_access_token(),
54
- CLIENT_ID_HEADER: self.iam_client.get_client_id()
55
- }
56
- result = self.client.get("/get_all_artifacts", custom_headers)
47
+ result = self.client.get("/get_all_artifacts", self.iam_client.get_custom_headers())
57
48
  if not result:
58
49
  return []
59
50
  return [Artifact.model_validate(item) for item in result]
@@ -67,11 +58,7 @@ class ArtifactClient:
67
58
  :return: The response object containing the created artifact details.
68
59
  :rtype: CreateArtifactResponse
69
60
  """
70
- custom_headers = {
71
- ACCESS_TOKEN_HEADER: self.iam_client.get_access_token(),
72
- CLIENT_ID_HEADER: self.iam_client.get_client_id()
73
- }
74
- result = self.client.post("/create_artifact", custom_headers, request.model_dump())
61
+ result = self.client.post("/create_artifact", self.iam_client.get_custom_headers(), request.model_dump())
75
62
 
76
63
  return CreateArtifactResponse.model_validate(result)
77
64
 
@@ -84,11 +71,7 @@ class ArtifactClient:
84
71
  :return: The response object containing the created artifact details.
85
72
  :rtype: CreateArtifactFromTemplateResponse
86
73
  """
87
- custom_headers = {
88
- ACCESS_TOKEN_HEADER: self.iam_client.get_access_token(),
89
- CLIENT_ID_HEADER: self.iam_client.get_client_id()
90
- }
91
- result = self.client.post("/create_artifact_from_template", custom_headers,
74
+ result = self.client.post("/create_artifact_from_template", self.iam_client.get_custom_headers(),
92
75
  {"artifact_template_id": artifact_template_id})
93
76
 
94
77
  return CreateArtifactFromTemplateResponse.model_validate(result)
@@ -102,11 +85,8 @@ class ArtifactClient:
102
85
  :return: The response object containing the rebuilt artifact details.
103
86
  :rtype: RebuildArtifactResponse
104
87
  """
105
- custom_headers = {
106
- ACCESS_TOKEN_HEADER: self.iam_client.get_access_token(),
107
- CLIENT_ID_HEADER: self.iam_client.get_client_id()
108
- }
109
- result = self.client.post("/rebuild_artifact", custom_headers, {"artifact_id": artifact_id})
88
+ result = self.client.post("/rebuild_artifact", self.iam_client.get_custom_headers(),
89
+ {"artifact_id": artifact_id})
110
90
 
111
91
  return CreateArtifactResponse.model_validate(result)
112
92
 
@@ -119,11 +99,8 @@ class ArtifactClient:
119
99
  :return: The response object containing the deleted artifact details.
120
100
  :rtype: DeleteArtifactResponse
121
101
  """
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.delete("/delete_artifact", custom_headers, {"artifact_id": artifact_id})
102
+ result = self.client.delete("/delete_artifact", self.iam_client.get_custom_headers(),
103
+ {"artifact_id": artifact_id})
127
104
 
128
105
  return DeleteArtifactResponse.model_validate(result)
129
106
 
@@ -136,11 +113,7 @@ class ArtifactClient:
136
113
  :return: The response object containing the pre-signed URL and upload details.
137
114
  :rtype: GetBigFileUploadUrlResponse
138
115
  """
139
- custom_headers = {
140
- ACCESS_TOKEN_HEADER: self.iam_client.get_access_token(),
141
- CLIENT_ID_HEADER: self.iam_client.get_client_id()
142
- }
143
- result = self.client.post("/get_bigfile_upload_url", custom_headers, request.model_dump())
116
+ result = self.client.post("/get_bigfile_upload_url", self.iam_client.get_custom_headers(), request.model_dump())
144
117
 
145
118
  return GetBigFileUploadUrlResponse.model_validate(result)
146
119
 
@@ -153,11 +126,7 @@ class ArtifactClient:
153
126
  :return: The response object containing the deletion status.
154
127
  :rtype: DeleteBigfileResponse
155
128
  """
156
- custom_headers = {
157
- ACCESS_TOKEN_HEADER: self.iam_client.get_access_token(),
158
- CLIENT_ID_HEADER: self.iam_client.get_client_id()
159
- }
160
- result = self.client.delete("/delete_bigfile", custom_headers, request.dict())
129
+ result = self.client.delete("/delete_bigfile", self.iam_client.get_custom_headers(), request.dict())
161
130
 
162
131
  return DeleteBigfileResponse.model_validate(result)
163
132
 
@@ -169,9 +138,5 @@ class ArtifactClient:
169
138
  :return: A list of ArtifactTemplate objects.
170
139
  :rtype: List[ArtifactTemplate]
171
140
  """
172
- custom_headers = {
173
- ACCESS_TOKEN_HEADER: self.iam_client.get_access_token(),
174
- CLIENT_ID_HEADER: self.iam_client.get_client_id()
175
- }
176
- result = self.client.get("/get_artifact_templates", custom_headers)
141
+ result = self.client.get("/get_artifact_templates", self.iam_client.get_custom_headers())
177
142
  return GetArtifactTemplatesResponse.model_validate(result)
@@ -2,7 +2,6 @@ import requests
2
2
  from .._exceptions import APIError
3
3
  from .._exceptions import UnauthorizedError
4
4
  from .._constants import *
5
- from .._config import *
6
5
 
7
6
 
8
7
  class HTTPClient:
@@ -62,6 +61,7 @@ class HTTPClient:
62
61
  raise APIError(f"HTTP Request failed: {error_message}")
63
62
  # Raise for HTTP errors
64
63
  response.raise_for_status()
64
+ print(response.text)
65
65
 
66
66
  except requests.exceptions.RequestException as e:
67
67
  raise APIError(f"HTTP Request failed: {str(e)}")
@@ -3,7 +3,7 @@ import jwt
3
3
  from ._http_client import HTTPClient
4
4
  from .._config import IAM_SERVICE_BASE_URL
5
5
  from .._models import *
6
- from .._constants import CLIENT_ID_HEADER
6
+ from .._constants import CLIENT_ID_HEADER, AUTHORIZATION_HEADER
7
7
 
8
8
 
9
9
  class IAMClient:
@@ -34,12 +34,35 @@ class IAMClient:
34
34
  custom_headers = {
35
35
  CLIENT_ID_HEADER: self._client_id
36
36
  }
37
- req = LoginRequest(email=self._email, password=self._password)
38
- result = self.client.post("/me/sessions", custom_headers, req.model_dump())
37
+ req = AuthTokenRequest(email=self._email, password=self._password)
38
+ auth_tokens_result = self.client.post("/me/auth-tokens", custom_headers, req.model_dump())
39
+ auth_tokens_resp = AuthTokenResponse.model_validate(auth_tokens_result)
39
40
 
40
- resp = LoginResponse.model_validate(result)
41
- self._access_token = resp.accessToken
42
- self._refresh_token = resp.refreshToken
41
+ create_session_result = None
42
+ if auth_tokens_resp.is2FARequired:
43
+ max_attempts = 3
44
+ for attempt in range(max_attempts):
45
+ code = input(f"Attempt {attempt + 1}/{max_attempts}: Please enter the 2FA code: ")
46
+
47
+ create_session_req = CreateSessionRequest(
48
+ type="native", authToken=auth_tokens_resp.authToken, otpCode=code
49
+ )
50
+ try:
51
+ create_session_result = self.client.post(
52
+ "/me/sessions", custom_headers, create_session_req.model_dump()
53
+ )
54
+ break
55
+ except Exception as e:
56
+ print("Invalid 2FA code, please try again.")
57
+ if attempt == max_attempts - 1:
58
+ raise Exception("Failed to create session after 3 incorrect 2FA attempts.") from e
59
+ else:
60
+ create_session_req = CreateSessionRequest(type="native", authToken=auth_tokens_resp.authToken, otpCode=None)
61
+ create_session_result = self.client.post("/me/sessions", custom_headers, create_session_req.model_dump())
62
+
63
+ create_session_resp = CreateSessionResponse.model_validate(create_session_result)
64
+ self._access_token = create_session_resp.accessToken
65
+ self._refresh_token = create_session_resp.refreshToken
43
66
  self._user_id = self.parse_user_id()
44
67
 
45
68
  def refresh_token(self):
@@ -49,9 +72,9 @@ class IAMClient:
49
72
  custom_headers = {
50
73
  CLIENT_ID_HEADER: self._client_id
51
74
  }
52
- result = self.client.patch("/me/sessions", custom_headers, {"refreshToken": self.refresh_token})
75
+ result = self.client.patch("/me/sessions", custom_headers, {"refreshToken": self._refresh_token})
53
76
 
54
- resp = LoginResponse.model_validate(result)
77
+ resp = CreateSessionResponse.model_validate(result)
55
78
  self._access_token = resp.accessToken
56
79
  self._refresh_token = resp.refreshToken
57
80
 
@@ -90,3 +113,12 @@ class IAMClient:
90
113
  Gets the current client ID.
91
114
  """
92
115
  return self._client_id
116
+
117
+ def get_custom_headers(self) -> dict:
118
+ """
119
+ Gets the custom headers for the IAM client.
120
+ """
121
+ return {
122
+ AUTHORIZATION_HEADER: f'Bearer {self._access_token}',
123
+ CLIENT_ID_HEADER: self._client_id
124
+ }
@@ -3,7 +3,6 @@ from ._decorator import handle_refresh_token
3
3
  from ._iam_client import IAMClient
4
4
  from .._config import TASK_SERVICE_BASE_URL
5
5
  from .._models import *
6
- from .._constants import ACCESS_TOKEN_HEADER, CLIENT_ID_HEADER
7
6
 
8
7
 
9
8
  class TaskClient:
@@ -30,11 +29,7 @@ class TaskClient:
30
29
  :return: An instance of Task containing the details of the retrieved task.
31
30
  :rtype: Task
32
31
  """
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})
32
+ result = self.client.get("/get_task", self.iam_client.get_custom_headers(), {"task_id": task_id})
38
33
 
39
34
  return Task.model_validate(result)
40
35
 
@@ -46,11 +41,7 @@ class TaskClient:
46
41
  :return: An instance of GetAllTasksResponse containing the retrieved tasks.
47
42
  :rtype: GetAllTasksResponse
48
43
  """
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)
44
+ result = self.client.get("/get_tasks", self.iam_client.get_custom_headers())
54
45
  if not result:
55
46
  return GetAllTasksResponse(tasks=[])
56
47
 
@@ -63,12 +54,7 @@ class TaskClient:
63
54
 
64
55
  :param task: The Task object containing the details of the task to be created.
65
56
  """
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())
57
+ result = self.client.post("/create_task", self.iam_client.get_custom_headers(), task.model_dump())
72
58
 
73
59
  return CreateTaskResponse.model_validate(result)
74
60
 
@@ -79,11 +65,7 @@ class TaskClient:
79
65
 
80
66
  :param task: The Task object containing the updated task details.
81
67
  """
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())
68
+ self.client.put("/update_schedule", self.iam_client.get_custom_headers(), task.model_dump())
87
69
 
88
70
  @handle_refresh_token
89
71
  def start_task(self, task_id: str):
@@ -92,11 +74,7 @@ class TaskClient:
92
74
 
93
75
  :param task_id: The ID of the task to be started.
94
76
  """
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})
77
+ self.client.post("/start_task", self.iam_client.get_custom_headers(), {"task_id": task_id})
100
78
 
101
79
  @handle_refresh_token
102
80
  def stop_task(self, task_id: str):
@@ -105,11 +83,7 @@ class TaskClient:
105
83
 
106
84
  :param task_id: The ID of the task to be stopped.
107
85
  """
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})
86
+ self.client.post("/stop_task", self.iam_client.get_custom_headers(), {"task_id": task_id})
113
87
 
114
88
  @handle_refresh_token
115
89
  def get_usage_data(self, start_timestamp: str, end_timestamp: str) -> GetUsageDataResponse:
@@ -119,11 +93,7 @@ class TaskClient:
119
93
  :param start_timestamp: The start timestamp of the usage data.
120
94
  :param end_timestamp: The end timestamp of the usage data.
121
95
  """
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,
96
+ result = self.client.get("/get_usage_data", self.iam_client.get_custom_headers(),
127
97
  {"start_timestamp": start_timestamp, "end_timestamp": end_timestamp})
128
98
 
129
99
  return result
@@ -135,8 +105,4 @@ class TaskClient:
135
105
 
136
106
  :param task_id: The ID of the task to be archived.
137
107
  """
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})
108
+ self.client.post("/archive_task", self.iam_client.get_custom_headers(), {"task_id": task_id})
@@ -1,4 +1,5 @@
1
1
  ACCEPT_HEADER = 'Accept'
2
+ AUTHORIZATION_HEADER = 'Authorization'
2
3
  CONTENT_TYPE_HEADER = 'Content-Type'
3
4
  CLIENT_ID_HEADER = 'CE-ClientId'
4
5
  ACCESS_TOKEN_HEADER = 'CE-AccessToken'
@@ -307,10 +307,35 @@ class CreateTaskResponse(BaseModel):
307
307
  upload_link: str # URL to upload the task data.
308
308
 
309
309
 
310
- class LoginResponse(BaseModel):
310
+ class AuthTokenRequest(BaseModel):
311
+ """
312
+ Request object for user login.
313
+ """
314
+ email: str # User email.
315
+ password: str # User password.
316
+
317
+
318
+ class AuthTokenResponse(BaseModel):
311
319
  """
312
320
  Response object for user login.
313
321
  """
322
+ authToken: str # Access token for the user session.
323
+ is2FARequired: bool # Indicates if 2FA is required for the user.
324
+
325
+
326
+ class CreateSessionRequest(BaseModel):
327
+ """
328
+ Request object for creating a user session.
329
+ """
330
+ type: str # Type of the session (e.g., native).
331
+ authToken: str # Access token for the user session.
332
+ otpCode: Optional[str] # 2FA code for the user session.
333
+
334
+
335
+ class CreateSessionResponse(BaseModel):
336
+ """
337
+ Response object for creating a user session.
338
+ """
314
339
  accessToken: str # Access token for the user session.
315
340
  refreshToken: str # Refresh token for the user session.
316
341
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: gmicloud
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: GMI Cloud Python SDK
5
5
  Author-email: GMI <gmi@gmitec.net>
6
6
  License: MIT
@@ -10,10 +10,13 @@ Classifier: Operating System :: OS Independent
10
10
  Requires-Python: >=3.6
11
11
  Description-Content-Type: text/markdown
12
12
 
13
- # GMICloud SDK
13
+ # GMICloud SDK (Beta)
14
14
 
15
15
  ## Overview
16
16
 
17
+ Before you start: Our service and GPU resource is currenly invite-only so please contact our team (
18
+ getstarted@gmicloud.ai) to get invited if you don't have one yet.
19
+
17
20
  The GMI Inference Engine SDK provides a Python interface for deploying and managing machine learning models in
18
21
  production environments. It allows users to create model artifacts, schedule tasks for serving models, and call
19
22
  inference APIs easily.
@@ -62,7 +65,18 @@ client = Client(client_id="<YOUR_CLIENT_ID>", email="<YOUR_EMAIL>", password="<Y
62
65
 
63
66
  ## Quick Start
64
67
 
65
- ### 1. Create a Task from an Artifact Template
68
+ ### 1. How to run the code in the example folder
69
+ ```bash
70
+ cd path/to/gmicloud-sdk
71
+ # Create a virtual environment
72
+ python -m venv venv
73
+ source venv/bin/activate
74
+
75
+ pip install -r requirements.txt
76
+ python -m examples.<example_name>
77
+ ```
78
+
79
+ ### 2. Create a Task from an Artifact Template
66
80
 
67
81
  This is the simplest example to deploy an existing artifact template:
68
82
 
@@ -91,24 +105,30 @@ response = call_chat_completion(client, task.task_id)
91
105
  print(response)
92
106
  ```
93
107
 
94
- ### 2. Step-by-Step Example: Create Artifact, Task, and Query the Endpoint
108
+ ### 3. Step-by-Step Example: Create Artifact, Task, and Query the Endpoint
95
109
 
96
110
  #### (a) Create an Artifact from a Template
97
111
 
98
112
  First, you’ll retrieve all templates and create an artifact based on the desired template (e.g., "Llama3.1 8B"):
99
113
 
100
114
  ```python
101
- def create_artifact_from_template(client):
115
+ from gmicloud import *
116
+
117
+ def create_artifact_from_template(client: Client) -> str:
102
118
  artifact_manager = client.artifact_manager
103
119
 
104
- # List all available templates
120
+ # Get all artifact templates
105
121
  templates = artifact_manager.get_artifact_templates()
106
122
  for template in templates:
107
123
  if template.artifact_template_id == "qwen_2.5_14b_instruct_template_001":
108
- return artifact_manager.create_artifact_from_template(
109
- artifact_template_id=template.artifact_template_id
124
+ # Create an artifact from a template
125
+ artifact_id = artifact_manager.create_artifact_from_template(
126
+ artifact_template_id=template.artifact_template_id,
110
127
  )
111
- return None
128
+
129
+ return artifact_id
130
+
131
+ return ""
112
132
  ```
113
133
 
114
134
  #### (b) Create a Task from the Artifact
@@ -116,43 +136,55 @@ def create_artifact_from_template(client):
116
136
  Wait until the artifact becomes "ready" and then deploy it using task scheduling:
117
137
 
118
138
  ```python
119
- def create_task_and_start(client, artifact_id):
120
- artifact_manager = client.artifact_manager
139
+ from gmicloud import *
140
+ import time
141
+ from datetime import datetime
121
142
 
122
- # Wait until the artifact is ready
143
+ def create_task_and_start(client: Client, artifact_id: str) -> str:
144
+ artifact_manager = client.artifact_manager
145
+ # Wait for the artifact to be ready
123
146
  while True:
124
- artifact = artifact_manager.get_artifact(artifact_id)
125
- if artifact.build_status == "SUCCESS":
126
- break
127
- print("Waiting for artifact to be ready...")
147
+ try:
148
+ artifact = artifact_manager.get_artifact(artifact_id)
149
+ print(f"Artifact status: {artifact.build_status}")
150
+ # Wait until the artifact is ready
151
+ if artifact.build_status == BuildStatus.SUCCESS:
152
+ break
153
+ except Exception as e:
154
+ raise e
155
+ # Wait for 2 seconds
128
156
  time.sleep(2)
129
-
130
- # Configure and start the task
131
- task_manager = client.task_manager
132
- task = task_manager.create_task(Task(
133
- config=TaskConfig(
134
- ray_task_config=RayTaskConfig(
135
- ray_version="2.40.0-py310-gpu",
136
- file_path="serve",
137
- artifact_id=artifact_id,
138
- deployment_name="app",
139
- replica_resource=ReplicaResource(
140
- cpu=10,
141
- ram_gb=100,
142
- gpu=1,
157
+ try:
158
+ task_manager = client.task_manager
159
+ # Create a task
160
+ task = task_manager.create_task(Task(
161
+ config=TaskConfig(
162
+ ray_task_config=RayTaskConfig(
163
+ ray_version="2.40.0-py310-gpu",
164
+ file_path="serve",
165
+ artifact_id=artifact_id,
166
+ deployment_name="app",
167
+ replica_resource=ReplicaResource(
168
+ cpu=10,
169
+ ram_gb=100,
170
+ gpu=1,
171
+ ),
172
+ ),
173
+ task_scheduling=TaskScheduling(
174
+ scheduling_oneoff=OneOffScheduling(
175
+ trigger_timestamp=int(datetime.now().timestamp()) + 10,
176
+ min_replicas=1,
177
+ max_replicas=10,
178
+ )
143
179
  ),
144
180
  ),
145
- task_scheduling=TaskScheduling(
146
- scheduling_oneoff=OneOffScheduling(
147
- trigger_timestamp=int(datetime.now().timestamp()) + 10,
148
- min_replicas=1,
149
- max_replicas=10,
150
- )
151
- ),
152
- ),
153
- ))
181
+ ))
182
+
183
+ # Start the task
184
+ task_manager.start_task(task.task_id)
185
+ except Exception as e:
186
+ raise e
154
187
 
155
- task_manager.start_task(task.task_id)
156
188
  return task.task_id
157
189
  ```
158
190
 
@@ -161,14 +193,20 @@ def create_task_and_start(client, artifact_id):
161
193
  Once the task is running, use the endpoint for inference:
162
194
 
163
195
  ```python
196
+ from gmicloud import *
164
197
  from examples.completion import call_chat_completion
165
198
 
166
- client = Client()
167
- artifact_id = create_artifact_from_template(client)
168
- task_id = create_task_and_start(client, artifact_id)
199
+ # Initialize the Client
200
+ cli = Client()
169
201
 
170
- response = call_chat_completion(client, task_id)
171
- print(response)
202
+ # Create an artifact from a template
203
+ artifact_id = create_artifact_from_template(cli)
204
+
205
+ # Create a task and start it
206
+ task_id = create_task_and_start(cli, artifact_id)
207
+
208
+ # Call chat completion
209
+ print(call_chat_completion(cli, task_id))
172
210
  ```
173
211
 
174
212
  ## API Reference
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "gmicloud"
7
- version = "0.1.2"
7
+ version = "0.1.4"
8
8
  description = "GMI Cloud Python SDK"
9
9
  authors = [{ name = "GMI", email = "gmi@gmitec.net" }]
10
10
  license = { text = "MIT" }
File without changes
File without changes
File without changes
File without changes