fix(ReCraft-API-node): allow custom multipart parser to return FormData (#10244)

This commit is contained in:
Alexander Piskun 2025-10-07 23:15:32 +03:00 committed by GitHub
parent 8aea746212
commit fc34c3d112
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 32 additions and 13 deletions

View File

@ -220,13 +220,16 @@ class ApiClient:
if multipart_parser and data: if multipart_parser and data:
data = multipart_parser(data) data = multipart_parser(data)
form = aiohttp.FormData(default_to_multipart=True) if isinstance(data, aiohttp.FormData):
if data: # regular text fields form = data # If the parser already returned a FormData, pass it through
for k, v in data.items(): else:
if v is None: form = aiohttp.FormData(default_to_multipart=True)
continue # aiohttp fails to serialize "None" values if data: # regular text fields
# aiohttp expects strings or bytes; convert enums etc. for k, v in data.items():
form.add_field(k, str(v) if not isinstance(v, (bytes, bytearray)) else v) if v is None:
continue # aiohttp fails to serialize "None" values
# aiohttp expects strings or bytes; convert enums etc.
form.add_field(k, str(v) if not isinstance(v, (bytes, bytearray)) else v)
if files: if files:
file_iter = files if isinstance(files, list) else files.items() file_iter = files if isinstance(files, list) else files.items()

View File

@ -35,6 +35,7 @@ from server import PromptServer
import torch import torch
from io import BytesIO from io import BytesIO
from PIL import UnidentifiedImageError from PIL import UnidentifiedImageError
import aiohttp
async def handle_recraft_file_request( async def handle_recraft_file_request(
@ -82,10 +83,16 @@ async def handle_recraft_file_request(
return all_bytesio return all_bytesio
def recraft_multipart_parser(data, parent_key=None, formatter: callable=None, converted_to_check: list[list]=None, is_list=False) -> dict: def recraft_multipart_parser(
data,
parent_key=None,
formatter: callable = None,
converted_to_check: list[list] = None,
is_list: bool = False,
return_mode: str = "formdata" # "dict" | "formdata"
) -> dict | aiohttp.FormData:
""" """
Formats data such that multipart/form-data will work with requests library Formats data such that multipart/form-data will work with aiohttp library when both files and data are present.
when both files and data are present.
The OpenAI client that Recraft uses has a bizarre way of serializing lists: The OpenAI client that Recraft uses has a bizarre way of serializing lists:
@ -103,19 +110,19 @@ def recraft_multipart_parser(data, parent_key=None, formatter: callable=None, co
# Modification of a function that handled a different type of multipart parsing, big ups: # Modification of a function that handled a different type of multipart parsing, big ups:
# https://gist.github.com/kazqvaizer/4cebebe5db654a414132809f9f88067b # https://gist.github.com/kazqvaizer/4cebebe5db654a414132809f9f88067b
def handle_converted_lists(data, parent_key, lists_to_check=tuple[list]): def handle_converted_lists(item, parent_key, lists_to_check=tuple[list]):
# if list already exists exists, just extend list with data # if list already exists exists, just extend list with data
for check_list in lists_to_check: for check_list in lists_to_check:
for conv_tuple in check_list: for conv_tuple in check_list:
if conv_tuple[0] == parent_key and isinstance(conv_tuple[1], list): if conv_tuple[0] == parent_key and isinstance(conv_tuple[1], list):
conv_tuple[1].append(formatter(data)) conv_tuple[1].append(formatter(item))
return True return True
return False return False
if converted_to_check is None: if converted_to_check is None:
converted_to_check = [] converted_to_check = []
effective_mode = return_mode if parent_key is None else "dict"
if formatter is None: if formatter is None:
formatter = lambda v: v # Multipart representation of value formatter = lambda v: v # Multipart representation of value
@ -145,6 +152,15 @@ def recraft_multipart_parser(data, parent_key=None, formatter: callable=None, co
else: else:
converted.append((current_key, formatter(value))) converted.append((current_key, formatter(value)))
if effective_mode == "formdata":
fd = aiohttp.FormData()
for k, v in dict(converted).items():
if isinstance(v, list):
for item in v:
fd.add_field(k, str(item))
else:
fd.add_field(k, str(v))
return fd
return dict(converted) return dict(converted)