mlops-python-sdk 1.0.0__py3-none-any.whl → 1.0.1__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.
Files changed (29) hide show
  1. mlops/__init__.py +3 -3
  2. mlops/api/client/api/storage/__init__.py +1 -0
  3. mlops/api/client/api/storage/get_storage_presign_download.py +175 -0
  4. mlops/api/client/api/storage/get_storage_presign_upload.py +175 -0
  5. mlops/api/client/api/tasks/cancel_task.py +14 -14
  6. mlops/api/client/api/tasks/delete_task.py +14 -14
  7. mlops/api/client/api/tasks/get_task.py +15 -15
  8. mlops/api/client/api/tasks/get_task_by_task_id.py +204 -0
  9. mlops/api/client/api/tasks/get_task_logs.py +300 -0
  10. mlops/api/client/api/tasks/list_tasks.py +14 -14
  11. mlops/api/client/models/__init__.py +16 -0
  12. mlops/api/client/models/get_storage_presign_download_response_200.py +60 -0
  13. mlops/api/client/models/get_storage_presign_upload_response_200.py +79 -0
  14. mlops/api/client/models/get_task_logs_direction.py +9 -0
  15. mlops/api/client/models/get_task_logs_log_type.py +10 -0
  16. mlops/api/client/models/log_pagination.py +90 -0
  17. mlops/api/client/models/task_log_entry.py +105 -0
  18. mlops/api/client/models/task_log_entry_log_type.py +9 -0
  19. mlops/api/client/models/task_logs_response.py +112 -0
  20. mlops/api/client/models/task_submit_request.py +6 -6
  21. mlops/connection_config.py +0 -7
  22. mlops/exceptions.py +10 -10
  23. mlops/task/__init__.py +1 -1
  24. mlops/task/client.py +11 -35
  25. mlops/task/task.py +152 -34
  26. {mlops_python_sdk-1.0.0.dist-info → mlops_python_sdk-1.0.1.dist-info}/METADATA +3 -12
  27. mlops_python_sdk-1.0.1.dist-info/RECORD +52 -0
  28. mlops_python_sdk-1.0.0.dist-info/RECORD +0 -39
  29. {mlops_python_sdk-1.0.0.dist-info → mlops_python_sdk-1.0.1.dist-info}/WHEEL +0 -0
@@ -19,7 +19,7 @@ class TaskSubmitRequest:
19
19
  """Task submission request
20
20
 
21
21
  Attributes:
22
- cluster_id (int): Slurm cluster ID to submit task to Example: 1.
22
+ cluster_name (str): Slurm cluster name to submit task to Example: slurm-prod.
23
23
  name (str): Task name Example: training-job.
24
24
  account (Union[None, Unset, str]): Account Example: research.
25
25
  command (Union[None, Unset, str]): Command to execute (alternative to script) Example: python train.py.
@@ -55,7 +55,7 @@ class TaskSubmitRequest:
55
55
  tres (Union[None, Unset, str]): Trackable resources string Example: cpu=4,mem=8G.
56
56
  """
57
57
 
58
- cluster_id: int
58
+ cluster_name: str
59
59
  name: str
60
60
  account: Union[None, Unset, str] = UNSET
61
61
  command: Union[None, Unset, str] = UNSET
@@ -91,7 +91,7 @@ class TaskSubmitRequest:
91
91
  def to_dict(self) -> dict[str, Any]:
92
92
  from ..models.task_submit_request_environment_type_0 import TaskSubmitRequestEnvironmentType0
93
93
 
94
- cluster_id = self.cluster_id
94
+ cluster_name = self.cluster_name
95
95
 
96
96
  name = self.name
97
97
 
@@ -269,7 +269,7 @@ class TaskSubmitRequest:
269
269
  field_dict.update(self.additional_properties)
270
270
  field_dict.update(
271
271
  {
272
- "cluster_id": cluster_id,
272
+ "cluster_name": cluster_name,
273
273
  "name": name,
274
274
  }
275
275
  )
@@ -340,7 +340,7 @@ class TaskSubmitRequest:
340
340
  from ..models.task_submit_request_environment_type_0 import TaskSubmitRequestEnvironmentType0
341
341
 
342
342
  d = dict(src_dict)
343
- cluster_id = d.pop("cluster_id")
343
+ cluster_name = d.pop("cluster_name")
344
344
 
345
345
  name = d.pop("name")
346
346
 
@@ -605,7 +605,7 @@ class TaskSubmitRequest:
605
605
  tres = _parse_tres(d.pop("tres", UNSET))
606
606
 
607
607
  task_submit_request = cls(
608
- cluster_id=cluster_id,
608
+ cluster_name=cluster_name,
609
609
  name=name,
610
610
  account=account,
611
611
  command=command,
@@ -29,10 +29,6 @@ class ConnectionConfig:
29
29
  def _api_key():
30
30
  return os.getenv('MLOPS_API_KEY')
31
31
 
32
- @staticmethod
33
- def _access_token():
34
- return os.getenv('MLOPS_ACCESS_TOKEN')
35
-
36
32
  @staticmethod
37
33
  def _api_path():
38
34
  return os.getenv('MLOPS_API_PATH', DEFAULT_API_PATH)
@@ -42,7 +38,6 @@ class ConnectionConfig:
42
38
  domain: Optional[str] = None,
43
39
  debug: Optional[bool] = None,
44
40
  api_key: Optional[str] = None,
45
- access_token: Optional[str] = None,
46
41
  request_timeout: Optional[float] = None,
47
42
  headers: Optional[Dict[str, str]] = None,
48
43
  proxy: Optional[ProxyTypes] = None,
@@ -51,11 +46,9 @@ class ConnectionConfig:
51
46
  self.domain = domain or ConnectionConfig._domain()
52
47
  self.debug = debug or ConnectionConfig._debug()
53
48
  self.api_key = api_key or ConnectionConfig._api_key()
54
- self.access_token = access_token or ConnectionConfig._access_token()
55
49
  self.headers = headers or {}
56
50
  self.proxy = proxy
57
51
  self.api_path = api_path or ConnectionConfig._api_path()
58
-
59
52
  self.request_timeout = ConnectionConfig._get_request_timeout(
60
53
  REQUEST_TIMEOUT,
61
54
  request_timeout,
mlops/exceptions.py CHANGED
@@ -10,17 +10,17 @@ def format_execution_timeout_error() -> Exception:
10
10
  )
11
11
 
12
12
 
13
- class XClientException(Exception):
13
+ class MLOpsException(Exception):
14
14
  """
15
- Base class for all XClient errors.
15
+ Base class for all MLOps errors.
16
16
 
17
- Raised when a general XClient exception occurs.
17
+ Raised when a general MLOps exception occurs.
18
18
  """
19
19
 
20
20
  pass
21
21
 
22
22
 
23
- class TimeoutException(XClientException):
23
+ class TimeoutException(MLOpsException):
24
24
  """
25
25
  Raised when a timeout occurs.
26
26
 
@@ -33,7 +33,7 @@ class TimeoutException(XClientException):
33
33
  pass
34
34
 
35
35
 
36
- class InvalidArgumentException(XClientException):
36
+ class InvalidArgumentException(MLOpsException):
37
37
  """
38
38
  Raised when an invalid argument is provided.
39
39
  """
@@ -41,7 +41,7 @@ class InvalidArgumentException(XClientException):
41
41
  pass
42
42
 
43
43
 
44
- class NotEnoughSpaceException(XClientException):
44
+ class NotEnoughSpaceException(MLOpsException):
45
45
  """
46
46
  Raised when there is not enough disk space.
47
47
  """
@@ -49,7 +49,7 @@ class NotEnoughSpaceException(XClientException):
49
49
  pass
50
50
 
51
51
 
52
- class NotFoundException(XClientException):
52
+ class NotFoundException(MLOpsException):
53
53
  """
54
54
  Raised when a resource is not found.
55
55
  """
@@ -57,7 +57,7 @@ class NotFoundException(XClientException):
57
57
  pass
58
58
 
59
59
 
60
- class AuthenticationException(XClientException):
60
+ class AuthenticationException(MLOpsException):
61
61
  """
62
62
  Raised when authentication fails.
63
63
  """
@@ -65,7 +65,7 @@ class AuthenticationException(XClientException):
65
65
  pass
66
66
 
67
67
 
68
- class RateLimitException(XClientException):
68
+ class RateLimitException(MLOpsException):
69
69
  """
70
70
  Raised when the API rate limit is exceeded.
71
71
  """
@@ -73,7 +73,7 @@ class RateLimitException(XClientException):
73
73
  pass
74
74
 
75
75
 
76
- class APIException(XClientException):
76
+ class APIException(MLOpsException):
77
77
  """
78
78
  Raised when an API error occurs.
79
79
  """
mlops/task/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- """Task SDK module for XClient"""
1
+ """Task SDK module for MLOps"""
2
2
 
3
3
  from .client import TaskClient
4
4
  from .task import Task
mlops/task/client.py CHANGED
@@ -17,7 +17,7 @@ logger = logging.getLogger(__name__)
17
17
 
18
18
 
19
19
  def handle_api_exception(e: Response):
20
- """Handle API exceptions and convert them to appropriate XClient exceptions."""
20
+ """Handle API exceptions and convert them to appropriate MLOps exceptions."""
21
21
  try:
22
22
  body = json.loads(e.content) if e.content else {}
23
23
  except json.JSONDecodeError:
@@ -49,51 +49,27 @@ def handle_api_exception(e: Response):
49
49
 
50
50
  class TaskClient(AuthenticatedClient):
51
51
  """
52
- The client for interacting with the XClient Task API.
52
+ The client for interacting with the MLOps Task API.
53
53
  """
54
54
 
55
55
  def __init__(
56
56
  self,
57
57
  config: ConnectionConfig,
58
- require_api_key: bool = True,
59
- require_access_token: bool = False,
60
58
  limits: Optional[Limits] = None,
61
59
  *args,
62
60
  **kwargs,
63
61
  ):
64
- if require_api_key and require_access_token:
62
+ # NOTE: This SDK client only supports API key authentication.
63
+ # Header: X-API-Key (per OpenAPI spec)
64
+ if config.api_key is None:
65
65
  raise AuthenticationException(
66
- "Only one of api_key or access_token can be required, not both",
66
+ "API key is required. "
67
+ "You can set the environment variable `MLOPS_API_KEY`"
68
+ ' to pass it directly like ConnectionConfig(api_key="mlops_...")',
67
69
  )
68
70
 
69
- if not require_api_key and not require_access_token:
70
- raise AuthenticationException(
71
- "Either api_key or access_token is required",
72
- )
73
-
74
- token = None
75
- if require_api_key:
76
- if config.api_key is None:
77
- raise AuthenticationException(
78
- "API key is required. "
79
- "You can either set the environment variable `XCLIENT_API_KEY` "
80
- 'or pass it directly like TaskClient(api_key="xclient_...")',
81
- )
82
- token = config.api_key
83
-
84
- if require_access_token:
85
- if config.access_token is None:
86
- raise AuthenticationException(
87
- "Access token is required. "
88
- "You can set the environment variable `XCLIENT_ACCESS_TOKEN` "
89
- "or pass the `access_token` in options.",
90
- )
91
- token = config.access_token
92
-
93
- # API Key header: X-API-Key (per OpenAPI spec)
94
- # JWT header: Authorization: Bearer <token>
95
- auth_header_name = "X-API-Key" if require_api_key else "Authorization"
96
- prefix = "" if require_api_key else "Bearer"
71
+ auth_header_name = "X-API-Key"
72
+ prefix = ""
97
73
 
98
74
  headers = {
99
75
  **(config.headers or {}),
@@ -116,9 +92,9 @@ class TaskClient(AuthenticatedClient):
116
92
  base_url=config.api_url,
117
93
  httpx_args=httpx_args,
118
94
  headers=headers,
119
- token=token,
120
95
  auth_header_name=auth_header_name,
121
96
  prefix=prefix,
97
+ token=config.api_key,
122
98
  *args,
123
99
  **kwargs,
124
100
  )
mlops/task/task.py CHANGED
@@ -1,12 +1,17 @@
1
1
  """
2
- High-level Task SDK interface for XClient.
2
+ High-level Task SDK interface for MLOps.
3
3
 
4
- This module provides a convenient interface for managing tasks through the XClient API.
5
- """
4
+ This module provides a convenient interface for managing tasks through the MLOps API.
5
+ """
6
6
 
7
7
  import json
8
+ import os
8
9
  from http import HTTPStatus
10
+ from pathlib import Path
9
11
  from typing import Optional
12
+
13
+ import httpx
14
+
10
15
  from ..api.client.api.tasks import (
11
16
  submit_task,
12
17
  get_task,
@@ -14,8 +19,15 @@ from ..api.client.api.tasks import (
14
19
  cancel_task,
15
20
  delete_task,
16
21
  )
22
+ from ..api.client.api.storage import (
23
+ get_storage_presign_upload,
24
+ get_storage_presign_download,
25
+ )
17
26
  from ..api.client.models.task import Task as TaskModel
18
27
  from ..api.client.models.task_submit_request import TaskSubmitRequest
28
+ from ..api.client.models.task_submit_request_environment_type_0 import (
29
+ TaskSubmitRequestEnvironmentType0,
30
+ )
19
31
  from ..api.client.models.task_submit_response import TaskSubmitResponse
20
32
  from ..api.client.models.task_list_response import TaskListResponse
21
33
  from ..api.client.models.task_status import TaskStatus
@@ -29,13 +41,46 @@ from ..exceptions import (
29
41
  from .client import TaskClient, handle_api_exception
30
42
 
31
43
 
44
+ def _validate_archive_file_path(file_path: str) -> Path:
45
+ p = Path(os.path.expanduser(file_path)).resolve()
46
+ if not p.exists():
47
+ raise APIException(f"File not found: {p}")
48
+ if not p.is_file():
49
+ raise APIException(f"file_path must be a file: {p}")
50
+
51
+ lower = p.name.lower()
52
+ if not (lower.endswith(".zip") or lower.endswith(".tar.gz") or lower.endswith(".tgz")):
53
+ raise APIException(f"file_path must be one of .zip, .tar.gz, .tgz: {p}")
54
+ return p
55
+
56
+
57
+ def _upload_file_to_presigned_url(url: str, file_path: Path, timeout: Optional[float]) -> None:
58
+ size = file_path.stat().st_size
59
+ # Use a dedicated client for S3 presigned upload (avoid leaking API auth headers).
60
+ with httpx.Client(timeout=timeout) as client:
61
+ with file_path.open("rb") as f:
62
+ resp = client.put(
63
+ url,
64
+ content=f,
65
+ headers={
66
+ "Content-Length": str(size),
67
+ "Content-Type": "application/octet-stream",
68
+ },
69
+ )
70
+ if resp.status_code < 200 or resp.status_code >= 300:
71
+ body = (resp.text or "")[:2048]
72
+ raise APIException(
73
+ f"Failed to upload file to presigned url: HTTP {resp.status_code}: {body}"
74
+ )
75
+
76
+
32
77
  class Task:
33
78
  """
34
79
  High-level interface for managing tasks.
35
80
 
36
81
  Example:
37
82
  ```python
38
- from xclient import Task, ConnectionConfig
83
+ from mlops import Task, ConnectionConfig
39
84
 
40
85
  config = ConnectionConfig(api_key="your_api_key")
41
86
  task = Task(config=config)
@@ -43,28 +88,28 @@ class Task:
43
88
  # Submit a task with script
44
89
  result = task.submit(
45
90
  name="my-task",
46
- cluster_id=1,
91
+ cluster_name="slurm-cn",
47
92
  script="#!/bin/bash\\necho 'Hello World'"
48
93
  )
49
94
 
50
95
  # Or submit with command
51
96
  result = task.submit(
52
97
  name="my-task",
53
- cluster_id=1,
98
+ cluster_name="slurm-cn",
54
99
  command="echo 'Hello World'"
55
100
  )
56
101
 
57
102
  # Get task details
58
- task_info = task.get(task_id=result.job_id, cluster_id=1)
103
+ task_info = task.get(task_id=result.job_id, cluster_name="slurm-cn")
59
104
 
60
105
  # List tasks
61
106
  tasks = task.list(status=TaskStatus.RUNNING)
62
107
 
63
108
  # Cancel a task
64
- task.cancel(task_id=result.job_id, cluster_id=1)
109
+ task.cancel(task_id=result.job_id, cluster_name="slurm-cn")
65
110
 
66
111
  # Delete a task
67
- task.delete(task_id=result.job_id, cluster_id=1)
112
+ task.delete(task_id=result.job_id, cluster_name="slurm-cn")
68
113
  ```
69
114
  """
70
115
 
@@ -72,7 +117,6 @@ class Task:
72
117
  self,
73
118
  config: Optional["ConnectionConfig"] = None,
74
119
  api_key: Optional[str] = None,
75
- access_token: Optional[str] = None,
76
120
  domain: Optional[str] = None,
77
121
  debug: Optional[bool] = None,
78
122
  request_timeout: Optional[float] = None,
@@ -83,7 +127,6 @@ class Task:
83
127
  Args:
84
128
  config: ConnectionConfig instance. If not provided, a new one will be created.
85
129
  api_key: API key for authentication. Overrides config.api_key.
86
- access_token: Access token for authentication. Overrides config.access_token.
87
130
  domain: API domain. Overrides config.domain.
88
131
  debug: Enable debug mode. Overrides config.debug.
89
132
  request_timeout: Request timeout in seconds. Overrides config.request_timeout.
@@ -95,8 +138,6 @@ class Task:
95
138
  # Override config values if provided
96
139
  if api_key is not None:
97
140
  config.api_key = api_key
98
- if access_token is not None:
99
- config.access_token = access_token
100
141
  if domain is not None:
101
142
  config.domain = domain
102
143
  if debug is not None:
@@ -106,22 +147,22 @@ class Task:
106
147
 
107
148
  self._config = config
108
149
  self._client = TaskClient(config=config)
109
-
110
150
  def submit(
111
151
  self,
112
152
  name: str,
113
- cluster_id: Optional[int] = None,
153
+ cluster_name: str,
114
154
  script: Optional[str] = None,
115
155
  command: Optional[str] = None,
116
156
  resources: Optional[dict] = None,
117
157
  team_id: Optional[int] = None,
158
+ file_path: Optional[str] = None,
118
159
  ) -> TaskSubmitResponse:
119
160
  """
120
161
  Submit a new task.
121
162
 
122
163
  Args:
123
164
  name: Task name
124
- cluster_id: Cluster ID to submit the task to
165
+ cluster_name: Cluster name to submit the task to
125
166
  script: Task script content (optional, but at least one of script or command is required)
126
167
  command: Command to execute (optional, but at least one of script or command is required)
127
168
  resources: Resource requirements dict (optional)
@@ -134,19 +175,16 @@ class Task:
134
175
  APIException: If the API returns an error
135
176
  AuthenticationException: If authentication fails
136
177
  """
137
- # Validate required fields
138
- if cluster_id is None:
139
- raise APIException("cluster_id is required")
140
-
141
178
  # At least one of script or command must be provided
142
179
  if not script and not command:
143
180
  raise APIException("At least one of 'script' or 'command' must be provided")
144
181
 
145
182
  # Map resources dict to individual fields
146
183
  # resources dict can contain: cpu, cpus_per_task, memory, nodes, gres, time, partition, etc.
184
+
147
185
  request_kwargs = {
148
186
  "name": name,
149
- "cluster_id": cluster_id,
187
+ "cluster_name": cluster_name,
150
188
  }
151
189
 
152
190
  # Handle script and command (at least one is required)
@@ -177,7 +215,87 @@ class Task:
177
215
  request_kwargs["partition"] = resources.get("partition")
178
216
  if "tres" in resources:
179
217
  request_kwargs["tres"] = resources.get("tres")
180
-
218
+
219
+ if file_path:
220
+ local_path = _validate_archive_file_path(file_path)
221
+ timeout = self._config.get_request_timeout()
222
+
223
+ # 1) Get presigned upload URL
224
+ presign_upload_obj = get_storage_presign_upload.sync_detailed(
225
+ client=self._client,
226
+ filename=local_path.name,
227
+ )
228
+ presign_upload = presign_upload_obj.parsed
229
+ if isinstance(presign_upload, ErrorResponse):
230
+ status_code = (
231
+ presign_upload.code
232
+ if presign_upload.code != UNSET and presign_upload.code != 0
233
+ else presign_upload_obj.status_code.value
234
+ )
235
+ exception = handle_api_exception(
236
+ Response(
237
+ status_code=HTTPStatus(status_code),
238
+ content=presign_upload_obj.content,
239
+ headers=presign_upload_obj.headers,
240
+ parsed=None,
241
+ )
242
+ )
243
+ raise exception
244
+
245
+ if (
246
+ presign_upload is None
247
+ or presign_upload.url in (UNSET, None)
248
+ or presign_upload.key in (UNSET, None)
249
+ ):
250
+ raise APIException("Failed to get presigned upload url: empty response")
251
+
252
+ # 2) Upload file to S3 (presigned URL)
253
+ _upload_file_to_presigned_url(
254
+ url=str(presign_upload.url),
255
+ file_path=local_path,
256
+ timeout=timeout,
257
+ )
258
+
259
+ # 3) Get presigned download URL
260
+ presign_download_obj = get_storage_presign_download.sync_detailed(
261
+ client=self._client,
262
+ key=str(presign_upload.key),
263
+ )
264
+ presign_download = presign_download_obj.parsed
265
+ if isinstance(presign_download, ErrorResponse):
266
+ status_code = (
267
+ presign_download.code
268
+ if presign_download.code != UNSET and presign_download.code != 0
269
+ else presign_download_obj.status_code.value
270
+ )
271
+ exception = handle_api_exception(
272
+ Response(
273
+ status_code=HTTPStatus(status_code),
274
+ content=presign_download_obj.content,
275
+ headers=presign_download_obj.headers,
276
+ parsed=None,
277
+ )
278
+ )
279
+ raise exception
280
+
281
+ if presign_download is None or presign_download.url in (UNSET, None):
282
+ raise APIException(
283
+ "Failed to get presigned download url: empty response"
284
+ )
285
+
286
+ # 4) Set env var (merge if user already provided environment)
287
+ env: dict[str, str] = {}
288
+ existing_env = request_kwargs.get("environment")
289
+ if isinstance(existing_env, TaskSubmitRequestEnvironmentType0):
290
+ env.update(existing_env.additional_properties)
291
+ elif isinstance(existing_env, dict):
292
+ env.update(existing_env)
293
+
294
+ env["SYSTEM_DOWNLOAD_ARCHIVE_URL"] = str(presign_download.url)
295
+ request_kwargs["environment"] = TaskSubmitRequestEnvironmentType0.from_dict(
296
+ env
297
+ )
298
+
181
299
  request = TaskSubmitRequest(**request_kwargs)
182
300
 
183
301
  # Use sync_detailed to get full response information
@@ -230,14 +348,14 @@ class Task:
230
348
  def get(
231
349
  self,
232
350
  task_id: int,
233
- cluster_id: int,
351
+ cluster_name: str,
234
352
  ) -> TaskModel:
235
353
  """
236
354
  Get task details by task ID.
237
355
 
238
356
  Args:
239
357
  task_id: Task ID
240
- cluster_id: Cluster ID
358
+ cluster_name: Cluster name
241
359
 
242
360
  Returns:
243
361
  Task model with task details
@@ -250,7 +368,7 @@ class Task:
250
368
  response_obj = get_task.sync_detailed(
251
369
  id=task_id,
252
370
  client=self._client,
253
- cluster_id=cluster_id,
371
+ cluster_name=cluster_name,
254
372
  )
255
373
  response = response_obj.parsed
256
374
 
@@ -302,7 +420,7 @@ class Task:
302
420
  status: Optional[TaskStatus] = None,
303
421
  user_id: Optional[int] = None,
304
422
  team_id: Optional[int] = None,
305
- cluster_id: Optional[int] = None,
423
+ cluster_name: Optional[str] = None,
306
424
  ) -> TaskListResponse:
307
425
  """
308
426
  List tasks with optional filtering.
@@ -313,7 +431,7 @@ class Task:
313
431
  status: Filter by task status (optional)
314
432
  user_id: Filter by user ID (optional)
315
433
  team_id: Filter by team ID (optional)
316
- cluster_id: Filter by cluster ID (optional)
434
+ cluster_name: Filter by cluster name (optional)
317
435
 
318
436
  Returns:
319
437
  TaskListResponse containing the list of tasks
@@ -329,7 +447,7 @@ class Task:
329
447
  status=status if status is not None else UNSET,
330
448
  user_id=user_id if user_id is not None else UNSET,
331
449
  team_id=team_id if team_id is not None else UNSET,
332
- cluster_id=cluster_id if cluster_id is not None else UNSET,
450
+ cluster_name=cluster_name if cluster_name is not None else UNSET,
333
451
  )
334
452
  response = response_obj.parsed
335
453
 
@@ -377,14 +495,14 @@ class Task:
377
495
  def cancel(
378
496
  self,
379
497
  task_id: int,
380
- cluster_id: int,
498
+ cluster_name: str,
381
499
  ) -> bool:
382
500
  """
383
501
  Cancel a task.
384
502
 
385
503
  Args:
386
504
  task_id: Task ID to cancel
387
- cluster_id: Cluster ID where the task is running
505
+ cluster_name: Cluster name where the task is running
388
506
 
389
507
  Returns:
390
508
  True if the task was cancelled successfully
@@ -397,7 +515,7 @@ class Task:
397
515
  response_obj = cancel_task.sync_detailed(
398
516
  id=task_id,
399
517
  client=self._client,
400
- cluster_id=cluster_id,
518
+ cluster_name=cluster_name,
401
519
  )
402
520
  response = response_obj.parsed
403
521
 
@@ -434,14 +552,14 @@ class Task:
434
552
  def delete(
435
553
  self,
436
554
  task_id: int,
437
- cluster_id: int,
555
+ cluster_name: str,
438
556
  ) -> bool:
439
557
  """
440
558
  Delete a task.
441
559
 
442
560
  Args:
443
561
  task_id: Task ID to delete
444
- cluster_id: Cluster ID where the task is running
562
+ cluster_name: Cluster name where the task is running
445
563
 
446
564
  Returns:
447
565
  True if the task was deleted successfully
@@ -454,7 +572,7 @@ class Task:
454
572
  response_obj = delete_task.sync_detailed(
455
573
  id=task_id,
456
574
  client=self._client,
457
- cluster_id=cluster_id,
575
+ cluster_name=cluster_name,
458
576
  )
459
577
  response = response_obj.parsed
460
578
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: mlops-python-sdk
3
- Version: 1.0.0
3
+ Version: 1.0.1
4
4
  Summary: MLOps Python SDK for XCloud Service API
5
5
  License: MIT
6
6
  Author: mlops
@@ -39,9 +39,9 @@ pip install mlops-python-sdk
39
39
 
40
40
  ### 1. Setup Authentication
41
41
 
42
- You can authenticate using either an API Key or an Access Token.
42
+ You can authenticate using either an API Key.
43
43
 
44
- #### Option 1: API Key (Recommended for programmatic access)
44
+ #### API Key (Recommended for programmatic access)
45
45
 
46
46
  1. Sign up at [MLOps](https://xcloud-service.com)
47
47
  2. Create an API key from [API Keys](https://xcloud-service.com/home/api-keys)
@@ -52,13 +52,6 @@ export MLOPS_API_KEY=xck_******
52
52
  export MLOPS_DOMAIN=localhost:8090 # optional, default is localhost:8090
53
53
  ```
54
54
 
55
- #### Option 2: Access Token (For user authentication)
56
-
57
- ```bash
58
- export MLOPS_ACCESS_TOKEN=your_access_token
59
- export MLOPS_DOMAIN=localhost:8090 # optional
60
- ```
61
-
62
55
  ### 2. Basic Usage
63
56
 
64
57
  ```python
@@ -127,7 +120,6 @@ task = Task()
127
120
  # With explicit configuration
128
121
  config = ConnectionConfig(
129
122
  api_key="xck_******", # API key for authentication
130
- access_token="token_******", # Access token (alternative to API key)
131
123
  domain="localhost:8090", # API domain
132
124
  debug=False, # Enable debug mode
133
125
  request_timeout=30.0 # Request timeout in seconds
@@ -280,7 +272,6 @@ TaskStatus.CREATED # Task was created
280
272
  The SDK reads configuration from environment variables:
281
273
 
282
274
  - `MLOPS_API_KEY`: API key for authentication
283
- - `MLOPS_ACCESS_TOKEN`: Access token for authentication (alternative to API key)
284
275
  - `MLOPS_DOMAIN`: API domain (default: `localhost:8090`)
285
276
  - `MLOPS_DEBUG`: Enable debug mode (`true`/`false`, default: `false`)
286
277
  - `MLOPS_API_PATH`: API path prefix (default: `/api/v1`)