clouditia-manager 1.0.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.
@@ -0,0 +1,43 @@
1
+ """
2
+ Clouditia Manager SDK - Manage GPU sessions via the Computing API
3
+
4
+ Usage:
5
+ from clouditia_manager import GPUManager
6
+
7
+ manager = GPUManager(api_key="sk_compute_...")
8
+
9
+ # Create a session
10
+ session = manager.create_session(
11
+ gpu_type="nvidia-rtx-3090",
12
+ vcpu=2,
13
+ ram=4,
14
+ storage=20
15
+ )
16
+
17
+ # List sessions
18
+ sessions = manager.list_sessions()
19
+
20
+ # Stop a session
21
+ manager.stop_session("369bde33")
22
+ """
23
+
24
+ __version__ = "1.0.0"
25
+ __author__ = "Clouditia"
26
+
27
+ from .client import GPUManager
28
+ from .exceptions import (
29
+ ClouditiaManagerError,
30
+ AuthenticationError,
31
+ SessionNotFoundError,
32
+ InsufficientResourcesError,
33
+ APIError
34
+ )
35
+
36
+ __all__ = [
37
+ "GPUManager",
38
+ "ClouditiaManagerError",
39
+ "AuthenticationError",
40
+ "SessionNotFoundError",
41
+ "InsufficientResourcesError",
42
+ "APIError"
43
+ ]
@@ -0,0 +1,543 @@
1
+ """
2
+ Clouditia Manager SDK Client
3
+
4
+ Manage GPU sessions via the Computing API (sk_compute_ keys)
5
+ """
6
+
7
+ import requests
8
+ import time
9
+ import sys
10
+ from typing import Optional, Dict, List, Any, Callable
11
+ from dataclasses import dataclass
12
+ from datetime import datetime
13
+
14
+ from .exceptions import (
15
+ ClouditiaManagerError,
16
+ AuthenticationError,
17
+ SessionNotFoundError,
18
+ InsufficientResourcesError,
19
+ APIError
20
+ )
21
+
22
+
23
+ @dataclass
24
+ class GPUSession:
25
+ """Represents a GPU session"""
26
+ id: str
27
+ short_id: str
28
+ name: str
29
+ status: str
30
+ gpu_type: str
31
+ gpu_count: int
32
+ vcpu: int
33
+ ram: str
34
+ storage: str
35
+ vscode_port: Optional[int]
36
+ jupyter_port: Optional[int]
37
+ password: Optional[str]
38
+ url: str
39
+ created_at: Optional[datetime]
40
+ started_at: Optional[datetime]
41
+
42
+ def __repr__(self):
43
+ return f"GPUSession(name='{self.name}', id='{self.short_id}', status='{self.status}')"
44
+
45
+
46
+ @dataclass
47
+ class GPUInventory:
48
+ """Represents GPU inventory in marketplace"""
49
+ gpu_type: str
50
+ gpu_name: str
51
+ total: int
52
+ available: int
53
+ in_use: int
54
+ price_per_hour: float
55
+
56
+ def __repr__(self):
57
+ return f"GPUInventory(type='{self.gpu_type}', available={self.available}/{self.total})"
58
+
59
+
60
+ class GPUManager:
61
+ """
62
+ Clouditia GPU Manager Client
63
+
64
+ Manage GPU sessions using the Computing API.
65
+
66
+ Args:
67
+ api_key: Your sk_compute_ API key
68
+ base_url: API base URL (default: https://clouditia.com)
69
+ timeout: Request timeout in seconds (default: 60)
70
+ """
71
+
72
+ DEFAULT_BASE_URL = "https://clouditia.com"
73
+
74
+ def __init__(
75
+ self,
76
+ api_key: str,
77
+ base_url: str = None,
78
+ timeout: int = 60
79
+ ):
80
+ if not api_key or not api_key.startswith("sk_compute_"):
81
+ raise AuthenticationError("Invalid API key. Must start with 'sk_compute_'")
82
+
83
+ self.api_key = api_key
84
+ self.base_url = (base_url or self.DEFAULT_BASE_URL).rstrip("/")
85
+ self.timeout = timeout
86
+ self._session = requests.Session()
87
+ self._session.headers.update({
88
+ "Authorization": f"Bearer {api_key}",
89
+ "Content-Type": "application/json",
90
+ "User-Agent": "clouditia-manager/1.0.0"
91
+ })
92
+
93
+ # Verify API key on init
94
+ self._verify_api_key()
95
+
96
+ def _verify_api_key(self):
97
+ """Verify the API key is valid"""
98
+ try:
99
+ response = self._request("GET", "/api/computing/verify/")
100
+ if not response.get("valid"):
101
+ raise AuthenticationError("API key is not valid")
102
+ self.user = response.get("user", {})
103
+ except requests.exceptions.RequestException as e:
104
+ raise APIError(f"Failed to verify API key: {e}")
105
+
106
+ def _request(
107
+ self,
108
+ method: str,
109
+ endpoint: str,
110
+ data: Dict = None,
111
+ params: Dict = None
112
+ ) -> Dict:
113
+ """Make an API request"""
114
+ url = f"{self.base_url}{endpoint}"
115
+
116
+ try:
117
+ response = self._session.request(
118
+ method=method,
119
+ url=url,
120
+ json=data,
121
+ params=params,
122
+ timeout=self.timeout
123
+ )
124
+
125
+ if response.status_code == 401:
126
+ raise AuthenticationError("Invalid or expired API key")
127
+ elif response.status_code == 403:
128
+ raise AuthenticationError("Access denied")
129
+ elif response.status_code == 404:
130
+ raise SessionNotFoundError("Resource not found")
131
+
132
+ response.raise_for_status()
133
+ return response.json()
134
+
135
+ except requests.exceptions.JSONDecodeError:
136
+ return {"raw": response.text}
137
+ except requests.exceptions.RequestException as e:
138
+ raise APIError(f"Request failed: {e}")
139
+
140
+ def create_session(
141
+ self,
142
+ gpu_type: str = "nvidia-rtx-3090",
143
+ gpu_count: int = 1,
144
+ vcpu: int = 4,
145
+ ram: int = 16,
146
+ storage: int = 20,
147
+ wait_ready: bool = True,
148
+ timeout: int = 180,
149
+ verbose: bool = True
150
+ ) -> GPUSession:
151
+ """
152
+ Create a new GPU session.
153
+
154
+ Args:
155
+ gpu_type: GPU type slug (e.g., 'nvidia-rtx-3090', 'nvidia-rtx-4090')
156
+ gpu_count: Number of GPUs (default: 1)
157
+ vcpu: Number of vCPUs (default: 4)
158
+ ram: RAM in GB (default: 16)
159
+ storage: Storage in GB (default: 20)
160
+ wait_ready: Wait for session to be fully ready (default: True)
161
+ timeout: Max wait time in seconds (default: 180)
162
+ verbose: Print status messages (default: True)
163
+
164
+ Returns:
165
+ GPUSession object with session details
166
+
167
+ Raises:
168
+ InsufficientResourcesError: If requested GPU is not available
169
+ APIError: If session creation fails
170
+ """
171
+ data = {
172
+ "gpu_type": gpu_type,
173
+ "gpu_count": gpu_count,
174
+ "vcpu": vcpu,
175
+ "ram": ram,
176
+ "storage": storage
177
+ }
178
+
179
+ if verbose:
180
+ print(f"Creating GPU session with {gpu_type}...")
181
+
182
+ response = self._request("POST", "/api/computing/sessions/create/", data=data)
183
+
184
+ if not response.get("success"):
185
+ error = response.get("error", "Unknown error")
186
+ if "disponible" in error.lower() or "available" in error.lower():
187
+ raise InsufficientResourcesError(error)
188
+ raise APIError(error)
189
+
190
+ session_data = response.get("session", {})
191
+ session = self._parse_session(session_data)
192
+
193
+ if verbose:
194
+ print(f"Session created: {session.short_id}")
195
+
196
+ # Wait for session to be fully ready
197
+ if wait_ready:
198
+ session = self._wait_for_ready(session.short_id, timeout=timeout, verbose=verbose)
199
+
200
+ return session
201
+
202
+ def _wait_for_ready(
203
+ self,
204
+ session_id: str,
205
+ timeout: int = 180,
206
+ poll_interval: int = 3,
207
+ verbose: bool = True
208
+ ) -> GPUSession:
209
+ """
210
+ Internal method: Wait for session and GPU resources to be ready.
211
+
212
+ This method polls the session status until it's running and ready.
213
+ GPU resource updates are handled automatically by the server.
214
+ """
215
+ if verbose:
216
+ print(f"Waiting for session {session_id} to be ready...", end="", flush=True)
217
+
218
+ start_time = time.time()
219
+ last_status = None
220
+
221
+ while time.time() - start_time < timeout:
222
+ try:
223
+ session = self.get_session(session_id)
224
+
225
+ if session.status != last_status:
226
+ last_status = session.status
227
+ if verbose and session.status == "pending":
228
+ print(".", end="", flush=True)
229
+
230
+ if session.status == "running":
231
+ # Give a moment for GPU resources to fully initialize
232
+ time.sleep(2)
233
+
234
+ if verbose:
235
+ print(" Ready!")
236
+ print(f"\n{'='*50}")
237
+ print(f" SESSION READY")
238
+ print(f"{'='*50}")
239
+ print(f" Name : {session.name}")
240
+ print(f" Short ID : {session.short_id}")
241
+ print(f" Status : {session.status}")
242
+ print(f" GPU : {session.gpu_type} x{session.gpu_count}")
243
+ print(f" vCPU : {session.vcpu}")
244
+ print(f" RAM : {session.ram}")
245
+ print(f" Storage : {session.storage}")
246
+ print(f" URL : {session.url}")
247
+ print(f" Password : {session.password}")
248
+ print(f"{'='*50}\n")
249
+
250
+ return session
251
+
252
+ elif session.status == "failed":
253
+ if verbose:
254
+ print(" Failed!")
255
+ raise APIError(f"Session {session_id} failed to start")
256
+
257
+ except SessionNotFoundError:
258
+ pass
259
+
260
+ if verbose:
261
+ print(".", end="", flush=True)
262
+
263
+ time.sleep(poll_interval)
264
+
265
+ if verbose:
266
+ print(" Timeout!")
267
+ raise APIError(f"Timeout waiting for session {session_id} to be ready")
268
+
269
+ def stop_session(
270
+ self,
271
+ session_id: str,
272
+ wait_stopped: bool = True,
273
+ timeout: int = 120,
274
+ verbose: bool = True
275
+ ) -> GPUSession:
276
+ """
277
+ Stop a running GPU session.
278
+
279
+ Args:
280
+ session_id: Session ID (short or full UUID)
281
+ wait_stopped: Wait for session to be fully stopped (default: True)
282
+ timeout: Max wait time in seconds (default: 120)
283
+ verbose: Print status messages (default: True)
284
+
285
+ Returns:
286
+ GPUSession object with final status
287
+
288
+ Raises:
289
+ SessionNotFoundError: If session is not found
290
+ """
291
+ # Get session info first
292
+ try:
293
+ session = self.get_session(session_id)
294
+ except SessionNotFoundError:
295
+ raise SessionNotFoundError(f"Session {session_id} not found")
296
+
297
+ if verbose:
298
+ print(f"Stopping session {session.short_id}...")
299
+
300
+ response = self._request(
301
+ "POST",
302
+ "/api/computing/sessions/stop/",
303
+ data={"session_id": session_id}
304
+ )
305
+
306
+ if not response.get("success"):
307
+ error = response.get("error", "Unknown error")
308
+ if "not found" in error.lower() or "non trouvée" in error.lower():
309
+ raise SessionNotFoundError(f"Session {session_id} not found")
310
+ raise APIError(error)
311
+
312
+ # Wait for session to be fully stopped
313
+ if wait_stopped:
314
+ session = self._wait_for_stopped(
315
+ session.short_id,
316
+ session_info=session,
317
+ timeout=timeout,
318
+ verbose=verbose
319
+ )
320
+
321
+ return session
322
+
323
+ def _wait_for_stopped(
324
+ self,
325
+ session_id: str,
326
+ session_info: GPUSession = None,
327
+ timeout: int = 120,
328
+ poll_interval: int = 2,
329
+ verbose: bool = True
330
+ ) -> GPUSession:
331
+ """
332
+ Internal method: Wait for session and pods to be fully stopped.
333
+
334
+ GPU resource updates are handled automatically by the server.
335
+ """
336
+ if verbose:
337
+ print(f"Waiting for pod termination...", end="", flush=True)
338
+
339
+ start_time = time.time()
340
+
341
+ while time.time() - start_time < timeout:
342
+ try:
343
+ session = self.get_session(session_id)
344
+
345
+ if session.status == "stopped":
346
+ if verbose:
347
+ print(" Done!")
348
+ print(f"\n{'='*50}")
349
+ print(f" SESSION STOPPED")
350
+ print(f"{'='*50}")
351
+ print(f" Name : {session.name}")
352
+ print(f" Short ID : {session.short_id}")
353
+ print(f" Status : {session.status}")
354
+ print(f" GPU : {session.gpu_type} (released)")
355
+ print(f"{'='*50}\n")
356
+
357
+ return session
358
+
359
+ except SessionNotFoundError:
360
+ # Session might be deleted, consider it stopped
361
+ session_name = session_info.name if session_info else f"compute-gpu-{session_id}"
362
+ if verbose:
363
+ print(" Done!")
364
+ print(f"\n{'='*50}")
365
+ print(f" SESSION STOPPED")
366
+ print(f"{'='*50}")
367
+ print(f" Name : {session_name}")
368
+ print(f" Short ID : {session_id}")
369
+ print(f" Status : stopped (deleted)")
370
+ print(f"{'='*50}\n")
371
+
372
+ if session_info:
373
+ session_info.status = "stopped"
374
+ return session_info
375
+ return None
376
+
377
+ if verbose:
378
+ print(".", end="", flush=True)
379
+
380
+ time.sleep(poll_interval)
381
+
382
+ if verbose:
383
+ print(" Timeout!")
384
+
385
+ # Return last known state
386
+ try:
387
+ return self.get_session(session_id)
388
+ except:
389
+ if session_info:
390
+ return session_info
391
+ raise APIError(f"Timeout waiting for session {session_id} to stop")
392
+
393
+ def get_session(self, session_id: str) -> GPUSession:
394
+ """
395
+ Get details of a specific session.
396
+
397
+ Args:
398
+ session_id: Session ID (short or full UUID)
399
+
400
+ Returns:
401
+ GPUSession object
402
+ """
403
+ response = self._request(
404
+ "GET",
405
+ "/api/computing/sessions/status/",
406
+ params={"session_id": session_id}
407
+ )
408
+
409
+ if not response.get("success"):
410
+ raise SessionNotFoundError(f"Session {session_id} not found")
411
+
412
+ return self._parse_session(response.get("session", {}))
413
+
414
+ def list_sessions(self, status: str = None) -> List[GPUSession]:
415
+ """
416
+ List all GPU sessions.
417
+
418
+ Args:
419
+ status: Filter by status ('running', 'stopped', 'pending')
420
+
421
+ Returns:
422
+ List of GPUSession objects
423
+ """
424
+ params = {}
425
+ if status:
426
+ params["status"] = status
427
+
428
+ response = self._request("GET", "/api/computing/sessions/", params=params)
429
+
430
+ sessions = []
431
+ for session_data in response.get("sessions", []):
432
+ sessions.append(self._parse_session(session_data))
433
+
434
+ return sessions
435
+
436
+ def get_inventory(self) -> List[GPUInventory]:
437
+ """
438
+ Get available GPU inventory.
439
+
440
+ Returns:
441
+ List of GPUInventory objects
442
+ """
443
+ response = self._request("GET", "/api/computing/inventory/")
444
+
445
+ inventory = []
446
+ for item in response.get("inventory", []):
447
+ inventory.append(GPUInventory(
448
+ gpu_type=item.get("gpu_type", ""),
449
+ gpu_name=item.get("gpu_name", ""),
450
+ total=item.get("total", 0),
451
+ available=item.get("available", 0),
452
+ in_use=item.get("in_use", 0),
453
+ price_per_hour=item.get("price_per_hour", 0.0)
454
+ ))
455
+
456
+ return inventory
457
+
458
+ def generate_sdk_key(self, session_id: str, name: str = "SDK Key") -> str:
459
+ """
460
+ Generate an sk_live_ API key for a session.
461
+
462
+ This key can be used with the 'clouditia' SDK to execute code.
463
+
464
+ Args:
465
+ session_id: Session ID
466
+ name: Key name (default: "SDK Key")
467
+
468
+ Returns:
469
+ The generated sk_live_ API key
470
+ """
471
+ response = self._request(
472
+ "POST",
473
+ "/api/computing/sessions/generate-key/",
474
+ data={"session_id": session_id, "name": name}
475
+ )
476
+
477
+ if not response.get("success"):
478
+ raise APIError(response.get("error", "Failed to generate key"))
479
+
480
+ return response.get("api_key")
481
+
482
+ def rename_session(self, session_id: str, new_name: str) -> GPUSession:
483
+ """
484
+ Rename a GPU session.
485
+
486
+ Args:
487
+ session_id: Session ID (short or full UUID)
488
+ new_name: New name for the session
489
+
490
+ Returns:
491
+ GPUSession object with updated name
492
+ """
493
+ response = self._request(
494
+ "POST",
495
+ "/api/computing/sessions/rename/",
496
+ data={"session_id": session_id, "name": new_name}
497
+ )
498
+
499
+ if not response.get("success"):
500
+ error = response.get("error", "Failed to rename session")
501
+ if "not found" in error.lower():
502
+ raise SessionNotFoundError(f"Session {session_id} not found")
503
+ raise APIError(error)
504
+
505
+ # Get updated session
506
+ return self.get_session(session_id)
507
+
508
+ def _parse_session(self, data: Dict) -> GPUSession:
509
+ """Parse session data into GPUSession object"""
510
+ session_id = data.get("id", "")
511
+ short_id = session_id[:8] if session_id else ""
512
+ return GPUSession(
513
+ id=session_id,
514
+ short_id=short_id,
515
+ name=data.get("name", f"compute-gpu-{short_id}"),
516
+ status=data.get("status", "unknown"),
517
+ gpu_type=data.get("gpu_type", ""),
518
+ gpu_count=data.get("gpu_count", 1),
519
+ vcpu=data.get("vcpu", 0),
520
+ ram=data.get("ram", ""),
521
+ storage=data.get("storage", ""),
522
+ vscode_port=data.get("vscode_port"),
523
+ jupyter_port=data.get("jupyter_port"),
524
+ password=data.get("password"),
525
+ url=data.get("url", f"https://clouditia.com/code-editor/{session_id}/"),
526
+ created_at=self._parse_datetime(data.get("created_at")),
527
+ started_at=self._parse_datetime(data.get("started_at"))
528
+ )
529
+
530
+ @staticmethod
531
+ def _parse_datetime(value) -> Optional[datetime]:
532
+ """Parse datetime string"""
533
+ if not value:
534
+ return None
535
+ if isinstance(value, datetime):
536
+ return value
537
+ try:
538
+ return datetime.fromisoformat(value.replace("Z", "+00:00"))
539
+ except (ValueError, AttributeError):
540
+ return None
541
+
542
+ def __repr__(self):
543
+ return f"GPUManager(user='{self.user.get('username', 'unknown')}')"
@@ -0,0 +1,31 @@
1
+ """
2
+ Exceptions for Clouditia Manager SDK
3
+ """
4
+
5
+
6
+ class ClouditiaManagerError(Exception):
7
+ """Base exception for Clouditia Manager SDK"""
8
+ pass
9
+
10
+
11
+ class AuthenticationError(ClouditiaManagerError):
12
+ """Raised when API key is invalid or expired"""
13
+ pass
14
+
15
+
16
+ class SessionNotFoundError(ClouditiaManagerError):
17
+ """Raised when session is not found"""
18
+ pass
19
+
20
+
21
+ class InsufficientResourcesError(ClouditiaManagerError):
22
+ """Raised when requested GPU resources are not available"""
23
+ pass
24
+
25
+
26
+ class APIError(ClouditiaManagerError):
27
+ """Raised when API returns an error"""
28
+ def __init__(self, message, status_code=None, response=None):
29
+ super().__init__(message)
30
+ self.status_code = status_code
31
+ self.response = response
@@ -0,0 +1,308 @@
1
+ Metadata-Version: 2.4
2
+ Name: clouditia-manager
3
+ Version: 1.0.0
4
+ Summary: Manage GPU sessions on Clouditia platform
5
+ Home-page: https://clouditia.com
6
+ Author: Clouditia
7
+ Author-email: support@clouditia.com
8
+ Project-URL: Documentation, https://clouditia.com/docs/manager-sdk
9
+ Project-URL: Bug Reports, https://github.com/clouditia/clouditia-manager/issues
10
+ Project-URL: Source, https://github.com/clouditia/clouditia-manager
11
+ Keywords: gpu cloud computing ml machine-learning clouditia
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.8
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Requires-Python: >=3.8
23
+ Description-Content-Type: text/markdown
24
+ Requires-Dist: requests>=2.25.0
25
+ Dynamic: author
26
+ Dynamic: author-email
27
+ Dynamic: classifier
28
+ Dynamic: description
29
+ Dynamic: description-content-type
30
+ Dynamic: home-page
31
+ Dynamic: keywords
32
+ Dynamic: project-url
33
+ Dynamic: requires-dist
34
+ Dynamic: requires-python
35
+ Dynamic: summary
36
+
37
+ # Clouditia Manager SDK
38
+
39
+ SDK Python pour gérer les sessions GPU sur la plateforme Clouditia via l'API Computing (`sk_compute_`).
40
+
41
+ ## Installation
42
+
43
+ ```bash
44
+ pip install clouditia-manager
45
+ ```
46
+
47
+ ## Quick Start
48
+
49
+ ```python
50
+ from clouditia_manager import GPUManager
51
+
52
+ # Initialiser avec votre clé API sk_compute_
53
+ manager = GPUManager(api_key="sk_compute_xxxxx")
54
+
55
+ # Créer une session GPU
56
+ # Le SDK attend automatiquement que la session soit prête
57
+ session = manager.create_session(
58
+ gpu_type="nvidia-rtx-3090",
59
+ vcpu=2,
60
+ ram=4,
61
+ storage=20
62
+ )
63
+
64
+ # Output:
65
+ # Creating GPU session with nvidia-rtx-3090...
66
+ # Session created: 0e4c713a
67
+ # Waiting for session 0e4c713a to be ready... Ready!
68
+ #
69
+ # ==================================================
70
+ # SESSION READY
71
+ # ==================================================
72
+ # Name : compute-gpu-0e4c713a
73
+ # Short ID : 0e4c713a
74
+ # Status : running
75
+ # GPU : nvidia-rtx-3090 x1
76
+ # vCPU : 2
77
+ # RAM : 4Gi
78
+ # Storage : 20Gi
79
+ # URL : https://clouditia.com/code-editor/...
80
+ # Password : xxxxxxxxxxxx
81
+ # ==================================================
82
+
83
+ print(f"Session prête: {session.name}")
84
+ ```
85
+
86
+ ## Configuration
87
+
88
+ ```python
89
+ from clouditia_manager import GPUManager
90
+
91
+ # Configuration par défaut (production)
92
+ manager = GPUManager(api_key="sk_compute_xxxxx")
93
+
94
+ # Configuration personnalisée (développement local)
95
+ manager = GPUManager(
96
+ api_key="sk_compute_xxxxx",
97
+ base_url="http://127.0.0.1:8000/jobs", # URL de base de l'API
98
+ timeout=120 # Timeout en secondes
99
+ )
100
+ ```
101
+
102
+ ## Fonctionnalités
103
+
104
+ ### 1. Vérifier la clé API
105
+
106
+ La vérification est automatique à l'initialisation :
107
+
108
+ ```python
109
+ manager = GPUManager(api_key="sk_compute_xxxxx")
110
+ print(f"Utilisateur: {manager.user['username']}")
111
+ print(f"Email: {manager.user['email']}")
112
+ ```
113
+
114
+ ### 2. Créer une session GPU
115
+
116
+ ```python
117
+ # Création standard (attend automatiquement que la session soit prête)
118
+ session = manager.create_session(
119
+ gpu_type="nvidia-rtx-3090", # Type de GPU
120
+ gpu_count=1, # Nombre de GPUs
121
+ vcpu=4, # Nombre de vCPUs
122
+ ram=16, # RAM en GB
123
+ storage=20 # Stockage en GB
124
+ )
125
+
126
+ # La session est prête avec un nom automatique: compute-gpu-{short_id}
127
+ print(f"Nom: {session.name}") # compute-gpu-0e4c713a
128
+ print(f"ID: {session.short_id}") # 0e4c713a
129
+ print(f"Status: {session.status}") # running
130
+ print(f"URL: {session.url}")
131
+ print(f"Password: {session.password}")
132
+
133
+ # Options avancées
134
+ session = manager.create_session(
135
+ gpu_type="nvidia-rtx-3090",
136
+ wait_ready=True, # Attendre que la session soit prête (défaut: True)
137
+ timeout=180, # Timeout en secondes (défaut: 180)
138
+ verbose=True # Afficher les messages de status (défaut: True)
139
+ )
140
+
141
+ # Mode silencieux (sans attente ni messages)
142
+ session = manager.create_session(
143
+ gpu_type="nvidia-rtx-3090",
144
+ wait_ready=False, # Ne pas attendre
145
+ verbose=False # Pas de messages
146
+ )
147
+ ```
148
+
149
+ ### 3. Lister les sessions
150
+
151
+ ```python
152
+ # Toutes les sessions
153
+ sessions = manager.list_sessions()
154
+
155
+ # Filtrer par status
156
+ running = manager.list_sessions(status="running")
157
+ stopped = manager.list_sessions(status="stopped")
158
+
159
+ for session in sessions:
160
+ print(f"{session.name} ({session.short_id}): {session.status} - {session.gpu_type}")
161
+ ```
162
+
163
+ ### 4. Obtenir le status d'une session
164
+
165
+ ```python
166
+ # Par short ID (8 caractères)
167
+ session = manager.get_session("0e4c713a")
168
+
169
+ print(f"Nom: {session.name}") # compute-gpu-0e4c713a
170
+ print(f"Status: {session.status}") # running
171
+ print(f"GPU: {session.gpu_type}") # nvidia-rtx-3090
172
+ print(f"URL: {session.url}")
173
+ ```
174
+
175
+ ### 5. Renommer une session
176
+
177
+ ```python
178
+ # Chaque session a un nom par défaut: compute-gpu-{short_id}
179
+ session = manager.create_session(gpu_type="nvidia-rtx-3090")
180
+ print(f"Nom par défaut: {session.name}") # compute-gpu-0e4c713a
181
+
182
+ # Renommer la session
183
+ session = manager.rename_session("0e4c713a", "mon-projet-ml-v1")
184
+ print(f"Nouveau nom: {session.name}") # mon-projet-ml-v1
185
+ ```
186
+
187
+ ### 6. Arrêter une session
188
+
189
+ ```python
190
+ # Arrêt standard (attend automatiquement la suppression du pod)
191
+ session = manager.stop_session("0e4c713a")
192
+
193
+ # Output:
194
+ # Stopping session 0e4c713a...
195
+ # Waiting for pod termination... Done!
196
+ #
197
+ # ==================================================
198
+ # SESSION STOPPED
199
+ # ==================================================
200
+ # Name : mon-projet-ml-v1
201
+ # Short ID : 0e4c713a
202
+ # Status : stopped
203
+ # GPU : nvidia-rtx-3090 (released)
204
+ # ==================================================
205
+
206
+ print(f"Session arrêtée: {session.name}")
207
+ print(f"Status: {session.status}")
208
+
209
+ # Options avancées
210
+ session = manager.stop_session(
211
+ "0e4c713a",
212
+ wait_stopped=True, # Attendre la suppression complète (défaut: True)
213
+ timeout=120, # Timeout en secondes (défaut: 120)
214
+ verbose=True # Afficher les messages (défaut: True)
215
+ )
216
+
217
+ # Mode silencieux
218
+ session = manager.stop_session("0e4c713a", wait_stopped=False, verbose=False)
219
+ ```
220
+
221
+ ### 7. Consulter l'inventaire GPU
222
+
223
+ ```python
224
+ inventory = manager.get_inventory()
225
+
226
+ for gpu in inventory:
227
+ print(f"{gpu.gpu_name}: {gpu.available}/{gpu.total} disponibles")
228
+ print(f" Prix: {gpu.price_per_hour}€/h")
229
+ ```
230
+
231
+ ### 8. Générer une clé SDK (sk_live_)
232
+
233
+ ```python
234
+ # Générer une clé pour utiliser le SDK clouditia
235
+ sdk_key = manager.generate_sdk_key("0e4c713a", name="Ma clé SDK")
236
+ print(f"Clé SDK: {sdk_key}") # sk_live_xxxxx...
237
+
238
+ # Utiliser avec le SDK clouditia
239
+ from clouditia import GPUSession
240
+ gpu = GPUSession(api_key=sdk_key)
241
+ result = gpu.run("print('Hello GPU!')")
242
+ ```
243
+
244
+ ## Types de GPU disponibles
245
+
246
+ | GPU | Slug |
247
+ |-----|------|
248
+ | NVIDIA RTX 3060 Ti | `nvidia-rtx-3060ti` |
249
+ | NVIDIA RTX 3080 Ti | `nvidia-rtx-3080ti` |
250
+ | NVIDIA RTX 3090 | `nvidia-rtx-3090` |
251
+ | NVIDIA RTX 4090 | `nvidia-rtx-4090` |
252
+
253
+ ## Gestion des erreurs
254
+
255
+ ```python
256
+ from clouditia_manager import (
257
+ GPUManager,
258
+ AuthenticationError,
259
+ SessionNotFoundError,
260
+ InsufficientResourcesError,
261
+ APIError
262
+ )
263
+
264
+ try:
265
+ manager = GPUManager(api_key="sk_compute_xxxxx")
266
+ session = manager.create_session(gpu_type="nvidia-rtx-4090")
267
+ except AuthenticationError:
268
+ print("Clé API invalide")
269
+ except InsufficientResourcesError:
270
+ print("Aucun GPU disponible")
271
+ except SessionNotFoundError:
272
+ print("Session non trouvée")
273
+ except APIError as e:
274
+ print(f"Erreur API: {e}")
275
+ ```
276
+
277
+ ## Référence API
278
+
279
+ | Méthode | Description |
280
+ |---------|-------------|
281
+ | `GPUManager(api_key, base_url, timeout)` | Initialise le SDK |
282
+ | `create_session(gpu_type, gpu_count, vcpu, ram, storage, wait_ready, timeout, verbose)` | Crée une session GPU |
283
+ | `stop_session(session_id, wait_stopped, timeout, verbose)` | Arrête une session |
284
+ | `get_session(session_id)` | Récupère les détails d'une session |
285
+ | `list_sessions(status)` | Liste les sessions (filtre optionnel) |
286
+ | `rename_session(session_id, new_name)` | Renomme une session |
287
+ | `get_inventory()` | Récupère l'inventaire GPU |
288
+ | `generate_sdk_key(session_id, name)` | Génère une clé sk_live_ |
289
+
290
+ ## Attributs GPUSession
291
+
292
+ | Attribut | Type | Description |
293
+ |----------|------|-------------|
294
+ | `id` | str | UUID complet de la session |
295
+ | `short_id` | str | ID court (8 caractères) |
296
+ | `name` | str | Nom de la session |
297
+ | `status` | str | running, stopped, pending, failed |
298
+ | `gpu_type` | str | Type de GPU |
299
+ | `gpu_count` | int | Nombre de GPUs |
300
+ | `vcpu` | int | Nombre de vCPUs |
301
+ | `ram` | str | RAM allouée |
302
+ | `storage` | str | Stockage alloué |
303
+ | `url` | str | URL d'accès |
304
+ | `password` | str | Mot de passe |
305
+
306
+ ## License
307
+
308
+ MIT License
@@ -0,0 +1,7 @@
1
+ clouditia_manager/__init__.py,sha256=1kv3zDC8klv_X3pPL5JmyMmMu6CjlRCvjJ8naGjd9lo,849
2
+ clouditia_manager/client.py,sha256=Jfh9VHzAWK1LuifPfS_A39lv7jvr00Nef7fYNKHpHxw,17301
3
+ clouditia_manager/exceptions.py,sha256=ysn_iuAkyXDO0b3v-AzQmnpqjKuOoo6tVN8VIvCTTYI,753
4
+ clouditia_manager-1.0.0.dist-info/METADATA,sha256=l-KGFW_qYqVgJQW0kBNKZ6EZJdxh2BAjxDGL45jui_0,8937
5
+ clouditia_manager-1.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
6
+ clouditia_manager-1.0.0.dist-info/top_level.txt,sha256=rkZJF7zMHNO17z7JYEKk20GrJ30bSNKetDOESvwkA-k,18
7
+ clouditia_manager-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ clouditia_manager