diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 816df2d..0477999 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.3.1" + ".": "0.3.2" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f2059e..b330b79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,10 +6,10 @@ The format is based on Keep a Changelog, and this project follows Semantic Versi ## [Unreleased] +## [0.3.2] - 2026-03-07 + ### Added -- `PromptStyler: Advanced Prompt -> Conditioning` for search-based style resolution, color-grade overlays, style strength controls, and JSON debug metadata. -- Shared `style_library.py` used by the runtime node and maintainer tooling. - CI checks for version sync, style validation, style audit, and runtime regression tests. - `tools/sync_legacy_styles.py` and `tools/version_sync.py` for compatibility snapshot and release metadata maintenance. - New medium-first style packs: `styles/packs/32_print_reprographic.json`, `styles/packs/42_decorative_arts_material_processes.json`, and `styles/packs/43_digital_craft.json`. @@ -22,7 +22,7 @@ The format is based on Keep a Changelog, and this project follows Semantic Versi - `PromptStyler: Prompt -> Conditioning (Style Picker)` now uses the narrow legacy-compatible prompt-to-conditioning contract again for stability. - Broken or non-finite conditioning now raises an explicit node error instead of silently returning a malformed output. - `styles/styles_v1.json` is now treated as a generated compatibility snapshot of merged packs. -- README and maintainer documentation now describe the stable/advanced node split. +- README and maintainer documentation now describe the restored stable-node runtime surface and release workflow. ## [0.3.1] - 2026-01-31 @@ -71,4 +71,3 @@ The format is based on Keep a Changelog, and this project follows Semantic Versi ### Changed - README updates for new node input. - diff --git a/VERSION b/VERSION index 9e11b32..d15723f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.3.1 +0.3.2 diff --git a/output/pdf/comfyui_promptstyler_summary.pdf b/output/pdf/comfyui_promptstyler_summary.pdf new file mode 100644 index 0000000..21ec824 Binary files /dev/null and b/output/pdf/comfyui_promptstyler_summary.pdf differ diff --git a/tests/test_promptstyler.py b/tests/test_promptstyler.py index 51b8acb..124c4e3 100644 --- a/tests/test_promptstyler.py +++ b/tests/test_promptstyler.py @@ -6,39 +6,10 @@ import nodes import style_library -class _FakeBool: - def __init__(self, value: bool): - self._value = value - - def item(self) -> bool: - return self._value - - -class _FakeFiniteResult: - def __init__(self, value: bool): - self._value = value - - def all(self) -> _FakeBool: - return _FakeBool(self._value) - - -class FakeTensor: - def __init__(self, finite: bool = True, size: int = 3): - self._finite = finite - self._size = size - - def numel(self) -> int: - return self._size - - def isfinite(self) -> _FakeFiniteResult: - return _FakeFiniteResult(self._finite) - - class FakeTextEncoder: - def __init__(self, conditioning=None): + def __init__(self): self.last_text = None self.last_tokens = None - self._conditioning = conditioning def tokenize(self, text): self.last_text = text @@ -46,9 +17,7 @@ class FakeTextEncoder: return self.last_tokens def encode_from_tokens_scheduled(self, tokens): - if self._conditioning is not None: - return self._conditioning - return [[FakeTensor(), {"source": tokens["text"]}]] + return [[tokens, {"source": tokens["text"]}]] class PromptStylerTests(unittest.TestCase): @@ -56,7 +25,6 @@ class PromptStylerTests(unittest.TestCase): def setUpClass(cls): cls.library = style_library.get_cached_style_library(load_policy=style_library.LOAD_POLICY_STRICT) cls.stable_node = nodes.PromptStylerConditioning() - cls.advanced_node = nodes.PromptStylerAdvancedConditioning() cls.primary_choice = next(choice for choice in cls.library.choices if choice.endswith("| cinematic_anamorphic")) cls.secondary_choice = next(choice for choice in cls.library.choices if choice.endswith("| cin_film_noir")) @@ -119,6 +87,26 @@ class PromptStylerTests(unittest.TestCase): self.assertEqual(expected, encoder.last_text) self.assertEqual(1, len(conditioning)) + def test_flux_variant_uses_variant_text_when_present(self): + encoder = FakeTextEncoder() + conditioning, styled_prompt = self.stable_node.encode( + prompt="robot detective", + apply_style=True, + style=self.primary_choice, + template_variant="flux_2_klein", + style_id_override="", + text_encoder=encoder, + ) + expected_style = self.library.by_id["cinematic_anamorphic"] + expected_prompt, _details = style_library.compose_prompt_legacy( + "robot detective", + expected_style, + template_variant="flux_2_klein", + ) + self.assertEqual(expected_prompt, styled_prompt) + self.assertEqual(expected_prompt, encoder.last_text) + self.assertEqual(1, len(conditioning)) + def test_missing_variant_falls_back_to_default(self): style = self.library.by_id["cinematic_anamorphic"] default_prompt, _default_details = style_library.compose_prompt_legacy( @@ -134,49 +122,29 @@ class PromptStylerTests(unittest.TestCase): self.assertEqual(default_prompt, fallback_prompt) self.assertEqual(style_library.DEFAULT_VARIANT, details["variant"]) - def test_advanced_node_matches_stable_node_for_simple_dropdown_flow(self): - stable_encoder = FakeTextEncoder() - advanced_encoder = FakeTextEncoder() - stable_conditioning, stable_prompt = self.stable_node.encode( - prompt="robot detective, rainy alley", - apply_style=True, - style=self.primary_choice, - template_variant="default", - style_id_override="", - text_encoder=stable_encoder, - ) - advanced_conditioning, advanced_prompt, resolved_style_id, resolved_style_meta = self.advanced_node.encode( - prompt="robot detective, rainy alley", - apply_style=True, - style=self.primary_choice, - template_variant="default", - style_id_override="", - text_encoder=advanced_encoder, - selection_mode="dropdown", - style_search="", - category_hint="", - tag_hint="", - style_strength="strong", - dedupe_mode="smart", - overlay_style_id="", - on_missing_style="error", - ) - self.assertEqual(stable_prompt, advanced_prompt) - self.assertEqual("cinematic_anamorphic", resolved_style_id) - self.assertIn('"status": "ok"', resolved_style_meta) - self.assertEqual(1, len(stable_conditioning)) - self.assertEqual(1, len(advanced_conditioning)) + def test_missing_text_encoder_raises_explicit_error(self): + with self.assertRaisesRegex(RuntimeError, "text_encoder input is invalid"): + self.stable_node.encode( + prompt="robot detective", + apply_style=True, + style=self.primary_choice, + template_variant="default", + style_id_override="", + text_encoder=None, + ) - def test_invalid_conditioning_raises_explicit_error(self): - encoder = FakeTextEncoder(conditioning=[[FakeTensor(finite=False), {}]]) - with self.assertRaisesRegex(RuntimeError, "non-finite conditioning"): - nodes.encode_conditioning(encoder, "robot detective") - - def test_malformed_conditioning_raises_explicit_error(self): - encoder = FakeTextEncoder(conditioning=["bad"]) - with self.assertRaisesRegex(RuntimeError, "invalid conditioning item"): - nodes.encode_conditioning(encoder, "robot detective") + def test_unknown_style_id_override_raises_error(self): + encoder = FakeTextEncoder() + with self.assertRaisesRegex(ValueError, "Unknown style_id_override"): + self.stable_node.encode( + prompt="robot detective", + apply_style=True, + style=self.primary_choice, + template_variant="default", + style_id_override="does_not_exist", + text_encoder=encoder, + ) if __name__ == "__main__": - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tmp/pdfs/promptstyler_app_summary.html b/tmp/pdfs/promptstyler_app_summary.html new file mode 100644 index 0000000..6262aac --- /dev/null +++ b/tmp/pdfs/promptstyler_app_summary.html @@ -0,0 +1,88 @@ + + + + + ComfyUI PromptStyler - App Summary + + + +
+
+
Repo Summary
+

ComfyUI PromptStyler

+

Version 3.1.1. Custom ComfyUI node pack that applies curated style templates to a positive prompt and returns conditioning for downstream generation. It is structured around JSON style packs plus lightweight Python runtime code that composes the final prompt text at node execution time.

+
+
+
+ Who It’s For +

Primary persona: ComfyUI users building image-generation workflows who want reusable, curated prompt styling without hand-writing long style suffixes.

+
+
+ Install Shape +

Drop the repo into ComfyUI/custom_nodes/ComfyUI_PromptStyler and reload custom nodes.

+
+
+
+
+
+

What It Does

+ +
+
+

How To Run

+ +
+
+

How It Works

+ +

If you need external services, hosted APIs, or a packaged installer: Not found in repo.

+
+
+ + diff --git a/tmp/pdfs/promptstyler_app_summary.png b/tmp/pdfs/promptstyler_app_summary.png new file mode 100644 index 0000000..cdd1ae3 Binary files /dev/null and b/tmp/pdfs/promptstyler_app_summary.png differ