Files
Hopping-Mad-Games-ComfyUI_L…/utils/hash_utils.py
TashaSkyUp 40831eaa28 Add agent node docstrings, improve error handling, and update config
- Add detailed docstrings to AgentNode, BasicRecursionFilterNode, and
DocumentChunkRecursionFilterNode for clarity and documentation. -
Improve input validation and error handling in agent nodes. - Update
.gitignore to cover more environment, config, and IDE files. - Refactor
LiteLLMCustomEndpointProvider for custom API endpoints. - Add
conditional imports and dummy functions for LightRAG when unavailable. -
Update config.yaml to use environment variables for API keys and clarify
usage. - Refine requirements.txt to avoid heavy LightRAG dependencies. -
Add tests/__init__.py with test suite metadata; remove redundant test
files. - Fix CustomDict import and model config handling in
litellmnodes.py. - Improve chunk processing and recursion filter logic
in agent nodes. - Update utils/__init__.py to include env_config.
2025-06-24 17:47:36 -06:00

85 lines
3.4 KiB
Python
Executable File

import hashlib
import json
def get_input_hash(**kwargs):
"""
Creates a stable hash from input parameters, handling various Python types.
Handles:
- Callable objects (using their class names)
- Message dictionaries
- Image data (using shape and mean)
- Basic types (str, int, float, bool, None)
- Lists (converting elements to strings)
- Other types (using string representation)
Args:
**kwargs: Keyword arguments to hash
Returns:
str: MD5 hash of the input parameters
"""
# Create a copy of kwargs without the use_last_response flag
hash_kwargs = kwargs.copy()
hash_kwargs.pop('use_last_response', None)
# Create a list to store hashable components
hash_components = []
# Handle each input type appropriately
for key, value in sorted(hash_kwargs.items()):
if key in ('recursion_filter', 'memory_provider'):
if value is not None:
try:
# Get the class name for callable objects
if hasattr(value, '__self__'):
# If it's a bound method, get the class name of the instance
class_name = value.__self__.__class__.__name__
else:
# If it's a regular callable, get its class name
class_name = value.__class__.__name__
hash_components.append(f"{key}_class:{class_name}")
except:
# If we can't get the class name, use a default identifier
hash_components.append(f"{key}_class:unknown")
continue
elif key == 'messages':
# Convert messages to a stable string representation using json
try:
# Convert each message to a tuple of (role, content) and then to JSON
messages_data = [(m.get('role', ''), m.get('content', '')) for m in value]
messages_str = json.dumps(messages_data, sort_keys=True)
hash_components.append(f"messages:{messages_str}")
except:
# If messages can't be processed, skip them
continue
elif key == 'image':
# If there's image data, use its shape and mean value
try:
if value is not None:
hash_components.append(f"image_shape:{value.shape}")
hash_components.append(f"image_mean:{float(value.mean()):.4f}")
except:
# If image can't be processed, skip it
continue
elif isinstance(value, (str, int, float, bool, type(None))):
# Handle basic types directly
hash_components.append(f"{key}:{value}")
elif isinstance(value, list):
# Handle lists by converting to JSON for stable string representation
try:
list_str = json.dumps(value, sort_keys=True)
hash_components.append(f"{key}:{list_str}")
except:
continue
else:
# For any other type, use its string representation
try:
hash_components.append(f"{key}:{str(value)}")
except:
continue
# Join all components and create hash
hash_string = "|".join(hash_components)
return hashlib.md5(hash_string.encode()).hexdigest()