From 2763e9f9fa07741cdd6f8613e62681d10a726384 Mon Sep 17 00:00:00 2001 From: ghosttigerllc-bit Date: Fri, 16 Oct 2026 03:50:26 -0700 Subject: [PATCH] Read A1111/Forge generation parameters: fix the SELECT and parse multi-line prompts (#165) * get_image_prompt_info: include gi.parameters in SELECT and return value The function selects workflow_data, prompt_metadata and generation_time from generated_images but omits the parameters column, so the A1111/Forge generation-parameters string (steps, sampler, CFG, seed, LoRA list) never reaches the API even when present in the database. Add it to the SELECT and include it in the returned dict. * parse A1111/Forge parameters: multi-line prompts, structured settings _parse_comfyui_prompt took lines[0] of the parameters chunk as the whole positive prompt, truncating every multi-line prompt to its first line, and matched 'negative prompt:' only as a full line start. Split on the trailing 'Steps: ...' settings line instead, keep the full multi-line positive prompt, and extract Steps / Sampler / Schedule type / CFG scale / Seed as structured fields alongside the raw string. Together with the get_image_prompt_info SELECT fix (previous commit), the full A1111/Forge generation recipe now flows from the database to the API. * style: apply black to the A1111 parameters parser CI's black --check flagged py/api/__init__.py (slice spacing and two long lines in the settings extraction). Formatting only; the AST is unchanged. --------- Co-authored-by: kai --- database/operations.py | 6 ++++-- py/api/__init__.py | 41 +++++++++++++++++++++++++++++++++-------- 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/database/operations.py b/database/operations.py index a1b8bcb1..af53cb30 100644 --- a/database/operations.py +++ b/database/operations.py @@ -1971,12 +1971,13 @@ class PromptDatabase: """Look up prompt + image metadata for a given image path. Returns a dict with prompt_id, text, category, tags, rating, notes, - workflow_data, prompt_metadata, generation_time — or None. + workflow_data, prompt_metadata, generation_time, parameters — or None. """ with self.model.get_connection() as conn: cursor = conn.execute( """SELECT gi.prompt_id, p.text, p.category, p.rating, p.notes, - gi.workflow_data, gi.prompt_metadata, gi.generation_time + gi.workflow_data, gi.prompt_metadata, gi.generation_time, + gi.parameters FROM generated_images gi JOIN prompts p ON gi.prompt_id = p.id WHERE gi.image_path = ? OR gi.file_path = ? @@ -2009,6 +2010,7 @@ class PromptDatabase: "workflow_data": workflow, "prompt_metadata": prompt_meta, "generation_time": row[7], + "parameters": row[8] if len(row) > 8 else None, } def get_prompt_id_for_image(self, image_path: str) -> Optional[int]: diff --git a/py/api/__init__.py b/py/api/__init__.py index 747fa30e..cf267936 100644 --- a/py/api/__init__.py +++ b/py/api/__init__.py @@ -703,18 +703,43 @@ class PromptManagerAPI( "negative_prompt": None, } - # Check for A1111 style parameters first (like parse-metadata.py) + # Check for A1111/Forge style parameters first. + # The chunk is one block: a possibly multi-line positive prompt, then a + # "Negative prompt:" section, then a trailing "Steps: ..." settings line. + # Taking lines[0] as the positive prompt truncates any multi-line prompt + # to one line, so split on the settings line instead. if "parameters" in metadata: + import re as _re + params = metadata["parameters"] - lines = params.splitlines() - if lines: - result["positive_prompt"] = lines[0].strip() - for line in lines: - if line.lower().startswith("negative prompt:"): - result["negative_prompt"] = line.split(":", 1)[1].strip() - break + body = params + settings = "" + _sm = _re.search(r"\nSteps:\s", body) + if _sm: + settings = body[_sm.start() + 1 :].strip() + body = body[: _sm.start()] + negative = None + _nm = _re.search(r"(?im)^\s*negative prompt:\s*", body) + if _nm: + negative = body[_nm.end() :].strip() + body = body[: _nm.start()] + elif body.lower().lstrip().startswith("negative prompt:"): + negative = body.lower().lstrip()[len("negative prompt:") :].strip() + body = "" + result["positive_prompt"] = body.strip() or None + if negative: + result["negative_prompt"] = negative # Store raw parameters too result["parameters"]["parameters"] = params + if settings: + for _f in ("Steps", "Sampler", "Schedule type", "CFG scale", "Seed"): + _fm = _re.search( + r"(?i)(?:^|,\s*)" + _re.escape(_f) + r":\s*([^,]+)", settings + ) + if _fm: + result["parameters"][_f.lower().replace(" ", "_")] = _fm.group( + 1 + ).strip() # If no A1111 format found, proceed with ComfyUI parsing if result["positive_prompt"] is None: