refactor: 优化工作流API参数解析逻辑,简化输入输出字段处理,增强代码可读性
This commit is contained in:
@@ -364,7 +364,8 @@ async def submit_workflow_to_queue(
|
|||||||
|
|
||||||
def parse_api_spec(workflow_data: dict) -> Dict[str, Dict[str, Any]]:
|
def parse_api_spec(workflow_data: dict) -> Dict[str, Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
解析工作流,并根据规范 '{基础名}_{属性名}_{可选计数}' 生成API参数名。
|
解析工作流,并根据规范生成嵌套结构的API参数名。
|
||||||
|
结构: {'节点名': {'node_id': '节点ID', '字段名': {字段信息}}}
|
||||||
"""
|
"""
|
||||||
spec = {"inputs": {}, "outputs": {}}
|
spec = {"inputs": {}, "outputs": {}}
|
||||||
if "nodes" not in workflow_data or not isinstance(workflow_data["nodes"], list):
|
if "nodes" not in workflow_data or not isinstance(workflow_data["nodes"], list):
|
||||||
@@ -373,8 +374,6 @@ def parse_api_spec(workflow_data: dict) -> Dict[str, Dict[str, Any]]:
|
|||||||
)
|
)
|
||||||
|
|
||||||
nodes_map = {str(node["id"]): node for node in workflow_data["nodes"]}
|
nodes_map = {str(node["id"]): node for node in workflow_data["nodes"]}
|
||||||
input_name_counter = defaultdict(int)
|
|
||||||
output_name_counter = defaultdict(int)
|
|
||||||
|
|
||||||
for node_id, node in nodes_map.items():
|
for node_id, node in nodes_map.items():
|
||||||
title: str = node.get("title")
|
title: str = node.get("title")
|
||||||
@@ -382,65 +381,77 @@ def parse_api_spec(workflow_data: dict) -> Dict[str, Dict[str, Any]]:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
if title.startswith(API_INPUT_PREFIX):
|
if title.startswith(API_INPUT_PREFIX):
|
||||||
base_name = title[len(API_INPUT_PREFIX) :].lower()
|
# 提取节点名(去掉API_INPUT_PREFIX前缀)
|
||||||
|
node_name = title[len(API_INPUT_PREFIX) :].lower()
|
||||||
|
|
||||||
|
# 如果节点名不在inputs中,创建新的节点条目
|
||||||
|
if node_name not in spec["inputs"]:
|
||||||
|
spec["inputs"][node_name] = {
|
||||||
|
"node_id": node_id,
|
||||||
|
"class_type": node.get("type"),
|
||||||
|
}
|
||||||
|
|
||||||
if "inputs" in node:
|
if "inputs" in node:
|
||||||
for a_input in node.get("inputs", []):
|
for a_input in node.get("inputs", []):
|
||||||
if a_input.get("link") is None and "widget" in a_input:
|
if a_input.get("link") is None and "widget" in a_input:
|
||||||
widget_name = a_input["widget"]["name"].lower()
|
widget_name = a_input["widget"]["name"].lower()
|
||||||
|
|
||||||
# [BUG修复] 构建有意义的基础参数名
|
# 特殊处理:隐藏LoadImage节点的upload字段
|
||||||
param_name_candidate = f"{base_name}_{widget_name}"
|
if node.get("type") == "LoadImage" and widget_name == "upload":
|
||||||
|
continue
|
||||||
|
|
||||||
input_name_counter[param_name_candidate] += 1
|
# 确保inputs字段存在
|
||||||
count = input_name_counter[param_name_candidate]
|
if "inputs" not in spec["inputs"][node_name]:
|
||||||
final_param_name = (
|
spec["inputs"][node_name]["inputs"] = {}
|
||||||
f"{param_name_candidate}_{count}"
|
|
||||||
if count > 1
|
|
||||||
else param_name_candidate
|
|
||||||
)
|
|
||||||
|
|
||||||
input_type_str = a_input.get("type", "STRING").upper()
|
# 直接使用widget_name作为字段名
|
||||||
param_type = "string"
|
spec["inputs"][node_name]["inputs"][widget_name] = {
|
||||||
if "COMBO" in input_type_str:
|
"type": _get_param_type(a_input.get("type", "STRING")),
|
||||||
param_type = "UploadFile"
|
|
||||||
elif "INT" in input_type_str:
|
|
||||||
param_type = "int"
|
|
||||||
elif "FLOAT" in input_type_str:
|
|
||||||
param_type = "float"
|
|
||||||
|
|
||||||
spec["inputs"][final_param_name] = {
|
|
||||||
"node_id": node_id,
|
|
||||||
"type": param_type,
|
|
||||||
"widget_name": a_input["widget"]["name"],
|
"widget_name": a_input["widget"]["name"],
|
||||||
}
|
}
|
||||||
|
|
||||||
elif title.startswith(API_OUTPUT_PREFIX):
|
elif title.startswith(API_OUTPUT_PREFIX):
|
||||||
base_name = title[len(API_OUTPUT_PREFIX) :].lower()
|
# 提取节点名(去掉API_OUTPUT_PREFIX前缀)
|
||||||
|
node_name = title[len(API_OUTPUT_PREFIX) :].lower()
|
||||||
|
|
||||||
|
# 如果节点名不在outputs中,创建新的节点条目
|
||||||
|
if node_name not in spec["outputs"]:
|
||||||
|
spec["outputs"][node_name] = {
|
||||||
|
"node_id": node_id,
|
||||||
|
"class_type": node.get("type"),
|
||||||
|
}
|
||||||
|
|
||||||
if "outputs" in node:
|
if "outputs" in node:
|
||||||
for an_output in node.get("outputs", []):
|
for an_output in node.get("outputs", []):
|
||||||
output_name = an_output["name"].lower()
|
output_name = an_output["name"].lower()
|
||||||
|
|
||||||
# [BUG修复] 构建有意义的基础参数名
|
# 确保outputs字段存在
|
||||||
param_name_candidate = f"{base_name}_{output_name}"
|
if "outputs" not in spec["outputs"][node_name]:
|
||||||
|
spec["outputs"][node_name]["outputs"] = {}
|
||||||
|
|
||||||
output_name_counter[param_name_candidate] += 1
|
# 直接使用output_name作为字段名
|
||||||
count = output_name_counter[param_name_candidate]
|
spec["outputs"][node_name]["outputs"][output_name] = {
|
||||||
final_param_name = (
|
|
||||||
f"{param_name_candidate}_{count}"
|
|
||||||
if count > 1
|
|
||||||
else param_name_candidate
|
|
||||||
)
|
|
||||||
|
|
||||||
spec["outputs"][final_param_name] = {
|
|
||||||
"node_id": node_id,
|
|
||||||
"class_type": node.get("type"),
|
|
||||||
"output_name": an_output["name"],
|
"output_name": an_output["name"],
|
||||||
"output_index": node["outputs"].index(an_output),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return spec
|
return spec
|
||||||
|
|
||||||
|
|
||||||
|
def _get_param_type(input_type_str: str) -> str:
|
||||||
|
"""
|
||||||
|
根据输入类型字符串确定参数类型
|
||||||
|
"""
|
||||||
|
input_type_str = input_type_str.upper()
|
||||||
|
if "COMBO" in input_type_str:
|
||||||
|
return "UploadFile"
|
||||||
|
elif "INT" in input_type_str:
|
||||||
|
return "int"
|
||||||
|
elif "FLOAT" in input_type_str:
|
||||||
|
return "float"
|
||||||
|
else:
|
||||||
|
return "string"
|
||||||
|
|
||||||
|
|
||||||
async def patch_workflow(
|
async def patch_workflow(
|
||||||
workflow_data: dict,
|
workflow_data: dict,
|
||||||
api_spec: dict[str, dict[str, Any]],
|
api_spec: dict[str, dict[str, Any]],
|
||||||
@@ -449,70 +460,79 @@ async def patch_workflow(
|
|||||||
) -> dict:
|
) -> dict:
|
||||||
"""
|
"""
|
||||||
将request_data中的参数值,patch到workflow_data中。并返回修改后的workflow_data。
|
将request_data中的参数值,patch到workflow_data中。并返回修改后的workflow_data。
|
||||||
|
|
||||||
|
request_data结构: {"节点名称": {"字段名称": "字段值"}}
|
||||||
"""
|
"""
|
||||||
if "nodes" not in workflow_data:
|
if "nodes" not in workflow_data:
|
||||||
raise ValueError("无效的工作流格式")
|
raise ValueError("无效的工作流格式")
|
||||||
|
|
||||||
nodes_map = {str(node["id"]): node for node in workflow_data["nodes"]}
|
nodes_map = {str(node["id"]): node for node in workflow_data["nodes"]}
|
||||||
|
|
||||||
for param_name, value in request_data.items():
|
for node_name, node_fields in request_data.items():
|
||||||
if param_name not in api_spec["inputs"]:
|
if node_name not in api_spec["inputs"]:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
spec = api_spec["inputs"][param_name]
|
node_spec = api_spec["inputs"][node_name]
|
||||||
node_id = spec["node_id"]
|
node_id = node_spec["node_id"]
|
||||||
if node_id not in nodes_map:
|
if node_id not in nodes_map:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
target_node = nodes_map[node_id]
|
target_node = nodes_map[node_id]
|
||||||
widget_name_to_patch = spec["widget_name"]
|
|
||||||
|
|
||||||
# 找到所有属于widget的输入,并确定目标widget的索引
|
# 处理该节点下的所有字段
|
||||||
widget_inputs = [
|
for field_name, value in node_fields.items():
|
||||||
inp for inp in target_node.get("inputs", []) if "widget" in inp
|
if "inputs" not in node_spec or field_name not in node_spec["inputs"]:
|
||||||
]
|
continue
|
||||||
|
|
||||||
target_widget_index = -1
|
field_spec = node_spec["inputs"][field_name]
|
||||||
for i, widget_input in enumerate(widget_inputs):
|
widget_name_to_patch = field_spec["widget_name"]
|
||||||
# "widget" 字典可能不存在或没有 "name" 键
|
|
||||||
if widget_input.get("widget", {}).get("name") == widget_name_to_patch:
|
|
||||||
target_widget_index = i
|
|
||||||
break
|
|
||||||
|
|
||||||
if target_widget_index == -1:
|
# 找到所有属于widget的输入,并确定目标widget的索引
|
||||||
logger.warning(
|
widget_inputs = [
|
||||||
f"在节点 {node_id} 中未找到名为 '{widget_name_to_patch}' 的 widget。跳过此参数。"
|
inp for inp in target_node.get("inputs", []) if "widget" in inp
|
||||||
)
|
]
|
||||||
continue
|
|
||||||
|
|
||||||
# 确保 `widgets_values` 存在且为列表
|
target_widget_index = -1
|
||||||
if "widgets_values" not in target_node or not isinstance(
|
for i, widget_input in enumerate(widget_inputs):
|
||||||
target_node.get("widgets_values"), list
|
# "widget" 字典可能不存在或没有 "name" 键
|
||||||
):
|
if widget_input.get("widget", {}).get("name") == widget_name_to_patch:
|
||||||
# 如果不存在或格式错误,根据widget数量创建一个占位符列表
|
target_widget_index = i
|
||||||
target_node["widgets_values"] = [None] * len(widget_inputs)
|
break
|
||||||
|
|
||||||
# 确保 `widgets_values` 列表足够长
|
if target_widget_index == -1:
|
||||||
while len(target_node["widgets_values"]) <= target_widget_index:
|
logger.warning(
|
||||||
target_node["widgets_values"].append(None)
|
f"在节点 {node_id} 中未找到名为 '{widget_name_to_patch}' 的 widget。跳过此参数。"
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
# 根据API规范转换数据类型
|
# 确保 `widgets_values` 存在且为列表
|
||||||
target_type = str
|
if "widgets_values" not in target_node or not isinstance(
|
||||||
if spec["type"] == "int":
|
target_node.get("widgets_values"), list
|
||||||
target_type = int
|
):
|
||||||
elif spec["type"] == "float":
|
# 如果不存在或格式错误,根据widget数量创建一个占位符列表
|
||||||
target_type = float
|
target_node["widgets_values"] = [None] * len(widget_inputs)
|
||||||
|
|
||||||
# 在正确的位置上更新值
|
# 确保 `widgets_values` 列表足够长
|
||||||
try:
|
while len(target_node["widgets_values"]) <= target_widget_index:
|
||||||
if target_node.get("type") == "LoadImage":
|
target_node["widgets_values"].append(None)
|
||||||
value = await upload_image_to_comfy(value, server)
|
|
||||||
target_node["widgets_values"][target_widget_index] = target_type(value)
|
# 根据API规范转换数据类型
|
||||||
except (ValueError, TypeError) as e:
|
target_type = str
|
||||||
logger.warning(
|
if field_spec["type"] == "int":
|
||||||
f"无法将参数 '{param_name}' 的值 '{value}' 转换为类型 '{spec['type']}'。错误: {e}"
|
target_type = int
|
||||||
)
|
elif field_spec["type"] == "float":
|
||||||
continue
|
target_type = float
|
||||||
|
|
||||||
|
# 在正确的位置上更新值
|
||||||
|
try:
|
||||||
|
if target_node.get("type") == "LoadImage":
|
||||||
|
value = await upload_image_to_comfy(value, server)
|
||||||
|
target_node["widgets_values"][target_widget_index] = target_type(value)
|
||||||
|
except (ValueError, TypeError) as e:
|
||||||
|
logger.warning(
|
||||||
|
f"无法将节点 '{node_name}' 的字段 '{field_name}' 的值 '{value}' 转换为类型 '{field_spec['type']}'。错误: {e}"
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
workflow_data["nodes"] = list(nodes_map.values())
|
workflow_data["nodes"] = list(nodes_map.values())
|
||||||
return workflow_data
|
return workflow_data
|
||||||
|
|||||||
Reference in New Issue
Block a user