Files
EricRollei-Local_LLM_Prompt…/search_prompt_node.py
T
Eric HissandClaude Fable 5.1 4459af9ea6 V2.0.0: wildcard engine, platform profiles, MiniMax H3 default, API models
Every node id, input name and existing widget position is unchanged, so saved
workflows load; new inputs are appended only.

Wildcards
- core/wildcards: Dynamic Prompts / Impact Pack grammar, tag queries, variables,
  conditionals, concept / verbatim / loose bindings, reproducible seeds.
  Wired into every enhancer node; reads ComfyUI/models/wildcards.
- New nodes: Wildcard Processor, Semantic Variation (direction axes),
  Concept Survival Check. Starter lists in wildcards/.
- Random choices use a private generator; global random state is untouched.

Platform profiles
- core/profiles + profiles/*.yaml: one file per model (format, length,
  negative prompt or none, quality tokens, text and dialogue syntax, vendor
  rewriter prompt fetched once and cached). profiles_user/ overrides by id.
- New node: Platform Prompt Rewriter (prose, tags, edit instructions, JSON,
  MiniMax H3 fields, Hailuo brackets), with validators for each format.
- Text-to-Image and Image-to-Image read profiles through platforms.py;
  corrected facts for FLUX, Kontext, Chroma, Wan, AuraFlow, Kolors, NoobAI,
  Qwen-Image, HunyuanImage. Negative prompt is empty for models without one.
- Hosted / API models: Seedream 5, Nano Banana, GPT Image, Midjourney V8,
  FLUX.2 pro, Ideogram 3, Recraft V4, Seedance 2.0 / 2.5, Kling 3.0, Veo 3.1,
  Gemini Omni Flash, Runway, Wan 3.0, Luma, hosted H3, Grok, Vidu, PixVerse.
  Reports say how to switch the vendor's own prompt rewriting off.
- Illustrious: spaced tags, quality tags last, 25-80 words (OnomaAI paper).

Video nodes
- target_model and duration_seconds appended to the three video expanders.
  Default target is MiniMax H3; wan22_video is the original path, unchanged.
  A workflow saved earlier loads with the default until the input is set.

Also included: the Alice, Ideogram Magic Prompt and prompt-database nodes that
were already registered in __init__.py but had not been committed.

Fixes: v1 {animal} / {label:a|b} wildcards were inert in the video nodes;
Image-to-Video save_to_file raised NameError; T2I padded short-prompt models.

.gitattributes added (text=auto) so CRLF working files stop showing as
whole-file changes. prompt_database.db and the cached Ideogram prompt are
git-ignored.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019VpCi6nw5UECMdZ7ayYJvN
2026-09-21 00:28:58 -07:00

155 lines
5.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Search Prompt Database Node
ComfyUI node for keyword / filtered search through the prompt database.
Author: Eric Hiss (GitHub: EricRollei)
"""
import logging
from .prompt_database import PromptDatabase, PLATFORMS, CATEGORIES
logger = logging.getLogger(__name__)
class SearchPromptDatabase:
"""
Search the local prompt database by keyword, category, platform,
rating, tags, or favorites. Returns the top matching prompt as a
string output that can be fed directly into a sampler or other node.
"""
def __init__(self):
self.db = PromptDatabase()
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"search_query": ("STRING", {
"default": "",
"tooltip": (
"Keyword search. Supports AND, OR, NOT, and "
"\"quoted phrases\". Leave blank to browse all."
),
}),
"category": (CATEGORIES, {
"default": "any",
"tooltip": "Filter by category (or 'any' for all).",
}),
"platform": (PLATFORMS, {
"default": "any",
"tooltip": "Filter by target platform (or 'any' for all).",
}),
"min_rating": ("INT", {
"default": 0,
"min": 0,
"max": 5,
"step": 1,
"tooltip": "Only return prompts rated at least this value.",
}),
"favorites_only": ("BOOLEAN", {
"default": False,
"tooltip": "Show only prompts marked as favorites.",
}),
"tags_filter": ("STRING", {
"default": "",
"tooltip": "Comma-separated tags – all must be present in the result.",
}),
"order_by": (["newest", "oldest", "rating", "random"], {
"default": "newest",
"tooltip": "Sort order for results.",
}),
"result_index": ("INT", {
"default": 1,
"min": 1,
"max": 9999,
"step": 1,
"tooltip": "Which result to output (1 = top match).",
}),
"page_size": ("INT", {
"default": 20,
"min": 1,
"max": 100,
"step": 1,
"tooltip": "How many results to fetch per page.",
}),
},
}
RETURN_TYPES = ("STRING", "STRING", "INT", "STRING", "STRING")
RETURN_NAMES = ("prompt_text", "negative_prompt", "total_matches", "result_info", "all_results_summary")
FUNCTION = "search_prompts"
CATEGORY = "Eric Prompt Enhancers/Database"
OUTPUT_NODE = True
def search_prompts(
self,
search_query: str,
category: str,
platform: str,
min_rating: int,
favorites_only: bool,
tags_filter: str,
order_by: str,
result_index: int,
page_size: int,
):
try:
# Compute offset so result_index maps correctly
offset = 0
limit = max(result_index, page_size)
results, total = self.db.search(
query=search_query,
category=category,
platform=platform,
min_rating=min_rating,
favorites_only=favorites_only,
tags_filter=tags_filter,
limit=limit,
offset=offset,
order_by=order_by,
)
if not results:
return ("", "", 0, "No matching prompts found.", "")
# Pick the requested result
idx = min(result_index - 1, len(results) - 1)
chosen = results[idx]
# Build info line
info = (
f"Result {idx + 1}/{total} | "
f"ID #{chosen['id']} | "
f"Category: {chosen['category']} | "
f"Platform: {chosen['platform']} | "
f"Rating: {'★' * chosen['rating']}{'☆' * (5 - chosen['rating'])} | "
f"Tags: {chosen['tags'] or '(none)'} | "
f"Saved: {chosen['created_at']}"
)
# Summary of all fetched results
summary_lines = []
for i, r in enumerate(results[:page_size], start=1):
short = r["prompt_text"][:80].replace("\n", " ")
if len(r["prompt_text"]) > 80:
short += "..."
summary_lines.append(
f"[{i}] #{r['id']} ({'★' * r['rating']}) "
f"[{r['category']}/{r['platform']}] {short}"
)
summary = "\n".join(summary_lines)
return (
chosen["prompt_text"],
chosen.get("negative_prompt", ""),
total,
info,
summary,
)
except Exception as e:
logger.error(f"Search error: {e}")
return ("", "", 0, f"Error: {e}", "")