"""워크플로우 JSON 파서 및 BridgeInput 값 패처.""" import copy import json from pathlib import Path from typing import Any, Dict, Tuple, Union from .types import convert_value BRIDGE_INPUT_PREFIX = "BridgeInput" def load_workflow(source: Union[str, dict, Path]) -> dict: """워크플로우를 파일 경로 또는 dict로 로드한다.""" if isinstance(source, dict): return source path = Path(source) with path.open("r", encoding="utf-8") as f: return json.load(f) def find_bridge_inputs(workflow: dict) -> Dict[str, Tuple[str, str]]: """ 워크플로우에서 BridgeInput 노드를 탐색한다. Returns: { "input_name": (node_id, type_name) } 예: { "prompt": ("1", "STRING"), "steps": ("3", "INT"), "image": ("5", "ANY") } """ result: Dict[str, Tuple[str, str]] = {} for node_id, node in workflow.items(): if not isinstance(node, dict): continue class_type = node.get("class_type", "") if class_type.startswith(BRIDGE_INPUT_PREFIX): name = node.get("inputs", {}).get("name") type_name = class_type[len(BRIDGE_INPUT_PREFIX):] # "STRING", "INT", "ANY" 등 if name: result[name] = (node_id, type_name) return result def patch_inputs(workflow: dict, inputs: Dict[str, Any]) -> dict: """ 원본 워크플로우를 deepcopy 후 BridgeInput 노드의 value를 패치한다. 스칼라 타입 (STRING/INT/FLOAT/BOOLEAN): - inputs.value (위젯) 와 inputs.value_link (연결 포트) 모두 패치 - value_link가 연결된 경우 런타임에 우선되므로 위젯 값도 동기화 복합 타입 / ANY: - inputs.value 패치 """ patched = copy.deepcopy(workflow) bridge_map = find_bridge_inputs(patched) PRIMITIVE = {"STRING", "INT", "FLOAT", "BOOLEAN"} for name, value in inputs.items(): if name not in bridge_map: available = list(bridge_map.keys()) raise ValueError( f"BridgeInput '{name}' not found in workflow. " f"Available inputs: {available}" ) node_id, type_name = bridge_map[name] converted = convert_value(value, type_name) node_inputs = patched[node_id]["inputs"] if type_name in PRIMITIVE: # 위젯 값 패치 node_inputs["value"] = converted # value_link가 이미 노드 링크([node_id, idx])로 연결된 경우 건드리지 않음 # value_link가 없거나 null이면 value(위젯)가 자동으로 사용됨 else: # 복합 타입 / ANY node_inputs["value"] = converted return patched