Skip to content

Commit c1dfc72

Browse files
authored
Merge pull request #266 from Runware/feature-addtargetMegapixelsParam
Added async flow in imageUpscale, alias for IUpscaleSettings as ISettings
2 parents 622657e + 43632a9 commit c1dfc72

File tree

2 files changed

+85
-28
lines changed

2 files changed

+85
-28
lines changed

runware/base.py

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1274,7 +1274,11 @@ async def _removeImageBackground(
12741274

12751275
async def imageUpscale(self, upscaleGanPayload: "IImageUpscale") -> "Union[List[IImage], IAsyncTaskResponse]":
12761276
async with self._request_semaphore:
1277-
return await self._retry_with_reconnect(self._imageUpscale, upscaleGanPayload)
1277+
return await self._retry_async_with_reconnect(
1278+
self._imageUpscale,
1279+
upscaleGanPayload,
1280+
task_type=ETaskType.IMAGE_UPSCALE.value,
1281+
)
12781282

12791283
async def _imageUpscale(self, upscaleGanPayload: IImageUpscale) -> Union[List[IImage], IAsyncTaskResponse]:
12801284
await self.ensureConnection()
@@ -1297,12 +1301,15 @@ async def _upscaleGan(self, upscaleGanPayload: "IImageUpscale") -> "Union[List[I
12971301
taskUUID = getUUID()
12981302
upscaleGanPayload.taskUUID = taskUUID
12991303

1300-
# Create a dictionary with mandatory parameters
13011304
task_params = {
13021305
"taskType": ETaskType.IMAGE_UPSCALE.value,
13031306
"taskUUID": taskUUID,
1304-
"upscaleFactor": upscaleGanPayload.upscaleFactor,
1307+
"deliveryMethod": upscaleGanPayload.deliveryMethod,
13051308
}
1309+
if upscaleGanPayload.upscaleFactor is not None:
1310+
task_params["upscaleFactor"] = upscaleGanPayload.upscaleFactor
1311+
if upscaleGanPayload.targetMegapixels is not None:
1312+
task_params["targetMegapixels"] = upscaleGanPayload.targetMegapixels
13061313

13071314
# Use inputs.image format if inputs is provided, otherwise use inputImage (legacy)
13081315
if upscaleGanPayload.inputs and upscaleGanPayload.inputs.image:
@@ -1347,6 +1354,39 @@ async def _upscaleGan(self, upscaleGanPayload: "IImageUpscale") -> "Union[List[I
13471354
debug_key="image-upscale-webhook"
13481355
)
13491356

1357+
delivery_method_enum = (
1358+
EDeliveryMethod(upscaleGanPayload.deliveryMethod)
1359+
if isinstance(upscaleGanPayload.deliveryMethod, str)
1360+
else upscaleGanPayload.deliveryMethod
1361+
)
1362+
1363+
if delivery_method_enum is EDeliveryMethod.ASYNC:
1364+
future, should_send = await self._register_pending_operation(
1365+
taskUUID,
1366+
expected_results=1,
1367+
complete_predicate=lambda r: True,
1368+
)
1369+
try:
1370+
if should_send:
1371+
await self.send([task_params])
1372+
await self._mark_operation_sent(taskUUID)
1373+
results = await asyncio.wait_for(future, timeout=IMAGE_INITIAL_TIMEOUT / 1000)
1374+
response = results[0]
1375+
self._handle_error_response(response)
1376+
if response.get("status") == "success" or response.get("imageUUID") is not None:
1377+
image = createImageFromResponse(response)
1378+
return [image]
1379+
return createAsyncTaskResponse(response)
1380+
except asyncio.TimeoutError:
1381+
raise ConnectionError(
1382+
f"Timeout waiting for async image upscale acknowledgment | TaskUUID: {taskUUID} | "
1383+
f"Timeout: {IMAGE_INITIAL_TIMEOUT}ms"
1384+
)
1385+
except RunwareAPIError:
1386+
raise
1387+
finally:
1388+
await self._unregister_pending_operation(taskUUID)
1389+
13501390
future, should_send = await self._register_pending_operation(
13511391
taskUUID,
13521392
expected_results=1,
@@ -3102,6 +3142,17 @@ def configure_from_task_type(task_type_val: Optional[str]):
31023142
IMAGE_POLLING_DELAY,
31033143
f"Image generation timeout after {MAX_POLLS_IMAGE_GENERATION} polls"
31043144
)
3145+
case (
3146+
ETaskType.IMAGE_UPSCALE.value
3147+
| ETaskType.IMAGE_VECTORIZE.value
3148+
| ETaskType.IMAGE_BACKGROUND_REMOVAL.value
3149+
):
3150+
return (
3151+
IImage,
3152+
MAX_POLLS_IMAGE_GENERATION,
3153+
IMAGE_POLLING_DELAY,
3154+
f"Image task timeout after {MAX_POLLS_IMAGE_GENERATION} polls"
3155+
)
31053156
case (
31063157
ETaskType.VIDEO_INFERENCE.value
31073158
| ETaskType.VIDEO_BACKGROUND_REMOVAL.value

runware/types.py

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -866,6 +866,20 @@ class ISettings(SerializableMixin):
866866
expressiveness: Optional[str] = None
867867
removeBackground: Optional[bool] = None
868868
backgroundColor: Optional[str] = None
869+
# Image upscale
870+
steps: Optional[int] = None
871+
seed: Optional[int] = None
872+
CFGScale: Optional[float] = None
873+
positivePrompt: Optional[str] = None
874+
negativePrompt: Optional[str] = None
875+
controlNetWeight: Optional[float] = None
876+
strength: Optional[float] = None
877+
scheduler: Optional[str] = None
878+
colorFix: Optional[bool] = None
879+
tileDiffusion: Optional[bool] = None
880+
clipSkip: Optional[int] = None
881+
enhanceDetails: Optional[bool] = None
882+
realism: Optional[bool] = None
869883

870884
def __post_init__(self):
871885
if self.sparseStructure is not None and isinstance(self.sparseStructure, dict):
@@ -880,6 +894,18 @@ def request_key(self) -> str:
880894
return "settings"
881895

882896

897+
@dataclass
898+
class IUpscaleSettings(ISettings):
899+
900+
def __post_init__(self):
901+
super().__post_init__()
902+
warnings.warn(
903+
"IUpscaleSettings is deprecated and will be removed in a future release; use ISettings for image upscale settings instead.",
904+
DeprecationWarning,
905+
stacklevel=3,
906+
)
907+
908+
883909
@dataclass
884910
class IInputFrame(SerializableMixin):
885911
image: Union[str, File]
@@ -1241,45 +1267,25 @@ def __hash__(self):
12411267
return hash((self.taskType, self.taskUUID, self.text, self.cost))
12421268

12431269

1244-
@dataclass
1245-
class IUpscaleSettings:
1246-
# Common parameters across all upscaler models
1247-
steps: Optional[int] = None # Quality steps (4-60 depending on model)
1248-
seed: Optional[int] = None # Reproducibility toggle
1249-
CFGScale: Optional[float] = None # Guidance CFG (3-20 depending on model)
1250-
positivePrompt: Optional[str] = None
1251-
negativePrompt: Optional[str] = None
1252-
1253-
# Clarity upscaler specific
1254-
controlNetWeight: Optional[float] = None # Style preservation/Resemblance (0-1)
1255-
strength: Optional[float] = None # Creativity (0-1)
1256-
scheduler: Optional[str] = None # Controls noise addition/removal
1257-
1258-
# CCSR and Latent upscaler specific
1259-
colorFix: Optional[bool] = None # Color correction (ADAIN/NOFIX)
1260-
tileDiffusion: Optional[bool] = None # Tile diffusion for large images
1261-
1262-
# Latent upscaler specific
1263-
clipSkip: Optional[int] = None # Skip CLIP layers during guidance (0-2)
1264-
1265-
12661270
@dataclass
12671271
class IImageUpscale:
1268-
upscaleFactor: float # Changed to float to support decimal values like 1.5
1272+
upscaleFactor: Optional[float] = None
1273+
targetMegapixels: Optional[int] = None
12691274
inputImage: Optional[Union[str, File]] = None
12701275
model: Optional[str] = None # Model AIR ID (runware:500@1, runware:501@1, runware:502@1, runware:503@1)
1271-
settings: Optional[Union[IUpscaleSettings, Dict[str, Any]]] = None # Advanced upscaling settings
1276+
settings: Optional[Union[ISettings, Dict[str, Any]]] = None
12721277
outputType: Optional[IOutputType] = None
12731278
outputFormat: Optional[IOutputFormat] = None
12741279
includeCost: bool = False
12751280
webhookURL: Optional[str] = None
12761281
providerSettings: Optional[ImageProviderSettings] = None
12771282
safety: Optional[Union[ISafety, Dict[str, Any]]] = None
12781283
inputs: Optional[Union[IInputs, Dict[str, Any]]] = None
1284+
deliveryMethod: str = "sync"
12791285

12801286
def __post_init__(self):
12811287
if self.settings is not None and isinstance(self.settings, dict):
1282-
self.settings = IUpscaleSettings(**self.settings)
1288+
self.settings = ISettings(**self.settings)
12831289
if self.safety is not None and isinstance(self.safety, dict):
12841290
self.safety = ISafety(**self.safety)
12851291
if self.inputs is not None and isinstance(self.inputs, dict):

0 commit comments

Comments
 (0)