hypercli-sdk 0.4.2__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.
@@ -0,0 +1,51 @@
1
+ # Dependencies
2
+ node_modules/
3
+ .pnp
4
+ .pnp.js
5
+
6
+ # Python
7
+ __pycache__/
8
+ *.py[cod]
9
+ *$py.class
10
+ *.so
11
+ .Python
12
+ venv/
13
+ env/
14
+ ENV/
15
+ .venv/
16
+ *.egg-info/
17
+
18
+ # Build outputs
19
+ .next
20
+ out
21
+ dist
22
+ build
23
+
24
+ # Testing
25
+ coverage
26
+
27
+ # Misc
28
+ .DS_Store
29
+ *.pem
30
+
31
+ # Debug
32
+ npm-debug.log*
33
+ yarn-debug.log*
34
+ yarn-error.log*
35
+
36
+ # Local env files
37
+ .env
38
+ .env.local
39
+ .env.development.local
40
+ .env.test.local
41
+ .env.production.local
42
+
43
+ # Vercel
44
+ .vercel
45
+
46
+ # TypeScript
47
+ *.tsbuildinfo
48
+ next-env.d.ts
49
+
50
+ # Turbo
51
+ .turbo
@@ -0,0 +1,141 @@
1
+ Metadata-Version: 2.4
2
+ Name: hypercli-sdk
3
+ Version: 0.4.2
4
+ Summary: Python SDK for HyperCLI - GPU orchestration and LLM API
5
+ Project-URL: Homepage, https://hypercli.com
6
+ Project-URL: Documentation, https://docs.hypercli.com
7
+ Project-URL: Repository, https://github.com/hypercli/sdk-python
8
+ Author-email: HyperCLI <support@hypercli.com>
9
+ License-Expression: MIT
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Requires-Python: >=3.10
17
+ Requires-Dist: httpx>=0.28.1
18
+ Requires-Dist: websockets>=15.0.1
19
+ Provides-Extra: comfyui
20
+ Requires-Dist: comfyui-workflow-templates-media-image>=0.3.0; extra == 'comfyui'
21
+ Requires-Dist: comfyui-workflow-templates>=0.7.0; extra == 'comfyui'
22
+ Provides-Extra: dev
23
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
24
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
25
+ Requires-Dist: ruff>=0.3.0; extra == 'dev'
26
+ Description-Content-Type: text/markdown
27
+
28
+ # C3 SDK
29
+
30
+ Python SDK for [HyperCLI](https://hypercli.com) - GPU orchestration API.
31
+
32
+ ## Installation
33
+
34
+ ```bash
35
+ pip install c3-sdk
36
+ ```
37
+
38
+ ## Setup
39
+
40
+ Set your API key:
41
+
42
+ ```bash
43
+ export C3_API_KEY=your_api_key
44
+ ```
45
+
46
+ Or create `~/.c3/config`:
47
+ ```
48
+ C3_API_KEY=your_api_key
49
+ ```
50
+
51
+ Or pass directly:
52
+ ```python
53
+ c3 = C3(api_key="your_api_key")
54
+ ```
55
+
56
+ ## Usage
57
+
58
+ ```python
59
+ from c3 import C3
60
+
61
+ c3 = C3()
62
+
63
+ # Check balance
64
+ balance = c3.billing.balance()
65
+ print(f"Balance: ${balance.total:.2f}")
66
+ print(f"Rewards: ${balance.rewards:.2f}")
67
+
68
+ # List transactions
69
+ for tx in c3.billing.transactions(limit=10):
70
+ print(f"{tx.transaction_type}: ${tx.amount_usd:.4f}")
71
+
72
+ # Create a job
73
+ job = c3.jobs.create(
74
+ image="nvidia/cuda:12.0",
75
+ command="python train.py",
76
+ gpu_type="l40s",
77
+ gpu_count=1,
78
+ )
79
+ print(f"Job ID: {job.job_id}")
80
+ print(f"State: {job.state}")
81
+
82
+ # List jobs
83
+ for job in c3.jobs.list():
84
+ print(f"{job.job_id}: {job.state}")
85
+
86
+ # Get job details
87
+ job = c3.jobs.get("job_id")
88
+
89
+ # Get job logs
90
+ logs = c3.jobs.logs("job_id")
91
+
92
+ # Get GPU metrics
93
+ metrics = c3.jobs.metrics("job_id")
94
+ for gpu in metrics.gpus:
95
+ print(f"GPU {gpu.index}: {gpu.utilization}% util, {gpu.temperature}°C")
96
+
97
+ # Cancel a job
98
+ c3.jobs.cancel("job_id")
99
+
100
+ # Extend runtime
101
+ c3.jobs.extend("job_id", runtime=7200)
102
+
103
+ # Get user info
104
+ user = c3.user.get()
105
+ print(f"User: {user.email}")
106
+ ```
107
+
108
+ ## LLM API
109
+
110
+ For LLM access, use the OpenAI SDK with C3's base URL:
111
+
112
+ ```python
113
+ from openai import OpenAI
114
+
115
+ client = OpenAI(
116
+ api_key="your_c3_api_key",
117
+ base_url="https://api.hypercli.com/v1"
118
+ )
119
+
120
+ response = client.chat.completions.create(
121
+ model="deepseek-v3.1",
122
+ messages=[{"role": "user", "content": "Hello!"}]
123
+ )
124
+ ```
125
+
126
+ ## Error Handling
127
+
128
+ ```python
129
+ from c3 import C3, APIError
130
+
131
+ c3 = C3()
132
+
133
+ try:
134
+ job = c3.jobs.get("invalid_id")
135
+ except APIError as e:
136
+ print(f"Error {e.status_code}: {e.detail}")
137
+ ```
138
+
139
+ ## License
140
+
141
+ MIT
@@ -0,0 +1,114 @@
1
+ # C3 SDK
2
+
3
+ Python SDK for [HyperCLI](https://hypercli.com) - GPU orchestration API.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install c3-sdk
9
+ ```
10
+
11
+ ## Setup
12
+
13
+ Set your API key:
14
+
15
+ ```bash
16
+ export C3_API_KEY=your_api_key
17
+ ```
18
+
19
+ Or create `~/.c3/config`:
20
+ ```
21
+ C3_API_KEY=your_api_key
22
+ ```
23
+
24
+ Or pass directly:
25
+ ```python
26
+ c3 = C3(api_key="your_api_key")
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ ```python
32
+ from c3 import C3
33
+
34
+ c3 = C3()
35
+
36
+ # Check balance
37
+ balance = c3.billing.balance()
38
+ print(f"Balance: ${balance.total:.2f}")
39
+ print(f"Rewards: ${balance.rewards:.2f}")
40
+
41
+ # List transactions
42
+ for tx in c3.billing.transactions(limit=10):
43
+ print(f"{tx.transaction_type}: ${tx.amount_usd:.4f}")
44
+
45
+ # Create a job
46
+ job = c3.jobs.create(
47
+ image="nvidia/cuda:12.0",
48
+ command="python train.py",
49
+ gpu_type="l40s",
50
+ gpu_count=1,
51
+ )
52
+ print(f"Job ID: {job.job_id}")
53
+ print(f"State: {job.state}")
54
+
55
+ # List jobs
56
+ for job in c3.jobs.list():
57
+ print(f"{job.job_id}: {job.state}")
58
+
59
+ # Get job details
60
+ job = c3.jobs.get("job_id")
61
+
62
+ # Get job logs
63
+ logs = c3.jobs.logs("job_id")
64
+
65
+ # Get GPU metrics
66
+ metrics = c3.jobs.metrics("job_id")
67
+ for gpu in metrics.gpus:
68
+ print(f"GPU {gpu.index}: {gpu.utilization}% util, {gpu.temperature}°C")
69
+
70
+ # Cancel a job
71
+ c3.jobs.cancel("job_id")
72
+
73
+ # Extend runtime
74
+ c3.jobs.extend("job_id", runtime=7200)
75
+
76
+ # Get user info
77
+ user = c3.user.get()
78
+ print(f"User: {user.email}")
79
+ ```
80
+
81
+ ## LLM API
82
+
83
+ For LLM access, use the OpenAI SDK with C3's base URL:
84
+
85
+ ```python
86
+ from openai import OpenAI
87
+
88
+ client = OpenAI(
89
+ api_key="your_c3_api_key",
90
+ base_url="https://api.hypercli.com/v1"
91
+ )
92
+
93
+ response = client.chat.completions.create(
94
+ model="deepseek-v3.1",
95
+ messages=[{"role": "user", "content": "Hello!"}]
96
+ )
97
+ ```
98
+
99
+ ## Error Handling
100
+
101
+ ```python
102
+ from c3 import C3, APIError
103
+
104
+ c3 = C3()
105
+
106
+ try:
107
+ job = c3.jobs.get("invalid_id")
108
+ except APIError as e:
109
+ print(f"Error {e.status_code}: {e.detail}")
110
+ ```
111
+
112
+ ## License
113
+
114
+ MIT
@@ -0,0 +1,57 @@
1
+ """C3 SDK - Python client for HyperCLI API"""
2
+ from .client import C3
3
+ from .config import configure, GHCR_IMAGES, COMFYUI_IMAGE
4
+ from .http import APIError, AsyncHTTPClient
5
+ from .instances import GPUType, GPUConfig, Region, GPUPricing, PricingTier
6
+ from .jobs import Job, JobMetrics, GPUMetrics, find_job, find_by_id, find_by_hostname, find_by_ip
7
+ from .renders import Render, RenderStatus
8
+ from .files import File, AsyncFiles
9
+ from .job import BaseJob, ComfyUIJob, apply_params, apply_graph_modes, find_node, find_nodes, load_template, graph_to_api, DEFAULT_OBJECT_INFO
10
+ from .logs import LogStream, stream_logs, fetch_logs
11
+
12
+ __version__ = "0.2.1"
13
+ __all__ = [
14
+ "C3",
15
+ "configure",
16
+ "APIError",
17
+ # Images
18
+ "GHCR_IMAGES",
19
+ "COMFYUI_IMAGE",
20
+ # Instance types
21
+ "GPUType",
22
+ "GPUConfig",
23
+ "Region",
24
+ "GPUPricing",
25
+ "PricingTier",
26
+ # Jobs API
27
+ "Job",
28
+ "JobMetrics",
29
+ "GPUMetrics",
30
+ # Renders API
31
+ "Render",
32
+ "RenderStatus",
33
+ # Files API
34
+ "File",
35
+ "AsyncFiles",
36
+ "AsyncHTTPClient",
37
+ # Job lookup utils
38
+ "find_job",
39
+ "find_by_id",
40
+ "find_by_hostname",
41
+ "find_by_ip",
42
+ # Job helpers
43
+ "BaseJob",
44
+ "ComfyUIJob",
45
+ # Workflow utils
46
+ "apply_params",
47
+ "apply_graph_modes",
48
+ "find_node",
49
+ "find_nodes",
50
+ "load_template",
51
+ "graph_to_api",
52
+ "DEFAULT_OBJECT_INFO",
53
+ # Log streaming
54
+ "LogStream",
55
+ "stream_logs",
56
+ "fetch_logs",
57
+ ]
@@ -0,0 +1,72 @@
1
+ """Billing API"""
2
+ from dataclasses import dataclass
3
+ from typing import TYPE_CHECKING
4
+
5
+ if TYPE_CHECKING:
6
+ from .http import HTTPClient
7
+
8
+
9
+ @dataclass
10
+ class Balance:
11
+ total: str
12
+ rewards: str
13
+ paid: str
14
+ available: str
15
+
16
+ @classmethod
17
+ def from_dict(cls, data: dict) -> "Balance":
18
+ return cls(
19
+ total=data.get("total_balance", "0"),
20
+ rewards=data.get("rewards_balance", "0"),
21
+ paid=data.get("balance", "0"),
22
+ available=data.get("available_balance", "0"),
23
+ )
24
+
25
+
26
+ @dataclass
27
+ class Transaction:
28
+ id: str
29
+ user_id: str
30
+ amount: int
31
+ amount_usd: float
32
+ transaction_type: str
33
+ status: str
34
+ rewards: bool
35
+ job_id: str | None
36
+ created_at: str
37
+
38
+ @classmethod
39
+ def from_dict(cls, data: dict) -> "Transaction":
40
+ return cls(
41
+ id=data.get("id", ""),
42
+ user_id=data.get("user_id", ""),
43
+ amount=data.get("amount", 0),
44
+ amount_usd=data.get("amount_usd", 0),
45
+ transaction_type=data.get("transaction_type", ""),
46
+ status=data.get("status", ""),
47
+ rewards=data.get("rewards", False),
48
+ job_id=data.get("job_id"),
49
+ created_at=data.get("created_at", ""),
50
+ )
51
+
52
+
53
+ class Billing:
54
+ """Billing API wrapper"""
55
+
56
+ def __init__(self, http: "HTTPClient"):
57
+ self._http = http
58
+
59
+ def balance(self) -> Balance:
60
+ """Get account balance"""
61
+ data = self._http.get("/api/balance")
62
+ return Balance.from_dict(data)
63
+
64
+ def transactions(self, limit: int = 50, page: int = 1) -> list[Transaction]:
65
+ """List transactions"""
66
+ data = self._http.get("/api/tx", params={"page": page, "page_size": limit})
67
+ return [Transaction.from_dict(tx) for tx in data.get("transactions", [])]
68
+
69
+ def get_transaction(self, transaction_id: str) -> Transaction:
70
+ """Get a specific transaction"""
71
+ data = self._http.get(f"/api/tx/{transaction_id}")
72
+ return Transaction.from_dict(data)
@@ -0,0 +1,60 @@
1
+ """Main C3 client"""
2
+ from .config import get_api_key, get_api_url
3
+ from .http import HTTPClient
4
+ from .billing import Billing
5
+ from .jobs import Jobs
6
+ from .user import UserAPI
7
+ from .instances import Instances
8
+ from .renders import Renders
9
+ from .files import Files
10
+
11
+
12
+ class C3:
13
+ """
14
+ C3 API Client
15
+
16
+ Usage:
17
+ from c3 import C3
18
+
19
+ c3 = C3() # Uses C3_API_KEY from env or ~/.c3/config
20
+ # or
21
+ c3 = C3(api_key="your_key")
22
+
23
+ # Billing
24
+ balance = c3.billing.balance()
25
+ print(f"Balance: ${balance.total}")
26
+
27
+ # Jobs
28
+ job = c3.jobs.create(
29
+ image="nvidia/cuda:12.0",
30
+ gpu_type="l40s",
31
+ command="python train.py"
32
+ )
33
+ print(f"Job: {job.job_id}")
34
+
35
+ # User
36
+ user = c3.user.get()
37
+ """
38
+
39
+ def __init__(self, api_key: str = None, api_url: str = None):
40
+ self._api_key = api_key or get_api_key()
41
+ if not self._api_key:
42
+ raise ValueError(
43
+ "API key required. Set C3_API_KEY env var, "
44
+ "create ~/.c3/config, or pass api_key parameter."
45
+ )
46
+
47
+ self._api_url = api_url or get_api_url()
48
+ self._http = HTTPClient(self._api_url, self._api_key)
49
+
50
+ # API namespaces
51
+ self.billing = Billing(self._http)
52
+ self.jobs = Jobs(self._http)
53
+ self.user = UserAPI(self._http)
54
+ self.instances = Instances(self._http)
55
+ self.renders = Renders(self._http)
56
+ self.files = Files(self._http)
57
+
58
+ @property
59
+ def api_url(self) -> str:
60
+ return self._api_url
@@ -0,0 +1,70 @@
1
+ """Configuration handling"""
2
+ import os
3
+ from pathlib import Path
4
+ from typing import Optional
5
+
6
+ CONFIG_DIR = Path.home() / ".c3"
7
+ CONFIG_FILE = CONFIG_DIR / "config"
8
+
9
+ DEFAULT_API_URL = "https://api.hypercli.com"
10
+ DEFAULT_WS_URL = "wss://api.hypercli.com"
11
+ WS_LOGS_PATH = "/orchestra/ws/logs" # WebSocket path for job logs: {WS_URL}{WS_LOGS_PATH}/{job_key}
12
+
13
+ # GHCR images
14
+ GHCR_IMAGES = "ghcr.io/hypercliai/images"
15
+ COMFYUI_IMAGE = f"{GHCR_IMAGES}/comfyui"
16
+
17
+
18
+ def _load_config_file() -> dict:
19
+ """Load config from ~/.c3/config"""
20
+ config = {}
21
+ if CONFIG_FILE.exists():
22
+ for line in CONFIG_FILE.read_text().splitlines():
23
+ line = line.strip()
24
+ if line and not line.startswith("#") and "=" in line:
25
+ key, value = line.split("=", 1)
26
+ config[key.strip()] = value.strip()
27
+ return config
28
+
29
+
30
+ def get_config_value(key: str, default: str = None) -> Optional[str]:
31
+ """Get config value: env var > config file > default"""
32
+ env_val = os.getenv(key)
33
+ if env_val:
34
+ return env_val
35
+ config = _load_config_file()
36
+ return config.get(key, default)
37
+
38
+
39
+ def get_api_key() -> Optional[str]:
40
+ """Get API key from env or config file"""
41
+ return get_config_value("C3_API_KEY")
42
+
43
+
44
+ def get_api_url() -> str:
45
+ """Get API URL"""
46
+ return get_config_value("C3_API_URL", DEFAULT_API_URL)
47
+
48
+
49
+ def get_ws_url() -> str:
50
+ """Get WebSocket URL"""
51
+ ws = get_config_value("C3_WS_URL")
52
+ if ws:
53
+ return ws
54
+ # Derive from API URL
55
+ api = get_api_url()
56
+ return api.replace("https://", "wss://").replace("http://", "ws://")
57
+
58
+
59
+ def configure(api_key: str, api_url: str = None):
60
+ """Save configuration to ~/.c3/config"""
61
+ CONFIG_DIR.mkdir(parents=True, exist_ok=True)
62
+
63
+ config = _load_config_file()
64
+ config["C3_API_KEY"] = api_key
65
+ if api_url:
66
+ config["C3_API_URL"] = api_url
67
+
68
+ lines = [f"{k}={v}" for k, v in config.items()]
69
+ CONFIG_FILE.write_text("\n".join(lines) + "\n")
70
+ CONFIG_FILE.chmod(0o600)