Spaces:
Running on Zero
Running on Zero
Download dev_client.py from speridlabs/iris-z: direct link, hf CLI and curl.
- Browser
- Download file 5.41 kB
-
https://huggingface.co/spaces/speridlabs/iris-z/resolve/main/dev_client.py
- Command line
-
hf download hf://spaces/speridlabs/iris-z/dev_client.py
-
curl -L -o dev_client.py https://huggingface.co/spaces/speridlabs/iris-z/resolve/main/dev_client.py
5.41 kB
| """Server-side HTTPS client. Credentials and Runpod URLs never reach the browser.""" | |
| import os | |
| import ssl | |
| import time | |
| import uuid | |
| from urllib.parse import urlparse | |
| import httpx | |
| from dev_protocol import JobRequest, decode_image | |
| class DevError(RuntimeError): | |
| pass | |
| def configured(): | |
| return bool(os.environ.get("IRIS_Z_DEV_URL") and os.environ.get("IRIS_Z_DEV_API_KEY")) | |
| def expected_seconds(request): | |
| """Expected GPU time of one Iris-Z big request on an H200, from measured timings (2026-11-11): 0.66 s per | |
| guided step at 512x512 and 2.45 s at 1024x1024 (time grows with pixels^0.94); depth steps run one pass, not a | |
| guided pair. The worker reports no step progress, so the Space shows elapsed / expected.""" | |
| if request.task in {"text_to_image", "text_to_rgbd"}: | |
| pixels = request.width * request.height | |
| else: | |
| pixels = request.resolution * request.resolution | |
| seconds_per_step = 0.66 * (pixels / (512 * 512)) ** 0.94 | |
| if request.task == "image_to_depth": | |
| seconds_per_step *= 0.5 | |
| return 3.0 + request.steps * seconds_per_step | |
| def run(payload, progress=None): | |
| if not configured(): | |
| raise DevError("Iris-Z big is not connected yet.") | |
| try: | |
| request = JobRequest.model_validate(payload) | |
| except ValueError: | |
| raise DevError("Choose supported Iris-Z big parameters and a valid image.") from None | |
| url = os.environ["IRIS_Z_DEV_URL"].rstrip("/") | |
| parsed = urlparse(url) | |
| if ( | |
| parsed.scheme != "https" | |
| or not parsed.hostname | |
| or parsed.username | |
| or parsed.password | |
| or parsed.query | |
| or parsed.fragment | |
| or parsed.path | |
| ): | |
| raise DevError("Iris-Z big has an invalid service configuration.") | |
| headers = { | |
| "Authorization": "Bearer " + os.environ["IRIS_Z_DEV_API_KEY"], | |
| "X-Request-ID": str(uuid.uuid4()), | |
| } | |
| deadline = time.monotonic() + 1800 | |
| def checked(response): | |
| if response.status_code >= 400: | |
| messages = { | |
| 401: "Iris-Z big authentication failed.", | |
| 403: "Iris-Z big access denied.", | |
| 429: "Iris-Z big is busy. Please try again shortly.", | |
| 503: "Iris-Z big is starting or unavailable. Please try again shortly.", | |
| 422: "Iris-Z big rejected the image or settings.", | |
| 404: "Iris-Z big job expired or the service restarted.", | |
| } | |
| raise DevError(messages.get(response.status_code, "Iris-Z big could not complete this request.")) | |
| return response.json() | |
| try: | |
| # Provisional AWS endpoints use our private CA; never disable TLS checks. | |
| certificate = os.environ.get("IRIS_Z_DEV_CA_PEM") | |
| verification = ssl.create_default_context(cadata=certificate) if certificate else True | |
| with httpx.Client( | |
| base_url=url, | |
| headers=headers, | |
| timeout=45, | |
| follow_redirects=False, | |
| trust_env=False, | |
| verify=verification, | |
| ) as client: | |
| # An ambiguous submission is retried with the same ID, never as a second GPU job. | |
| for attempt in range(3): | |
| try: | |
| job = checked(client.post("/jobs", json=request.model_dump())) | |
| break | |
| except httpx.TransportError: | |
| if attempt == 2: | |
| raise | |
| time.sleep(1) | |
| job_id = str(uuid.UUID(job["id"])) | |
| expected = expected_seconds(request) | |
| running_since = None | |
| while time.monotonic() < deadline: | |
| try: | |
| job_state = checked(client.get(f"/jobs/{job_id}")) | |
| status = job_state["status"] | |
| except httpx.TransportError: | |
| time.sleep(2) | |
| continue | |
| if status == "completed": | |
| result = checked(client.get(f"/jobs/{job_id}/result")) | |
| for key in ["image", "depth"]: | |
| if key in result: | |
| result[key] = decode_image(result[key]) | |
| return result | |
| if status == "failed": | |
| raise DevError( | |
| "Iris-Z big could not complete this request. Try a smaller image or fewer steps." | |
| ) | |
| if progress is not None: | |
| position = job_state.get("queue_position") | |
| waiting = ( | |
| f"Iris-Z big queue · approximately {position} in line" | |
| if isinstance(position, int) and position > 0 | |
| else "Waiting for Iris-Z big" | |
| ) | |
| if status == "queued": | |
| running_since = None | |
| progress(0, desc=waiting) | |
| else: | |
| running_since = running_since or time.monotonic() | |
| fraction = min(0.97, (time.monotonic() - running_since) / expected) | |
| progress(fraction, desc="Iris-Z big is generating") | |
| time.sleep(1) | |
| except (httpx.HTTPError, ValueError, KeyError): | |
| raise DevError("The connection to Iris-Z big failed. Please try again shortly.") from None | |
| raise DevError("Iris-Z big exceeded the request time limit.") | |