refactor: remove dead and test-only code and the write-only prompt registry (#620)

Removes helpers nothing calls, test-only wrappers and their tests, the unused
config writer, three unused images, and the prompt registry the nodes wrote to
but nothing read, which grew with every run.
This commit is contained in:
Vito Sansevero
2026-11-02 10:22:03 -08:00
parent 56d1072e58
commit f6432fec74
40 changed files with 50 additions and 969 deletions
-220
View File
@@ -1,220 +0,0 @@
"""ComfyUI integration utilities for PromptManager.
This module tracks the prompts PromptManager nodes encode during execution so
other components (e.g. image/prompt linking) can look them up.
ComfyUI already writes every node's own inputs into saved image metadata, so
SaveImage is deliberately NOT patched. An earlier patch wrote the last-run prompt
into every PromptManager node, saving negative prompts as copies of the
positive (#75).
Key features:
- Thread-safe prompt registration and retrieval
- Cross-thread prompt context sharing
- Standard metadata format compatibility
- Automatic cleanup of old prompt registrations
The integration works by:
1. PromptManager nodes register their prompts during execution
2. Callers retrieve the current prompt text by node id or recency
Typical usage:
from utils.comfyui_integration import get_comfyui_integration
integration = get_comfyui_integration()
integration.register_prompt(node_id, prompt_text, metadata)
text = integration.get_current_prompt_text(node_id)
"""
import threading
import time
from typing import Dict, Any, Optional
try:
from .logging_config import get_logger
except ImportError:
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from utils.logging_config import get_logger
class ComfyUIMetadataIntegration:
"""Integrates PromptManager with ComfyUI's standard metadata system.
This singleton class manages the integration between PromptManager custom nodes
and ComfyUI's standard metadata system. It ensures that prompts generated by
PromptManager appear in the standard ComfyUI metadata format for compatibility
with third-party tools and parsers.
The integration uses thread-local storage combined with global tracking to
handle prompt context across different execution threads, which is necessary
because ComfyUI executions may span multiple threads.
Key responsibilities:
- Register prompts from PromptManager nodes during execution
- Manage prompt lifecycle and cleanup
"""
_instance = None
_lock = threading.Lock()
def __new__(cls):
"""Ensure singleton pattern with thread safety.
Returns:
The single instance of ComfyUIMetadataIntegration
"""
if cls._instance is None:
with cls._lock:
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self):
"""Initialize the ComfyUI integration system.
Sets up prompt tracking data structures. Uses _initialized flag to
prevent duplicate initialization.
"""
if hasattr(self, "_initialized"):
return
self.logger = get_logger("prompt_manager.comfyui_integration")
self._current_prompts = {}
self._thread_local = threading.local()
self._initialized = True
def register_prompt(self, node_id: str, prompt_text: str, metadata: Dict[str, Any]):
"""
Register a prompt from PromptManager for inclusion in ComfyUI metadata.
This method is called by PromptManager nodes during execution to register
their prompt text for later inclusion in image metadata. The prompt is stored
in both thread-local and global storage to handle cross-thread access scenarios.
Args:
node_id: Unique identifier for this prompt (typically the node ID)
prompt_text: The actual prompt text that was encoded by PromptManager
metadata: Additional metadata from PromptManager (category, tags, etc.)
"""
thread_id = threading.current_thread().ident
# Store in thread-local storage
if not hasattr(self._thread_local, "prompts"):
self._thread_local.prompts = {}
self._thread_local.prompts[node_id] = {
"text": prompt_text,
"metadata": metadata,
"timestamp": time.time(),
"thread_id": thread_id,
}
# Also store globally for cross-thread access
with self._lock:
self._current_prompts[f"{thread_id}_{node_id}"] = {
"text": prompt_text,
"metadata": metadata,
"timestamp": time.time(),
"thread_id": thread_id,
}
self.logger.debug(
f"Registered prompt for node {node_id}: {prompt_text[:50]}..."
)
def get_current_prompt_text(self, node_id: str = None) -> Optional[str]:
"""
Get the current prompt text for metadata inclusion.
Retrieves the most appropriate prompt text for inclusion in image metadata.
Uses a fallback strategy: thread-local storage first, then global storage,
with recency checks to avoid stale prompts.
Args:
node_id: Optional specific node ID to retrieve prompt for
Returns:
The prompt text string if available, None if no suitable prompt found
"""
# First try thread-local storage
if hasattr(self._thread_local, "prompts"):
if node_id and node_id in self._thread_local.prompts:
return self._thread_local.prompts[node_id]["text"]
elif self._thread_local.prompts:
# Return the most recent prompt from this thread
latest = max(
self._thread_local.prompts.values(), key=lambda x: x["timestamp"]
)
return latest["text"]
# Fallback to global storage
thread_id = threading.current_thread().ident
with self._lock:
# Look for prompts from current thread
thread_prompts = {
k: v
for k, v in self._current_prompts.items()
if v["thread_id"] == thread_id
}
if thread_prompts:
latest = max(thread_prompts.values(), key=lambda x: x["timestamp"])
return latest["text"]
# Last resort: return the most recent prompt from any thread
if self._current_prompts:
latest = max(
self._current_prompts.values(), key=lambda x: x["timestamp"]
)
# Only return if it's recent (within last 5 minutes)
if time.time() - latest["timestamp"] < 300:
return latest["text"]
return None
def cleanup_old_prompts(self, max_age_seconds: int = 600):
"""
Clean up old prompt registrations.
Removes prompt registrations that are older than the specified age to
prevent memory leaks and ensure that only recent, relevant prompts are
used for metadata inclusion.
Args:
max_age_seconds: Maximum age in seconds before a prompt registration
is considered stale and removed (default: 600
seconds/10 minutes)
"""
current_time = time.time()
with self._lock:
old_keys = [
key
for key, prompt_data in self._current_prompts.items()
if current_time - prompt_data["timestamp"] > max_age_seconds
]
for key in old_keys:
del self._current_prompts[key]
if old_keys:
self.logger.debug(f"Cleaned up {len(old_keys)} old prompt registrations")
# Global instance
_integration_instance = None
def get_comfyui_integration() -> ComfyUIMetadataIntegration:
"""Get the global ComfyUI integration instance.
Returns:
The singleton ComfyUIMetadataIntegration instance, creating it if necessary
"""
global _integration_instance
if _integration_instance is None:
_integration_instance = ComfyUIMetadataIntegration()
return _integration_instance
+1 -33
View File
@@ -8,19 +8,15 @@ regardless of minor formatting differences.
Key features:
- SHA256-based prompt text hashing with normalization
- Content hashing for complex prompt metadata structures
- Duplicate detection utilities
- Consistent normalization (lowercase, trimmed whitespace)
Typical usage:
from utils.hashing import generate_prompt_hash, is_duplicate_prompt
from utils.hashing import generate_prompt_hash
hash1 = generate_prompt_hash("Beautiful landscape")
hash2 = generate_prompt_hash(" beautiful landscape ")
# hash1 == hash2 (normalization makes them identical)
if is_duplicate_prompt(text1, text2):
print("Duplicate prompts detected")
The hashing is designed for:
- Database deduplication (preventing duplicate prompt storage)
- Quick similarity checking
@@ -124,31 +120,3 @@ def stored_prompt_text_sql(expression: str) -> str:
character of Y, multi-byte ones included, so it strips what Python does.
"""
return f"trim({expression}, char({_STRIP_CODES}))"
def is_duplicate_prompt(text1: str, text2: str, threshold: float = 0.95) -> bool:
"""
Check if two prompts are likely duplicates using hash comparison.
Compares the normalized hashes of two prompt texts to determine if
they are functionally identical. This is an exact match after normalization,
not a similarity measure.
Args:
text1: First prompt text to compare
text2: Second prompt text to compare
threshold: Similarity threshold (not used - kept for API compatibility)
Returns:
True if the prompts have identical normalized hashes (i.e., are duplicates),
False otherwise
Note:
The threshold parameter is not used in the current implementation as
this performs exact hash matching rather than similarity scoring.
It's maintained for potential future fuzzy matching capabilities.
"""
hash1 = generate_prompt_hash(text1)
hash2 = generate_prompt_hash(text2)
return hash1 == hash2
-27
View File
@@ -295,30 +295,3 @@ def sanitize_input(text: str) -> str:
result_lines.append(line)
return "\n".join(result_lines).strip()
def parse_tags_string(tags_string: str) -> List[str]:
"""
Parse a comma-separated tags string into a clean list.
Converts comma-separated tag strings into a clean, deduplicated list
of tags. Each tag is sanitized and trimmed.
Args:
tags_string: Comma-separated tags string (e.g., "tag1, tag2, tag3")
Returns:
List of unique, cleaned tag strings. Empty input returns empty list.
Limited to maximum 20 tags to prevent abuse.
"""
if not tags_string or not isinstance(tags_string, str):
return []
# Split by comma and clean each tag
tags = []
for tag in tags_string.split(","):
clean_tag = sanitize_input(tag).strip()
if clean_tag and clean_tag not in tags: # Avoid duplicates
tags.append(clean_tag)
return tags[:20] # Limit to 20 tags