hypercli-sdk 0.8.7__tar.gz → 0.8.9__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 (25) hide show
  1. {hypercli_sdk-0.8.7 → hypercli_sdk-0.8.9}/PKG-INFO +1 -1
  2. {hypercli_sdk-0.8.7 → hypercli_sdk-0.8.9}/hypercli/__init__.py +4 -2
  3. {hypercli_sdk-0.8.7 → hypercli_sdk-0.8.9}/hypercli/x402.py +124 -17
  4. {hypercli_sdk-0.8.7 → hypercli_sdk-0.8.9}/pyproject.toml +1 -1
  5. {hypercli_sdk-0.8.7 → hypercli_sdk-0.8.9}/.gitignore +0 -0
  6. {hypercli_sdk-0.8.7 → hypercli_sdk-0.8.9}/README.md +0 -0
  7. {hypercli_sdk-0.8.7 → hypercli_sdk-0.8.9}/hypercli/billing.py +0 -0
  8. {hypercli_sdk-0.8.7 → hypercli_sdk-0.8.9}/hypercli/claw.py +0 -0
  9. {hypercli_sdk-0.8.7 → hypercli_sdk-0.8.9}/hypercli/client.py +0 -0
  10. {hypercli_sdk-0.8.7 → hypercli_sdk-0.8.9}/hypercli/config.py +0 -0
  11. {hypercli_sdk-0.8.7 → hypercli_sdk-0.8.9}/hypercli/files.py +0 -0
  12. {hypercli_sdk-0.8.7 → hypercli_sdk-0.8.9}/hypercli/http.py +0 -0
  13. {hypercli_sdk-0.8.7 → hypercli_sdk-0.8.9}/hypercli/instances.py +0 -0
  14. {hypercli_sdk-0.8.7 → hypercli_sdk-0.8.9}/hypercli/job/__init__.py +0 -0
  15. {hypercli_sdk-0.8.7 → hypercli_sdk-0.8.9}/hypercli/job/base.py +0 -0
  16. {hypercli_sdk-0.8.7 → hypercli_sdk-0.8.9}/hypercli/job/comfyui.py +0 -0
  17. {hypercli_sdk-0.8.7 → hypercli_sdk-0.8.9}/hypercli/job/gradio.py +0 -0
  18. {hypercli_sdk-0.8.7 → hypercli_sdk-0.8.9}/hypercli/jobs.py +0 -0
  19. {hypercli_sdk-0.8.7 → hypercli_sdk-0.8.9}/hypercli/keys.py +0 -0
  20. {hypercli_sdk-0.8.7 → hypercli_sdk-0.8.9}/hypercli/logs.py +0 -0
  21. {hypercli_sdk-0.8.7 → hypercli_sdk-0.8.9}/hypercli/renders.py +0 -0
  22. {hypercli_sdk-0.8.7 → hypercli_sdk-0.8.9}/hypercli/user.py +0 -0
  23. {hypercli_sdk-0.8.7 → hypercli_sdk-0.8.9}/tests/test_apply_params.py +0 -0
  24. {hypercli_sdk-0.8.7 → hypercli_sdk-0.8.9}/tests/test_claw.py +0 -0
  25. {hypercli_sdk-0.8.7 → hypercli_sdk-0.8.9}/tests/test_graph_to_api.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hypercli-sdk
3
- Version: 0.8.7
3
+ Version: 0.8.9
4
4
  Summary: Python SDK for HyperCLI - GPU orchestration and LLM API
5
5
  Project-URL: Homepage, https://hypercli.com
6
6
  Project-URL: Documentation, https://docs.hypercli.com
@@ -5,13 +5,13 @@ from .http import APIError, AsyncHTTPClient
5
5
  from .instances import GPUType, GPUConfig, Region, GPUPricing, PricingTier
6
6
  from .jobs import Job, JobMetrics, GPUMetrics, find_job, find_by_id, find_by_hostname, find_by_ip
7
7
  from .renders import Render, RenderStatus
8
- from .x402 import X402Client, X402JobLaunch, X402RenderCreate
8
+ from .x402 import X402Client, X402JobLaunch, X402FlowCreate, X402RenderCreate, FlowCatalogItem
9
9
  from .files import File, AsyncFiles
10
10
  from .job import BaseJob, ComfyUIJob, GradioJob, apply_params, apply_graph_modes, find_node, find_nodes, load_template, graph_to_api, expand_subgraphs, DEFAULT_OBJECT_INFO
11
11
  from .logs import LogStream, stream_logs, fetch_logs
12
12
  from .claw import Claw, ClawKey, ClawPlan, ClawModel
13
13
 
14
- __version__ = "0.8.7"
14
+ __version__ = "0.8.9"
15
15
  __all__ = [
16
16
  "HyperCLI",
17
17
  "configure",
@@ -35,7 +35,9 @@ __all__ = [
35
35
  # x402 API
36
36
  "X402Client",
37
37
  "X402JobLaunch",
38
+ "X402FlowCreate",
38
39
  "X402RenderCreate",
40
+ "FlowCatalogItem",
39
41
  # Files API
40
42
  "File",
41
43
  "AsyncFiles",
@@ -1,4 +1,4 @@
1
- """x402 payment helpers for pay-per-use job and render launches."""
1
+ """x402 payment helpers for pay-per-use job and flow launches."""
2
2
  from __future__ import annotations
3
3
 
4
4
  import base64
@@ -35,8 +35,8 @@ class X402JobLaunch:
35
35
 
36
36
 
37
37
  @dataclass
38
- class X402RenderCreate:
39
- """Response payload for x402 render creation."""
38
+ class X402FlowCreate:
39
+ """Response payload for x402 flow render creation."""
40
40
 
41
41
  render: Render
42
42
  access_key: str
@@ -44,7 +44,7 @@ class X402RenderCreate:
44
44
  cancel_url: str
45
45
 
46
46
  @classmethod
47
- def from_dict(cls, data: dict[str, Any]) -> "X402RenderCreate":
47
+ def from_dict(cls, data: dict[str, Any]) -> "X402FlowCreate":
48
48
  return cls(
49
49
  render=Render.from_dict(data.get("render", {})),
50
50
  access_key=data.get("access_key", ""),
@@ -53,6 +53,34 @@ class X402RenderCreate:
53
53
  )
54
54
 
55
55
 
56
+ @dataclass
57
+ class FlowCatalogItem:
58
+ """Public flow catalog entry."""
59
+
60
+ flow_type: str
61
+ price_usd: float
62
+ template: str | None = None
63
+ type: str = "comfyui"
64
+ regions: dict[str, str] | None = None
65
+ interruptible: bool | None = None
66
+
67
+ @classmethod
68
+ def from_dict(cls, data: dict[str, Any]) -> "FlowCatalogItem":
69
+ flow_type = str(data.get("flow_type") or data.get("name") or "")
70
+ return cls(
71
+ flow_type=flow_type,
72
+ price_usd=float(data.get("price_usd", 0)),
73
+ template=data.get("template"),
74
+ type=str(data.get("type", "comfyui")),
75
+ regions=data.get("regions") if isinstance(data.get("regions"), dict) else {},
76
+ interruptible=data.get("interruptible"),
77
+ )
78
+
79
+
80
+ # Backward-compat alias. Raw x402 render endpoint was replaced by flow endpoints.
81
+ X402RenderCreate = X402FlowCreate
82
+
83
+
56
84
  def _require_x402_deps():
57
85
  try:
58
86
  from x402 import x402ClientSync
@@ -62,7 +90,7 @@ def _require_x402_deps():
62
90
  return x402ClientSync, x402HTTPClientSync, EthAccountSigner, register_exact_evm_client
63
91
  except ImportError as exc:
64
92
  raise RuntimeError(
65
- "x402 dependencies missing. Install with: pip install 'x402[httpx,evm]' eth-account"
93
+ "x402 dependencies missing. Install with: pip install x402[httpx,evm] eth-account"
66
94
  ) from exc
67
95
 
68
96
 
@@ -111,13 +139,77 @@ def _x402_post(
111
139
  return data
112
140
 
113
141
 
142
+ def _json_get(base_url: str, path: str, timeout: float) -> Any:
143
+ endpoint = f"{base_url.rstrip('/')}{path}"
144
+ with httpx.Client(timeout=timeout) as client:
145
+ response = client.get(endpoint)
146
+ if response.status_code >= 400:
147
+ raise APIError(response.status_code, _error_detail(response))
148
+ return response.json()
149
+
150
+
114
151
  class X402Client:
115
- """x402 pay-per-use client for launching jobs and renders without a full API account."""
152
+ """x402 pay-per-use client for launching jobs and flow renders without a full API account."""
116
153
 
117
154
  def __init__(self, api_url: str | None = None, timeout: float = 30.0):
118
155
  self.api_url = (api_url or get_api_url()).rstrip("/")
119
156
  self.timeout = timeout
120
157
 
158
+ def get_flow_catalog(self) -> list[FlowCatalogItem]:
159
+ data = _json_get(self.api_url, "/flows", self.timeout)
160
+
161
+ rows: list[dict[str, Any]] = []
162
+ if isinstance(data, list):
163
+ # Backward compatibility with old /api/flow/public response shape
164
+ rows = [row for row in data if isinstance(row, dict)]
165
+ elif isinstance(data, dict):
166
+ flows = data.get("flows")
167
+ if isinstance(flows, list):
168
+ rows = [row for row in flows if isinstance(row, dict)]
169
+
170
+ if not rows:
171
+ raise APIError(500, "Malformed flow catalog response")
172
+
173
+ expanded_rows: list[dict[str, Any]] = []
174
+ for row in rows:
175
+ flow_name = str(row.get("flow_type") or row.get("name") or "")
176
+ if not flow_name:
177
+ continue
178
+
179
+ has_details = any(k in row for k in ("template", "regions", "type"))
180
+ if has_details:
181
+ expanded_rows.append(row)
182
+ continue
183
+
184
+ flow_path = row.get("path")
185
+ if not isinstance(flow_path, str) or not flow_path:
186
+ flow_path = f"/flows/{flow_name}"
187
+
188
+ try:
189
+ detail = _json_get(self.api_url, flow_path, self.timeout)
190
+ except APIError:
191
+ detail = {}
192
+
193
+ if isinstance(detail, dict):
194
+ # Preserve index price/path while augmenting with detail fields.
195
+ merged = {**detail, **row}
196
+ expanded_rows.append(merged)
197
+ else:
198
+ expanded_rows.append(row)
199
+
200
+ items = [FlowCatalogItem.from_dict(row) for row in expanded_rows]
201
+ return [item for item in items if item.flow_type]
202
+
203
+ def get_flow_price(self, flow_type: str) -> float:
204
+ if not flow_type:
205
+ raise ValueError("flow_type is required")
206
+ for item in self.get_flow_catalog():
207
+ if item.flow_type == flow_type:
208
+ if item.price_usd <= 0:
209
+ raise APIError(500, f"Flow {flow_type} has invalid configured price")
210
+ return item.price_usd
211
+ raise APIError(404, f"Flow {flow_type} not found in flow catalog")
212
+
121
213
  def create_job(
122
214
  self,
123
215
  *,
@@ -159,25 +251,40 @@ class X402Client:
159
251
  data = _x402_post(self.api_url, "/api/x402/job", payload, account, self.timeout)
160
252
  return X402JobLaunch.from_dict(data)
161
253
 
162
- def create_render(
254
+ def create_flow(
163
255
  self,
164
256
  *,
257
+ flow_type: str,
165
258
  amount: float,
166
259
  account: Any,
167
- params: dict[str, Any],
168
- render_type: str = "comfyui",
260
+ params: dict[str, Any] | None = None,
169
261
  notify_url: str | None = None,
170
- ) -> X402RenderCreate:
262
+ ) -> X402FlowCreate:
171
263
  if amount <= 0:
172
264
  raise ValueError("amount must be greater than 0")
265
+ if not flow_type:
266
+ raise ValueError("flow_type is required")
173
267
 
174
- render_payload: dict[str, Any] = {
175
- "type": render_type,
176
- "params": params,
268
+ payload: dict[str, Any] = {
269
+ "amount": amount,
270
+ "params": params or {},
177
271
  }
178
272
  if notify_url:
179
- render_payload["notify_url"] = notify_url
273
+ payload["notify_url"] = notify_url
180
274
 
181
- payload = {"amount": amount, "render": render_payload}
182
- data = _x402_post(self.api_url, "/api/x402/render", payload, account, self.timeout)
183
- return X402RenderCreate.from_dict(data)
275
+ data = _x402_post(self.api_url, f"/api/x402/flow/{flow_type}", payload, account, self.timeout)
276
+ return X402FlowCreate.from_dict(data)
277
+
278
+ def create_render(
279
+ self,
280
+ *,
281
+ amount: float,
282
+ account: Any,
283
+ params: dict[str, Any],
284
+ render_type: str = "comfyui",
285
+ notify_url: str | None = None,
286
+ ) -> X402RenderCreate:
287
+ del amount, account, params, render_type, notify_url
288
+ raise RuntimeError(
289
+ "x402 render endpoint has been removed. Use create_flow(flow_type=..., amount=..., params=...) instead."
290
+ )
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "hypercli-sdk"
7
- version = "0.8.7"
7
+ version = "0.8.9"
8
8
  description = "Python SDK for HyperCLI - GPU orchestration and LLM API"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
File without changes
File without changes