diff --git a/krea_normal_formatter.py b/krea_normal_formatter.py index 16f006f..6fc4f80 100644 --- a/krea_normal_formatter.py +++ b/krea_normal_formatter.py @@ -86,6 +86,16 @@ def _appearance_with_phrase(appearance: str, with_indefinite_article: Callable[[ return "" first_clause = appearance.split(",", 1)[0].lower() if re.search(r"\b(?:body|build|figure|frame|physique|silhouette)\b", first_clause): + nested_shape = re.match( + r"^(.+\b(?:body|build|figure|frame|physique|silhouette))\s+with\s+(.+?)(?=,\s+[^,]*(?:skin|hair|eyes)\b|$)(.*)$", + appearance, + flags=re.IGNORECASE, + ) + if nested_shape: + shape = with_indefinite_article(nested_shape.group(1).strip()) + detail = nested_shape.group(2).strip() + rest = nested_shape.group(3).strip() + return f"with {shape} defined by {detail}{rest}" appearance = with_indefinite_article(appearance) return f"with {appearance}" diff --git a/tools/prompt_route_simulation.py b/tools/prompt_route_simulation.py index 88d3773..022fcaa 100644 --- a/tools/prompt_route_simulation.py +++ b/tools/prompt_route_simulation.py @@ -546,6 +546,12 @@ def _krea_grammar_issues(name: str, krea_prompt: str) -> list[str]: flags=re.IGNORECASE, ): issues.append(f"{name}.krea_prompt: subject_appearance_with_grammar") + if re.search( + r"\b(?:woman|man|person)\s+with\s+a\s+[^.;]{0,120}\bfigure\s+with\b", + krea_prompt, + flags=re.IGNORECASE, + ): + issues.append(f"{name}.krea_prompt: nested_figure_with_grammar") if re.search(r",\s+[A-Z][A-Za-z -]+ camera layout\b", krea_prompt): issues.append(f"{name}.krea_prompt: comma_joined_camera_layout") return issues diff --git a/tools/prompt_smoke.py b/tools/prompt_smoke.py index bd59743..12f5383 100644 --- a/tools/prompt_smoke.py +++ b/tools/prompt_smoke.py @@ -621,7 +621,15 @@ def smoke_builtin_single() -> None: ) _expect(item in str(krea_metadata.get("krea_prompt", "")), "Krea metadata-only built-in route lost explicit item") _expect(pose in str(krea_metadata.get("krea_prompt", "")), "Krea metadata-only built-in route lost explicit pose") - _expect(body_phrase in str(krea_metadata.get("krea_prompt", "")), "Krea metadata-only built-in route lost body phrase") + krea_metadata_prompt = str(krea_metadata.get("krea_prompt", "")) + if re.search(r"\b(?:body|build|figure|frame|physique|silhouette)\s+with\b", body_phrase, flags=re.IGNORECASE): + body_head, body_detail = body_phrase.split(" with ", 1) + _expect( + body_head in krea_metadata_prompt and body_detail in krea_metadata_prompt, + "Krea metadata-only built-in route lost normalized body phrase", + ) + else: + _expect(body_phrase in krea_metadata_prompt, "Krea metadata-only built-in route lost body phrase") _expect(skin in str(krea_metadata.get("krea_prompt", "")), "Krea metadata-only built-in route lost skin") _expect(hair in str(krea_metadata.get("krea_prompt", "")), "Krea metadata-only built-in route lost hair") _expect(eyes in str(krea_metadata.get("krea_prompt", "")), "Krea metadata-only built-in route lost eyes") @@ -1448,9 +1456,13 @@ def smoke_krea_normal_row_routes() -> None: "Krea single route kept old comma-with figure grammar", ) _expect( - "adult woman with a slim busty figure with" in figure_note_prompt, + "adult woman with a slim busty figure defined by soft" in figure_note_prompt, "Krea single route did not attach figure-note appearance to the subject", ) + _expect( + "adult woman with a slim busty figure with" not in figure_note_prompt, + "Krea single route kept nested figure-with wording", + ) _expect( "adult woman, showing slim busty figure with" not in figure_note_prompt, "Krea single route kept awkward showing figure-note grammar",