195 lines
5.9 KiB
Python
195 lines
5.9 KiB
Python
import os
|
||
import json
|
||
import logging
|
||
import traceback
|
||
from typing import Optional
|
||
from typing import Union
|
||
|
||
# ----------------------------
|
||
# 日志系统配置
|
||
# ----------------------------
|
||
desktop_path = os.path.join(os.path.expanduser("~"), "Desktop")
|
||
log_path = os.path.join(desktop_path, "ollama_comfyui.log")
|
||
|
||
# 日志初始化
|
||
logging.basicConfig(
|
||
filename=log_path,
|
||
level=logging.DEBUG,
|
||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||
filemode='w'
|
||
)
|
||
logger = logging.getLogger(__name__)
|
||
logger.addHandler(logging.StreamHandler()) # 控制台输出
|
||
|
||
class StringArrayFormatter:
|
||
"""
|
||
字符串数组处理节点
|
||
|
||
功能:
|
||
- 解析JSON格式字符串数组
|
||
- 生成格式化统计报告
|
||
- 自动验证输入格式
|
||
- 支持自定义分隔符
|
||
- 错误友好提示
|
||
"""
|
||
|
||
# 类级日志器 ✅ 修正点1
|
||
logger = logging.getLogger("ComfyUI-Ollama")
|
||
|
||
@classmethod
|
||
def INPUT_TYPES(cls):
|
||
return {
|
||
"required": {
|
||
"input_str": ("STRING", {
|
||
"multiline": True,
|
||
"default": '["示例文本1", "示例文本2"]',
|
||
"dynamicPrompts": False
|
||
}),
|
||
"show_index": ("BOOLEAN", {"default": True}),
|
||
"max_length": ("INT", {
|
||
"default": 200,
|
||
"min": 0,
|
||
"max": 65535,
|
||
"step": 50,
|
||
"display": "slider"
|
||
}),
|
||
},
|
||
"optional": {
|
||
"delimiter": ("STRING", {"default": "|", "lazy": True}),
|
||
}
|
||
}
|
||
|
||
# 更新 RETURN_TYPES 和 RETURN_NAMES
|
||
RETURN_TYPES = ( "INT", "LIST_STR")
|
||
RETURN_NAMES = ("长度", "字符串数组")
|
||
FUNCTION = "format_array"
|
||
CATEGORY = "LLM/文本处理"
|
||
OUTPUT_NODE = True
|
||
|
||
def _validate_input(self, input_str: str) -> Optional[list]:
|
||
"""输入验证与解析"""
|
||
try:
|
||
data = json.loads(input_str)
|
||
if not isinstance(data, list):
|
||
raise ValueError("输入不是数组格式")
|
||
if not all(isinstance(item, str) for item in data):
|
||
raise ValueError("数组包含非字符串元素")
|
||
return data
|
||
except json.JSONDecodeError:
|
||
return None
|
||
except Exception as e:
|
||
logger.error(f"❌ 输入验证失败: {str(e)}")
|
||
return None
|
||
|
||
def _format_item(self, index: int, text: str, delimiter: str, max_len: int) -> str:
|
||
"""单个元素的格式化处理"""
|
||
# 截断处理
|
||
display_text = text[:max_len] + "..." if len(text) > max_len else text
|
||
|
||
# 转义分隔符
|
||
safe_text = display_text.replace(delimiter, f"\\{delimiter}")
|
||
|
||
# 添加索引
|
||
if index >= 0:
|
||
return f"[{index:02d}] {safe_text}"
|
||
return safe_text
|
||
|
||
def format_array(self, input_str: str, show_index: bool, max_length: int, delimiter: str = "|"):
|
||
# 输入验证(调整后允许自动转换元素类型)
|
||
str_array = []
|
||
try:
|
||
data = json.loads(input_str)
|
||
if not isinstance(data, list):
|
||
logger.error(f"⚠️ 输入必须是数组: {input_str}")
|
||
return (0, [])
|
||
# 强制所有元素转为字符串
|
||
str_array = [str(item) for item in data]
|
||
except Exception as e:
|
||
logger.error(f"⚠️ 解析失败: {str(e)}")
|
||
return (0, [])
|
||
|
||
# 返回原始数组和长度(新增第三个返回值)
|
||
return (
|
||
len(str_array),
|
||
str_array # 新增数组输出
|
||
)
|
||
|
||
@classmethod
|
||
def IS_CHANGED(cls, input_str: str, **kwargs):
|
||
"""通过哈希值检测输入变化"""
|
||
return hash(input_str)
|
||
|
||
|
||
class StringArrayIndexer:
|
||
"""
|
||
字符串数组索引器(增强输入兼容性)
|
||
"""
|
||
@classmethod
|
||
def INPUT_TYPES(cls):
|
||
return {
|
||
"required": {
|
||
"input_array": ("LIST_STR", {"forceInput": True}),
|
||
"index": ("INT", {"default": 0}),
|
||
},
|
||
"optional": {
|
||
"default_value": ("STRING", {"default": "🚫 无效索引"}),
|
||
}
|
||
}
|
||
|
||
RETURN_TYPES = ("STRING",)
|
||
FUNCTION = "get_element"
|
||
CATEGORY = "LLM/文本处理"
|
||
|
||
def get_element(self, input_array, index, default_value="🚫 无效索引"):
|
||
# 多模式解析
|
||
parsed_list = self._parse_input(input_array)
|
||
|
||
if not parsed_list:
|
||
return (default_value,)
|
||
|
||
# 处理负索引
|
||
if index < 0:
|
||
index += len(parsed_list)
|
||
|
||
# 边界检查
|
||
if 0 <= index < len(parsed_list):
|
||
return (parsed_list[index],)
|
||
|
||
return (default_value,)
|
||
|
||
def _parse_input(self, raw_input):
|
||
"""支持解析多种格式的列表输入"""
|
||
# 类型 1:直接列表输入
|
||
if isinstance(raw_input, list):
|
||
return [str(item) for item in raw_input]
|
||
|
||
# 类型 2:JSON 字符串
|
||
try:
|
||
data = json.loads(raw_input)
|
||
if isinstance(data, list):
|
||
return [str(item) for item in data]
|
||
except:
|
||
pass
|
||
|
||
# 类型 3:Python 风格列表字符串
|
||
if raw_input.startswith("[") and raw_input.endswith("]"):
|
||
try:
|
||
return [
|
||
item.strip().strip("\"' ")
|
||
for item in raw_input[1:-1].split(",")
|
||
]
|
||
except:
|
||
pass
|
||
|
||
return None
|
||
|
||
# 节点注册
|
||
NODE_CLASS_MAPPINGS = {
|
||
"StringArrayIndexer": StringArrayIndexer,
|
||
"StringArrayFormatter": StringArrayFormatter
|
||
}
|
||
|
||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||
"StringArrayIndexer": "📜 数组元素索引器",
|
||
"StringArrayFormatter": "📜 文本数组格式化器"
|
||
} |