openai image edit endpoint

This commit is contained in:
Concedo
2026-08-07 14:40:54 +08:00
parent 927b997345
commit 0132829017
+83 -4
View File
@@ -2600,6 +2600,24 @@ def sd_oai_transform_params(genparams):
genparams["height"] = height
return genparams
def sd_oai_transform_edit_params(genparams):
genparams = sd_oai_transform_params(genparams)
images = genparams.pop("image", None)
if images is None:
images = genparams.pop("image[]", None)
if images and not isinstance(images, list):
images = [images]
images = [img for img in (images or []) if img]
if images:
genparams["init_images"] = []
extra_images = genparams.get("extra_images", images)
if not isinstance(extra_images, list):
extra_images = [extra_images]
genparams["extra_images"] = extra_images
if not genparams.get("denoising_strength"):
genparams["denoising_strength"] = 0.6
return genparams
def sd_comfyui_tranform_params(genparams):
promptobj = genparams.get('prompt', None)
if promptobj and isinstance(promptobj, dict):
@@ -5076,7 +5094,7 @@ class KcppProxyHandler(http.server.BaseHTTPRequestHandler):
is_chat_completions_path = (clean_path.endswith('/v1/chat/completions') or clean_path=='/chat/completions')
#any requests to the following endpoints is capable of waking the server
wake_requests = ["/api/extra/generate/stream","/api/extra/tokencount","/api/v1/generate","/sdapi/v1/interrogate","/v1/completions","/v1/chat/completions","/v1/responses","/completions","/chat/completions","/responses","/api/extra/transcribe","/v1/audio/transcriptions","/api/extra/tts","/v1/audio/speech","/api/extra/embeddings","/v1/embeddings","/api/embed","/api/extra/music/prepare","/api/extra/music/generate","/images/generations","/v1/images/generations","/sdapi/v1/txt2img","/sdapi/v1/img2img","/sdapi/v1/upscale"]
wake_requests = ["/api/extra/generate/stream","/api/extra/tokencount","/api/v1/generate","/sdapi/v1/interrogate","/v1/completions","/v1/chat/completions","/v1/responses","/completions","/chat/completions","/responses","/api/extra/transcribe","/v1/audio/transcriptions","/api/extra/tts","/v1/audio/speech","/api/extra/embeddings","/v1/embeddings","/api/embed","/api/extra/music/prepare","/api/extra/music/generate","/images/generations","/v1/images/generations","/images/edits","/v1/images/edits","/sdapi/v1/txt2img","/sdapi/v1/img2img","/sdapi/v1/upscale"]
is_wake_request = clean_path in wake_requests
autoswapEnabled = global_memory["autoswapmode"] is not None and global_memory["autoswapmode"]
@@ -5130,7 +5148,7 @@ class KcppProxyHandler(http.server.BaseHTTPRequestHandler):
ttsReqs = ["/api/extra/tts", "/v1/audio/speech"]
embedReqs = ["/api/extra/embeddings", "/v1/embeddings", "/api/embed"]
musicReqs = ["/api/extra/music/prepare","/api/extra/music/generate"]
imageReqs = ["/images/generations", "/v1/images/generations", "/sdapi/v1/txt2img", "/sdapi/v1/img2img", "/sdapi/v1/upscale"] # "/sdapi/v1/sd-models", "/sdapi/v1/options", "/sdapi/v1/samplers"
imageReqs = ["/images/generations", "/v1/images/generations", "/images/edits", "/v1/images/edits", "/sdapi/v1/txt2img", "/sdapi/v1/img2img", "/sdapi/v1/upscale"] # "/sdapi/v1/sd-models", "/sdapi/v1/options", "/sdapi/v1/samplers"
swapModeChanged = False
if any(clean_path.endswith(e) for e in textReqs) and (global_memory["swapReqType"] is None or global_memory["swapReqType"] != "text"):
@@ -5351,6 +5369,58 @@ class KcppServerRequestHandler(http.server.SimpleHTTPRequestHandler):
print(f"File Upload Process Error: {e}")
return result
def extract_multipart_formdata(self, body):
result = {"fields": {}, "files": {}}
try:
content_type = self.headers.get('content-type', '') or self.headers.get('Content-Type', '')
boundary_match = re.search(r'boundary=(?:"([^"]+)"|([^;]+))', content_type, flags=re.IGNORECASE)
if not boundary_match:
return result
boundary = (boundary_match.group(1) or boundary_match.group(2) or "").strip()
if not boundary:
return result
delimiter = ("--" + boundary).encode()
for part in body.split(delimiter):
part = part.strip(b"\r\n")
if not part or part == b"--":
continue
if part.endswith(b"--"):
part = part[:-2].rstrip(b"\r\n")
header_end = part.find(b"\r\n\r\n")
if header_end < 0:
continue
header_text = part[:header_end].decode("utf-8", errors="ignore")
content = part[header_end + 4:]
if content.endswith(b"\r\n"):
content = content[:-2]
name_match = re.search(r'Content-Disposition[^;]*;.*\bname=(?:"([^"]+)"|([^;\s]+))', header_text, flags=re.IGNORECASE)
if not name_match:
continue
name = name_match.group(1) or name_match.group(2)
filename_match = re.search(r'\bfilename=(?:"([^"]*)"|([^;\s]*))', header_text, flags=re.IGNORECASE)
if filename_match:
file_data_base64 = base64.b64encode(content).decode("utf-8", "ignore")
result["files"].setdefault(name, []).append(file_data_base64)
else:
result["fields"][name] = content.decode("utf-8", errors="ignore").strip()
return result
except Exception as e:
print(f"Multipart FormData Process Error: {e}")
return result
def extract_oai_image_edit_formdata(self, body):
formdata = self.extract_multipart_formdata(body)
genparams = dict(formdata.get("fields", {}))
files = formdata.get("files", {})
images = []
for key in ("image", "image[]"):
images.extend(files.get(key, []))
if images:
genparams["image"] = images
if files.get("mask"):
genparams["mask"] = files["mask"][0]
return genparams
def prepare_basic_responses_body(self,resp_id,genparams):
global friendlymodelname
global autoswapmode, textName, sttName, ttsName, embedName, musicName, imageName, mmprojName
@@ -7235,6 +7305,7 @@ Change Mode<br>
is_imggen = False
is_comfyui_imggen = False
is_oai_imggen = False
is_oai_imgedit = False
is_img_upscale = False
is_transcribe = False
is_tts = False
@@ -7334,12 +7405,15 @@ Change Mode<br>
api_format = 9
elif clean_path.endswith('/sdapi/v1/extra-single-image') or clean_path.endswith('/sdapi/v1/upscale'):
is_img_upscale = True
elif clean_path=="/prompt" or clean_path=="/images/generations" or clean_path.endswith('/v1/images/generations') or clean_path.endswith('/sdapi/v1/txt2img') or clean_path.endswith('/sdapi/v1/img2img'):
elif clean_path=="/prompt" or clean_path=="/images/generations" or clean_path.endswith('/v1/images/generations') or clean_path=="/images/edits" or clean_path.endswith('/v1/images/edits') or clean_path.endswith('/sdapi/v1/txt2img') or clean_path.endswith('/sdapi/v1/img2img'):
is_imggen = True
if clean_path=="/prompt":
is_comfyui_imggen = True
elif clean_path.endswith('/v1/images/generations') or clean_path=="/images/generations":
is_oai_imggen = True
elif clean_path.endswith('/v1/images/edits') or clean_path=="/images/edits":
is_oai_imggen = True
is_oai_imgedit = True
elif clean_path.endswith('/api/extra/transcribe') or clean_path.endswith('/v1/audio/transcriptions') or clean_path=="/audio/transcriptions":
is_transcribe = True
elif clean_path.endswith('/api/extra/tts') or clean_path.endswith('/v1/audio/speech') or clean_path=="/audio/speech" or clean_path.endswith('/tts_to_audio'):
@@ -7379,6 +7453,8 @@ Change Mode<br>
genparams["prompt"] = formdata["prompt"]
if "language" in formdata and formdata["language"]:
genparams["language"] = formdata["language"]
elif is_oai_imgedit:
genparams = self.extract_oai_image_edit_formdata(body)
if not genparams:
utfprint("Body Err: " + str(body))
@@ -7710,6 +7786,8 @@ Change Mode<br>
if is_comfyui_imggen:
lastgeneratedcomfyimg = b''
genparams = sd_comfyui_tranform_params(genparams)
elif is_oai_imgedit:
genparams = sd_oai_transform_edit_params(genparams)
elif is_oai_imggen:
genparams = sd_oai_transform_params(genparams)
if not genparams.get('lora'):
@@ -7742,7 +7820,8 @@ Change Mode<br>
lastgeneratedcomfyimg = b''
genresp = (json.dumps({"prompt_id": "12345678-0000-0000-0000-000000000001","number": 0,"node_errors":{}}).encode())
elif is_oai_imggen:
genresp = (json.dumps({"created":int(time.time()),"data":[{"b64_json":gendat}],"background":"opaque","output_format":"png","size":"1024x1024","quality":"medium"}).encode())
response_size = genparams.get("size", f"{genparams.get('width', 1024)}x{genparams.get('height', 1024)}")
genresp = (json.dumps({"created":int(time.time()),"data":[{"b64_json":gendat}],"background":"opaque","output_format":"png","size":response_size,"quality":"medium"}).encode())
else:
genresp = (json.dumps({"images":[gendat],"parameters":{},"info":geninfo,"animated":genanim,"extra_data":gendatextra, "final_frame":genfinalframe}).encode())
self.send_response(200)