@@ -548,7 +548,10 @@ def from_yaml(cls, path: str, agent_factory: Callable[[dict[str, Any]], Any] | N
548548 """
549549 Load a workflow from a YAML file.
550550
551- Expected format::
551+ Dependencies can be declared per-node with ``depends_on`` or in a
552+ top-level ``edges`` list; both build the same graph. Each edge is either
553+ a ``[source, target]`` pair or a mapping with ``source``/``target``
554+ (aliases ``from``/``to``) and an optional ``key``::
552555
553556 workflow:
554557 name: my_pipeline
@@ -560,6 +563,10 @@ def from_yaml(cls, path: str, agent_factory: Callable[[dict[str, Any]], Any] | N
560563 agent: summary_agent
561564 depends_on: [search]
562565
566+ # equivalent wiring via a top-level edges block:
567+ # edges:
568+ # - [search, summarize]
569+
563570 Args:
564571 path: Path to the YAML file
565572 agent_factory: Optional callable that receives a node dict and
@@ -568,6 +575,10 @@ def from_yaml(cls, path: str, agent_factory: Callable[[dict[str, Any]], Any] | N
568575
569576 Returns:
570577 A validated WorkflowDAG
578+
579+ An unrecognized top-level key is reported with a warning rather than
580+ dropped silently, so a mis-keyed file (e.g. ``edge:`` instead of
581+ ``edges:``) does not validate as a workflow with all its declared wiring.
571582 """
572583 import yaml # pyyaml is an existing dependency
573584
@@ -577,6 +588,17 @@ def from_yaml(cls, path: str, agent_factory: Callable[[dict[str, Any]], Any] | N
577588 wf_data = data .get ("workflow" , data )
578589 name = wf_data .get ("name" , "workflow" )
579590
591+ # Surface unknown top-level keys instead of dropping the wiring silently.
592+ _known_top = {"name" , "nodes" , "edges" , "description" , "metadata" }
593+ unknown = [k for k in wf_data if k not in _known_top ]
594+ if unknown :
595+ logger .warning (
596+ "Workflow '%s': ignoring unrecognized top-level key(s) %s "
597+ "(recognized: %s). Declare dependencies with per-node "
598+ "'depends_on' or a top-level 'edges' list." ,
599+ name , sorted (unknown ), sorted (_known_top ),
600+ )
601+
580602 dag = cls (name = name )
581603
582604 node_defs = wf_data .get ("nodes" , [])
@@ -589,19 +611,54 @@ def from_yaml(cls, path: str, agent_factory: Callable[[dict[str, Any]], Any] | N
589611 id = nd ["id" ],
590612 agent = agent ,
591613 tools = nd .get ("tools" , []),
614+ input_keys = nd .get ("input_keys" , []),
592615 output_key = nd .get ("output_key" , nd ["id" ]),
593616 metadata = {k : v for k , v in nd .items ()
594- if k not in ("id" , "tools" , "output_key" , "depends_on" , "agent" )},
617+ if k not in ("id" , "tools" , "input_keys" ,
618+ "output_key" , "depends_on" , "agent" )},
595619 )
596620 dag .add_node (node )
597621
598- # Create edges from depends_on
622+ # Create edges from per-node depends_on ...
599623 for nd in node_defs :
600624 for dep in nd .get ("depends_on" , []):
601625 dag .connect (dep , nd ["id" ])
602626
627+ # ... and from a top-level edges list (same graph; both may be present).
628+ for edge in wf_data .get ("edges" , []):
629+ src , tgt , key = cls ._parse_yaml_edge (edge )
630+ dag .connect (src , tgt , key = key )
631+
603632 return dag
604633
634+ @staticmethod
635+ def _parse_yaml_edge (edge : Any ) -> tuple [str , str , str | None ]:
636+ """Parse one entry of a YAML ``edges`` list into ``(source, target, key)``.
637+
638+ Accepts a ``[source, target]`` pair or a mapping with ``source``/``target``
639+ (aliases ``from``/``to``) and an optional ``key``.
640+ """
641+ if isinstance (edge , list | tuple ):
642+ if len (edge ) < 2 :
643+ raise ValueError (
644+ f"Workflow edge { edge !r} must be [source, target]."
645+ )
646+ return str (edge [0 ]), str (edge [1 ]), (str (edge [2 ]) if len (edge ) > 2 else None )
647+ if isinstance (edge , dict ):
648+ src = edge .get ("source" , edge .get ("from" ))
649+ tgt = edge .get ("target" , edge .get ("to" ))
650+ if not src or not tgt :
651+ raise ValueError (
652+ f"Workflow edge { edge !r} needs 'source'/'target' "
653+ "(aliases 'from'/'to')."
654+ )
655+ key = edge .get ("key" )
656+ return str (src ), str (tgt ), (str (key ) if key is not None else None )
657+ raise ValueError (
658+ f"Unsupported workflow edge { edge !r} : use [source, target] or "
659+ "{source, target}."
660+ )
661+
605662 # -- Introspection --
606663
607664 def get_node (self , node_id : str ) -> WorkflowNode | None :
0 commit comments