hypercli-sdk 0.4.7__tar.gz → 0.5.0__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.
- {hypercli_sdk-0.4.7 → hypercli_sdk-0.5.0}/PKG-INFO +1 -1
- {hypercli_sdk-0.4.7 → hypercli_sdk-0.5.0}/hypercli/__init__.py +3 -2
- {hypercli_sdk-0.4.7 → hypercli_sdk-0.5.0}/hypercli/job/__init__.py +2 -0
- hypercli_sdk-0.5.0/hypercli/job/gradio.py +118 -0
- {hypercli_sdk-0.4.7 → hypercli_sdk-0.5.0}/hypercli/renders.py +76 -0
- {hypercli_sdk-0.4.7 → hypercli_sdk-0.5.0}/pyproject.toml +1 -1
- {hypercli_sdk-0.4.7 → hypercli_sdk-0.5.0}/.gitignore +0 -0
- {hypercli_sdk-0.4.7 → hypercli_sdk-0.5.0}/README.md +0 -0
- {hypercli_sdk-0.4.7 → hypercli_sdk-0.5.0}/hypercli/billing.py +0 -0
- {hypercli_sdk-0.4.7 → hypercli_sdk-0.5.0}/hypercli/client.py +0 -0
- {hypercli_sdk-0.4.7 → hypercli_sdk-0.5.0}/hypercli/config.py +0 -0
- {hypercli_sdk-0.4.7 → hypercli_sdk-0.5.0}/hypercli/files.py +0 -0
- {hypercli_sdk-0.4.7 → hypercli_sdk-0.5.0}/hypercli/http.py +0 -0
- {hypercli_sdk-0.4.7 → hypercli_sdk-0.5.0}/hypercli/instances.py +0 -0
- {hypercli_sdk-0.4.7 → hypercli_sdk-0.5.0}/hypercli/job/base.py +0 -0
- {hypercli_sdk-0.4.7 → hypercli_sdk-0.5.0}/hypercli/job/comfyui.py +0 -0
- {hypercli_sdk-0.4.7 → hypercli_sdk-0.5.0}/hypercli/jobs.py +0 -0
- {hypercli_sdk-0.4.7 → hypercli_sdk-0.5.0}/hypercli/logs.py +0 -0
- {hypercli_sdk-0.4.7 → hypercli_sdk-0.5.0}/hypercli/user.py +0 -0
- {hypercli_sdk-0.4.7 → hypercli_sdk-0.5.0}/tests/test_apply_params.py +0 -0
- {hypercli_sdk-0.4.7 → hypercli_sdk-0.5.0}/tests/test_graph_to_api.py +0 -0
|
@@ -6,10 +6,10 @@ 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
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, expand_subgraphs, DEFAULT_OBJECT_INFO
|
|
9
|
+
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
|
|
10
10
|
from .logs import LogStream, stream_logs, fetch_logs
|
|
11
11
|
|
|
12
|
-
__version__ = "0.
|
|
12
|
+
__version__ = "0.5.0"
|
|
13
13
|
__all__ = [
|
|
14
14
|
"HyperCLI",
|
|
15
15
|
"configure",
|
|
@@ -42,6 +42,7 @@ __all__ = [
|
|
|
42
42
|
# Job helpers
|
|
43
43
|
"BaseJob",
|
|
44
44
|
"ComfyUIJob",
|
|
45
|
+
"GradioJob",
|
|
45
46
|
# Workflow utils
|
|
46
47
|
"apply_params",
|
|
47
48
|
"apply_graph_modes",
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""Gradio job helpers for GPU workloads running Gradio-based services"""
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
|
+
|
|
4
|
+
from .base import BaseJob
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from ..client import HyperCLI
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class GradioJob(BaseJob):
|
|
12
|
+
"""Gradio-specific job with service connection helpers.
|
|
13
|
+
|
|
14
|
+
Used for GPU workloads that expose a Gradio API (e.g., WhisperX transcription).
|
|
15
|
+
Supports HTTPS load balancer with bearer token auth (same pattern as ComfyUI).
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
DEFAULT_GPU_TYPE = "l4"
|
|
19
|
+
HEALTH_ENDPOINT = "/"
|
|
20
|
+
HEALTH_TIMEOUT = 10.0
|
|
21
|
+
GRADIO_PORT = 7860
|
|
22
|
+
|
|
23
|
+
def __init__(self, client: "HyperCLI", job, use_lb: bool = False, use_auth: bool = False):
|
|
24
|
+
super().__init__(client, job)
|
|
25
|
+
self._use_lb = use_lb
|
|
26
|
+
self.use_auth = use_auth
|
|
27
|
+
self._job_token: str | None = None
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def use_lb(self) -> bool:
|
|
31
|
+
return self._use_lb
|
|
32
|
+
|
|
33
|
+
@use_lb.setter
|
|
34
|
+
def use_lb(self, value: bool):
|
|
35
|
+
self._use_lb = value
|
|
36
|
+
self._base_url = None
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def base_url(self) -> str:
|
|
40
|
+
"""Gradio base URL - HTTPS if using lb, HTTP otherwise"""
|
|
41
|
+
if not self._base_url and self.hostname:
|
|
42
|
+
if self._use_lb:
|
|
43
|
+
self._base_url = f"https://{self.hostname}"
|
|
44
|
+
else:
|
|
45
|
+
self._base_url = f"http://{self.hostname}:{self.GRADIO_PORT}"
|
|
46
|
+
return self._base_url or ""
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def auth_headers(self) -> dict:
|
|
50
|
+
"""Headers for authenticated requests"""
|
|
51
|
+
if self.use_auth:
|
|
52
|
+
if not self._job_token:
|
|
53
|
+
self._job_token = self.client.jobs.token(self.job_id)
|
|
54
|
+
return {"Authorization": f"Bearer {self._job_token}"}
|
|
55
|
+
return super().auth_headers
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def job_token(self) -> str:
|
|
59
|
+
"""Get the job-specific bearer token for Gradio API auth"""
|
|
60
|
+
if not self._job_token:
|
|
61
|
+
self._job_token = self.client.jobs.token(self.job_id)
|
|
62
|
+
return self._job_token
|
|
63
|
+
|
|
64
|
+
@classmethod
|
|
65
|
+
def create_for_service(
|
|
66
|
+
cls,
|
|
67
|
+
client: "HyperCLI",
|
|
68
|
+
image: str,
|
|
69
|
+
gpu_type: str = None,
|
|
70
|
+
gpu_count: int = 1,
|
|
71
|
+
runtime: int = 3600,
|
|
72
|
+
lb: int = None,
|
|
73
|
+
auth: bool = True,
|
|
74
|
+
**kwargs,
|
|
75
|
+
) -> "GradioJob":
|
|
76
|
+
"""Create a new Gradio job for a specific Docker image.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
client: HyperCLI client instance
|
|
80
|
+
image: Docker image to run (e.g., "ghcr.io/comput3ai/c3-whisperx-gradio:latest")
|
|
81
|
+
gpu_type: GPU type (default: l4)
|
|
82
|
+
gpu_count: Number of GPUs
|
|
83
|
+
runtime: Max runtime in seconds
|
|
84
|
+
lb: Port for HTTPS load balancer (default: 7860). If set, uses HTTPS.
|
|
85
|
+
auth: Enable Bearer token auth on load balancer (default: True)
|
|
86
|
+
"""
|
|
87
|
+
if lb is None:
|
|
88
|
+
lb = cls.GRADIO_PORT
|
|
89
|
+
|
|
90
|
+
ports = kwargs.pop("ports", {}) or {}
|
|
91
|
+
if lb:
|
|
92
|
+
ports["lb"] = lb
|
|
93
|
+
else:
|
|
94
|
+
ports[str(cls.GRADIO_PORT)] = cls.GRADIO_PORT
|
|
95
|
+
|
|
96
|
+
job = client.jobs.create(
|
|
97
|
+
image=image,
|
|
98
|
+
gpu_type=gpu_type or cls.DEFAULT_GPU_TYPE,
|
|
99
|
+
gpu_count=gpu_count,
|
|
100
|
+
runtime=runtime,
|
|
101
|
+
ports=ports,
|
|
102
|
+
auth=auth,
|
|
103
|
+
**kwargs,
|
|
104
|
+
)
|
|
105
|
+
return cls(client, job, use_lb=bool(lb), use_auth=auth)
|
|
106
|
+
|
|
107
|
+
def connect(self):
|
|
108
|
+
"""Return a gradio_client.Client connected to this instance.
|
|
109
|
+
|
|
110
|
+
Requires: pip install gradio-client
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
gradio_client.Client configured with auth headers
|
|
114
|
+
"""
|
|
115
|
+
from gradio_client import Client
|
|
116
|
+
|
|
117
|
+
headers = self.auth_headers if self.use_auth else {}
|
|
118
|
+
return Client(self.base_url, headers=headers)
|
|
@@ -337,3 +337,79 @@ class Renders:
|
|
|
337
337
|
)
|
|
338
338
|
"""
|
|
339
339
|
return self._flow("/api/flow/first-last-frame-video", prompt=prompt, start_image_url=start_image_url, end_image_url=end_image_url, negative=negative, width=width, height=height, notify_url=notify_url)
|
|
340
|
+
|
|
341
|
+
def audio_to_text(
|
|
342
|
+
self,
|
|
343
|
+
audio_url: str,
|
|
344
|
+
notify_url: str = None,
|
|
345
|
+
) -> Render:
|
|
346
|
+
"""Transcribe audio/video to text using WhisperX.
|
|
347
|
+
|
|
348
|
+
Args:
|
|
349
|
+
audio_url: URL of the audio or video file to transcribe
|
|
350
|
+
notify_url: Optional webhook URL for completion notification
|
|
351
|
+
|
|
352
|
+
Returns:
|
|
353
|
+
Render object. When completed, result_url points to a JSON file
|
|
354
|
+
containing {"text": "transcription..."}.
|
|
355
|
+
|
|
356
|
+
Example:
|
|
357
|
+
render = client.renders.audio_to_text("https://example.com/recording.mp3")
|
|
358
|
+
"""
|
|
359
|
+
return self._flow("/api/flow/audio-to-text", audio_url=audio_url, notify_url=notify_url)
|
|
360
|
+
|
|
361
|
+
def text_to_speech(
|
|
362
|
+
self,
|
|
363
|
+
text: str,
|
|
364
|
+
mode: str = "custom",
|
|
365
|
+
language: str = "Auto",
|
|
366
|
+
speaker: str = None,
|
|
367
|
+
style: str = None,
|
|
368
|
+
model_size: str = None,
|
|
369
|
+
voice_description: str = None,
|
|
370
|
+
ref_audio_url: str = None,
|
|
371
|
+
ref_text: str = None,
|
|
372
|
+
use_xvector_only: bool = None,
|
|
373
|
+
notify_url: str = None,
|
|
374
|
+
) -> Render:
|
|
375
|
+
"""Generate speech from text using Qwen3-TTS.
|
|
376
|
+
|
|
377
|
+
Three modes:
|
|
378
|
+
- "custom": Predefined speakers with optional style instructions
|
|
379
|
+
- "design": Describe any voice in natural language
|
|
380
|
+
- "clone": Clone a voice from reference audio
|
|
381
|
+
|
|
382
|
+
Args:
|
|
383
|
+
text: Text to synthesize
|
|
384
|
+
mode: TTS mode ("custom", "design", or "clone")
|
|
385
|
+
language: Language (Auto, English, Chinese, Japanese, Korean, etc.)
|
|
386
|
+
speaker: Speaker name for custom mode (Ryan, Serena, etc.)
|
|
387
|
+
style: Style instruction for custom mode (e.g. "Speak cheerfully")
|
|
388
|
+
model_size: Model size ("0.6B" or "1.7B")
|
|
389
|
+
voice_description: Voice description for design mode
|
|
390
|
+
ref_audio_url: Reference audio URL for clone mode
|
|
391
|
+
ref_text: Transcript of reference audio for clone mode
|
|
392
|
+
use_xvector_only: Use x-vector only for clone mode
|
|
393
|
+
notify_url: Optional webhook URL for completion notification
|
|
394
|
+
|
|
395
|
+
Returns:
|
|
396
|
+
Render object. When completed, result_url points to the WAV audio file.
|
|
397
|
+
|
|
398
|
+
Example:
|
|
399
|
+
render = client.renders.text_to_speech("Hello!", mode="design",
|
|
400
|
+
voice_description="A young Indian male, enthusiastic")
|
|
401
|
+
"""
|
|
402
|
+
return self._flow(
|
|
403
|
+
"/api/flow/text-to-speech",
|
|
404
|
+
text=text,
|
|
405
|
+
mode=mode,
|
|
406
|
+
language=language,
|
|
407
|
+
speaker=speaker,
|
|
408
|
+
style=style,
|
|
409
|
+
model_size=model_size,
|
|
410
|
+
voice_description=voice_description,
|
|
411
|
+
ref_audio_url=ref_audio_url,
|
|
412
|
+
ref_text=ref_text,
|
|
413
|
+
use_xvector_only=use_xvector_only,
|
|
414
|
+
notify_url=notify_url,
|
|
415
|
+
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|