smart_image_gen v0.7.1: rename edit_image arg + parse file id from URL

Two bugs in one screenshot:

1. LLM called edit_image(prompt=..., ...) but the signature was
   edit_image(edit_instruction=..., ...) — mismatch, missing-arg
   crash. Renamed the first param to `prompt` so both tools have a
   matching, predictable name. System prompt updated with an explicit
   'do not invent edit_instruction' line for stubborn models.

2. After fix #1, edit_image still couldn't find the prior generated
   image because Open WebUI assistant-message file attachments only
   carry {type, url} (no id, no path). _read_file_dict now also
   greps the file id out of /api/v1/files/<uuid>/content URLs and
   feeds it to Files.get_file_by_id. Verified pattern matches
   absolute URLs (https://llm-1.srvno.de/api/v1/files/.../content).

System prompt also now says 'including images you previously
generated in this chat' to nudge the LLM to pick up assistant
outputs as edit candidates.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
57_WolveandClaude Opus 4.7 committed 2026-04-19 14:58:40 -05:00
1 parent 780ce42711
commit 06433d3815
3 files changed
+39 -20

No files matched your search

@@ -1,7 +1,7 @@
"""
title: Smart Image Generator & Editor (ComfyUI)
author: ai-stack
version: 0.7.0
version: 0.7.1
description: Generate or edit images via ComfyUI with automatic SDXL
checkpoint routing. Two methods — generate_image (txt2img) and
edit_image (img2img on the user's most recently attached image). The
@@ -345,12 +345,19 @@ def _file_dict_is_image(f: dict) -> bool:
return "image" in ftype or fname.endswith((".png", ".jpg", ".jpeg", ".webp"))
_FILE_URL_ID_RE = re.compile(r"/(?:api/v1/)?files/([0-9a-fA-F-]{8,})(?:/content)?")
def _read_file_dict(f: dict) -> Optional[bytes]:
"""
Try to read raw bytes for one file dict. Path keys first (covers local
uploads), then Open WebUI's Files model lookup by id (covers assistant-
emitted images that only have an id + relative URL). Returns None if
no method worked.
Try to read raw bytes for one file dict. Tries in order:
1. Local filesystem path keys (covers user uploads with `path`).
2. Open WebUI's Files.get_file_by_id with f["id"] (covers files
the user uploaded via the file API).
3. Same lookup with the id parsed out of f["url"] (covers
assistant-emitted files where the message attachment is just
{"type":"image","url":"/api/v1/files/<uuid>/content"} —
no id field, no path field, but the URL has the id).
"""
for path_key in ("path", "filepath", "file_path"):
path = f.get(path_key)
@@ -361,12 +368,21 @@ def _read_file_dict(f: dict) -> Optional[bytes]:
except OSError:
pass
fid = f.get("id")
if _OPENWEBUI_RUNTIME and fid:
try:
file_model = Files.get_file_by_id(fid)
if file_model is not None:
# FileModel may expose path directly or under .meta
candidate_ids = []
if f.get("id"):
candidate_ids.append(f["id"])
url = f.get("url")
if url:
m = _FILE_URL_ID_RE.search(url)
if m:
candidate_ids.append(m.group(1))
if _OPENWEBUI_RUNTIME:
for fid in candidate_ids:
try:
file_model = Files.get_file_by_id(fid)
if file_model is None:
continue
path = getattr(file_model, "path", None)
if not path:
meta = getattr(file_model, "meta", None) or {}
@@ -380,8 +396,8 @@ def _read_file_dict(f: dict) -> Optional[bytes]:
return fh.read()
except OSError:
pass
except Exception:
pass
except Exception:
pass
return None
@@ -713,7 +729,7 @@ class Tools:
async def edit_image(
self,
edit_instruction: str,
prompt: str,
style: Optional[StyleName] = None,
mask_text: Optional[str] = None,
denoise: Optional[float] = None,
@@ -757,7 +773,7 @@ class Tools:
Pick `style` for the DESIRED OUTPUT, not the input image.
:param edit_instruction: What the changed area should look like.
:param prompt: What the changed area should look like.
Tool auto-prepends quality tags — don't include those.
:param style: One of the StyleName values. Omit to auto-detect.
:param mask_text: Noun phrase describing the region to edit. Set
@@ -768,7 +784,7 @@ class Tools:
:param seed: 0 to randomize, otherwise specific.
:return: Markdown image of the result, or an error if no image is attached.
"""
chosen = style or _route_style(edit_instruction)
chosen = style or _route_style(prompt)
settings = STYLES.get(chosen)
if not settings:
return f"Unknown style '{chosen}'. Available: {', '.join(STYLES.keys())}"
@@ -809,7 +825,7 @@ class Tools:
+ (f", mask='{mask_text}'" if mask_text else "")
)
positive = f"{settings['prefix']}{edit_instruction}"
positive = f"{settings['prefix']}{prompt}"
negative = settings["negative"]
if negative_prompt:
negative = f"{negative}, {negative_prompt}"