From 486cdb5d7d16bc625bddc631e59d86e5b14cd0b0 Mon Sep 17 00:00:00 2001 From: gilesb Date: Thu, 8 Jan 2026 23:56:53 +0000 Subject: [PATCH] Fix build_dag_from_recipe to use correct Node API - Use keyword arguments for Node constructor - Pass inputs list to Node instead of calling non-existent add_edge - Two-pass approach: create SOURCE nodes first, then resolve input names to content-addressed IDs for dependent nodes - Properly set output node using resolved ID Co-Authored-By: Claude Opus 4.5 --- server.py | 69 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 17 deletions(-) diff --git a/server.py b/server.py index 46694af..bab876f 100644 --- a/server.py +++ b/server.py @@ -1333,27 +1333,33 @@ def build_dag_from_recipe(yaml_config: dict, user_inputs: dict[str, str], recipe from artdag import DAG, Node dag = DAG() - node_map = {} # node_id -> Node + name_to_id = {} # Map YAML node names to content-addressed IDs registry = yaml_config.get("registry", {}) assets = registry.get("assets", {}) effects = registry.get("effects", {}) dag_config = yaml_config.get("dag", {}) nodes = dag_config.get("nodes", []) + output_node = dag_config.get("output") + # First pass: create all nodes and map names to IDs for node_def in nodes: - node_id = node_def.get("id") + node_name = node_def.get("id") node_type = node_def.get("type") node_config = node_def.get("config", {}) - input_ids = node_def.get("inputs", []) if node_type == "SOURCE": if node_config.get("input"): # Variable input - use user-provided hash - content_hash = user_inputs.get(node_id) + content_hash = user_inputs.get(node_name) if not content_hash: - raise HTTPException(400, f"Missing input for node {node_id}") - node = Node(node_id, "SOURCE", {"content_hash": content_hash}) + raise HTTPException(400, f"Missing input for node {node_name}") + node = Node( + node_type="SOURCE", + config={"content_hash": content_hash}, + inputs=[], + name=node_name + ) else: # Fixed input - use registry hash asset_name = node_config.get("asset") @@ -1361,24 +1367,53 @@ def build_dag_from_recipe(yaml_config: dict, user_inputs: dict[str, str], recipe content_hash = asset_info.get("hash") if not content_hash: raise HTTPException(400, f"Asset {asset_name} not found in registry") - node = Node(node_id, "SOURCE", {"content_hash": content_hash}) - elif node_type == "EFFECT": + node = Node( + node_type="SOURCE", + config={"content_hash": content_hash}, + inputs=[], + name=node_name + ) + name_to_id[node_name] = node.node_id + dag.add_node(node) + + # Second pass: create nodes with inputs (now we can resolve input names to IDs) + for node_def in nodes: + node_name = node_def.get("id") + node_type = node_def.get("type") + node_config = node_def.get("config", {}) + input_names = node_def.get("inputs", []) + + # Skip SOURCE nodes (already added) + if node_type == "SOURCE": + continue + + # Resolve input names to content-addressed IDs + input_ids = [name_to_id[name] for name in input_names if name in name_to_id] + + if node_type == "EFFECT": effect_name = node_config.get("effect") effect_info = effects.get(effect_name, {}) effect_hash = effect_info.get("hash") - node = Node(node_id, "EFFECT", {"effect": effect_name, "effect_hash": effect_hash}) + node = Node( + node_type="EFFECT", + config={"effect": effect_name, "effect_hash": effect_hash}, + inputs=input_ids, + name=node_name + ) else: - node = Node(node_id, node_type, node_config) + node = Node( + node_type=node_type, + config=node_config, + inputs=input_ids, + name=node_name + ) - node_map[node_id] = node + name_to_id[node_name] = node.node_id dag.add_node(node) - # Connect edges - for node_def in nodes: - node_id = node_def.get("id") - input_ids = node_def.get("inputs", []) - for input_id in input_ids: - dag.add_edge(input_id, node_id) + # Set output node + if output_node and output_node in name_to_id: + dag.set_output(name_to_id[output_node]) return dag