Spaces:
Paused
Paused
File size: 2,617 Bytes
1a36c6b da839c2 2fc178d da839c2 1a36c6b da839c2 1a36c6b da839c2 2fc178d da839c2 1a36c6b da839c2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | """Modal backend: spawn a deployed worker function (worker/modal_app.py) and poll its result."""
from __future__ import annotations
import importlib
from types import ModuleType
from .sim_backend import BackendError
class ModalClient:
name = "modal"
# Functions deployed as their own app (worker/modal_isaac.py), so the big image never slows the main deploy.
APP_SUFFIX = {"isaac_render": "-isaac"}
def __init__(self, app_name: str, modal_module: ModuleType | None = None):
if modal_module is None:
try:
modal_module = importlib.import_module("modal")
except ImportError as e:
raise BackendError("the modal package is not installed (pip install -r requirements.txt)") from e
self._modal = modal_module
self.app_name = app_name
def supports(self, function: str) -> bool:
return True
def submit(self, job_input: dict, execution_timeout_s: int, function: str) -> dict:
"""Start the job and return at once. Time limits are set on the deployed functions (see modal_app.py)."""
try:
fn = self._modal.Function.from_name(self.app_name + self.APP_SUFFIX.get(function, ""), function)
call = fn.spawn(job_input)
except Exception as e:
raise BackendError(f"Modal could not start {self.app_name}/{function}: {type(e).__name__}: {e}") from e
return {"id": call.object_id, "status": "IN_QUEUE"}
def status(self, job_id: str) -> dict:
exc = self._modal.exception
try:
output = self._modal.FunctionCall.from_id(job_id).get(timeout=0)
except exc.OutputExpiredError:
return {"status": "FAILED", "error": "Modal no longer holds this run's result (results expire)"}
except exc.FunctionTimeoutError as e:
return {"status": "TIMED_OUT", "error": str(e) or "the run hit the function's time limit"}
except TimeoutError: # builtin: not finished yet (Modal does not tell queued from running apart)
return {"status": "IN_PROGRESS"}
except exc.InputCancellation:
return {"status": "CANCELLED", "error": "the run was cancelled"}
except (exc.RemoteError, exc.ExecutionError, exc.InternalError) as e:
return {"status": "FAILED", "error": f"{type(e).__name__}: {e}"}
except Exception as e: # network or auth trouble on our side: keep the current status and retry later
raise BackendError(f"Modal status check failed: {type(e).__name__}: {e}") from e
return {"status": "COMPLETED", "output": output}
|