feat: add meta_info output to LoraInfo_UTK node - 参考comfyui-lora-auto-trigger-words项目,增加LoRA文件元数据提取功能

This commit is contained in:
Cyber Dick Lang
2025-07-15 18:47:37 +08:00
parent ee4cce63cb
commit c701d628cf
3 changed files with 77 additions and 10 deletions
+1 -1
View File
@@ -97,7 +97,7 @@ tqdm
#### 系统工具
- **PurgeVRAM_UTK**:显存清理,支持选择性清理缓存和模型
- **LoraInfo_UTK**:LoRA信息查询,获取CivitAI触发词、示例提示词、基础模型等信息
- **LoraInfo_UTK**:LoRA信息查询,获取CivitAI触发词、示例提示词、基础模型、元数据等信息
#### 预设系统
- **LoadKontextPresets_UTK**:Kontext VLM系统预设,包含27种专业图像变换预设
+67 -8
View File
@@ -55,12 +55,55 @@ def calculate_sha256(file_path):
sha256_hash.update(chunk)
return sha256_hash.hexdigest()
def get_metadata(filepath):
"""从LoRA文件中提取元数据"""
try:
filepath = folder_paths.get_full_path("loras", filepath)
with open(filepath, "rb") as file:
# https://github.com/huggingface/safetensors#format
# 8 bytes: N, an unsigned little-endian 64-bit integer, containing the size of the header
header_size = int.from_bytes(file.read(8), "little", signed=False)
if header_size <= 0:
return None
header = file.read(header_size)
if header_size <= 0:
return None
header_json = json.loads(header)
return header_json["__metadata__"] if "__metadata__" in header_json else None
except Exception as e:
print(f"Error reading metadata from {filepath}: {e}")
return None
def sort_tags_by_frequency(meta_tags):
"""按训练频率排序标签"""
if meta_tags is None:
return []
if "ss_tag_frequency" in meta_tags:
meta_tags = meta_tags["ss_tag_frequency"]
meta_tags = json.loads(meta_tags)
sorted_tags = {}
for _, dataset in meta_tags.items():
for tag, count in dataset.items():
tag = str(tag).strip()
if tag in sorted_tags:
sorted_tags[tag] = sorted_tags[tag] + count
else:
sorted_tags[tag] = count
# 按训练频率排序,最常见的标签在前
sorted_tags = dict(sorted(sorted_tags.items(), key=lambda item: item[1], reverse=True))
return list(sorted_tags.keys())
else:
return []
def get_lora_info(lora_name):
db = load_json_from_file(db_path)
output = None
examplePrompt = None
trainedWords = None
baseModel = None
metaInfo = None
loraInfo = db.get(lora_name, {})
@@ -71,6 +114,7 @@ def get_lora_info(lora_name):
examplePrompt = loraInfo.get('examplePrompt', None)
trainedWords = loraInfo.get('trainedWords', None)
baseModel = loraInfo.get('baseModel', None)
metaInfo = loraInfo.get('metaInfo', None)
if output is None or baseModel is None:
output = ""
@@ -106,24 +150,38 @@ def get_lora_info(lora_name):
output += f"{key}: {value}\n"
output += '\n'
# 获取元数据信息
metadata = get_metadata(lora_name)
if metadata:
metaInfo = json.dumps(metadata, indent=2, ensure_ascii=False)
else:
metaInfo = ""
db[lora_name] = {
"output": output,
"trainedWords": trainedWords,
"examplePrompt": examplePrompt,
"baseModel": baseModel
"baseModel": baseModel,
"metaInfo": metaInfo
}
save_dict_to_json(db, db_path)
return (output, trainedWords, examplePrompt, baseModel)
return (output, trainedWords, examplePrompt, baseModel, metaInfo)
@server.PromptServer.instance.routes.post('/lora_info_utk')
async def fetch_lora_info(request):
post = await request.post()
lora_name = post.get("lora_name")
(output, triggerWords, examplePrompt, baseModel) = get_lora_info(lora_name)
(output, triggerWords, examplePrompt, baseModel, metaInfo) = get_lora_info(lora_name)
return web.json_response({"output": output, "triggerWords": triggerWords, "examplePrompt": examplePrompt, "baseModel": baseModel})
return web.json_response({
"output": output,
"triggerWords": triggerWords,
"examplePrompt": examplePrompt,
"baseModel": baseModel,
"metaInfo": metaInfo
})
class LoraInfo_UTK:
"""
@@ -135,6 +193,7 @@ class LoraInfo_UTK:
- 基础模型 (Base Model)
- CivitAI链接
- 示例图片
- 元数据信息 (Meta Info)
"""
@classmethod
@@ -146,14 +205,14 @@ class LoraInfo_UTK:
},
}
RETURN_NAMES = ("lora_name", "civitai_trigger", "example_prompt", "civitai_info")
RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING")
RETURN_NAMES = ("lora_name", "civitai_trigger", "example_prompt", "civitai_info", "meta_info")
RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING", "STRING")
FUNCTION = "lora_info"
OUTPUT_NODE = True
CATEGORY = "UniversalToolkit/Tools"
def lora_info(self, lora_name):
(output, triggerWords, examplePrompt, baseModel) = get_lora_info(lora_name)
(output, triggerWords, examplePrompt, baseModel, metaInfo) = get_lora_info(lora_name)
# 构建信息文本
info_text = f"LoRA: {lora_name}\n"
@@ -171,7 +230,7 @@ class LoraInfo_UTK:
"text": (info_text,),
"model": (baseModel,)
},
"result": (lora_name, triggerWords or "", examplePrompt or "", info_text)
"result": (lora_name, triggerWords or "", examplePrompt or "", info_text, metaInfo or "")
}
+9 -1
View File
@@ -18,8 +18,14 @@ app.registerExtension({
["STRING", { multiline: true }],
app,
).widget;
this.metaInfoWidget = ComfyWidgets["STRING"](
this,
"meta_info",
["STRING", { multiline: true }],
app,
).widget;
const [loraNameWidget, baseModelWidget, outputWidget] = this.widgets;
const [loraNameWidget, baseModelWidget, outputWidget, metaInfoWidget] = this.widgets;
loraNameWidget.callback = () => {
const value = loraNameWidget.value;
@@ -32,6 +38,7 @@ app.registerExtension({
.then((resp) => {
baseModelWidget.value = resp.baseModel;
outputWidget.value = resp.output;
metaInfoWidget.value = resp.metaInfo;
});
};
}
@@ -41,6 +48,7 @@ app.registerExtension({
onExecuted?.apply(this, [message]);
this.showValueWidget.value = message.text[0];
this.baseModelWidget.value = message.model[0];
this.metaInfoWidget.value = message.metaInfo ? message.metaInfo[0] : "";
}
}
},