11
22from __future__ import annotations
33
4- from typing import cast , Any , Hashable
4+ from typing import cast , Any , Hashable , Callable
55from collections import defaultdict
66from collections .abc import Hashable , Sequence
77import asyncio
1010from ..states import StateProtocol , SharedProtocol
1111from ..diff import Change , ChangeConflictException , Diff
1212from ..nodes import Node , END , START
13- from .types import SingleNext , NextNode , ErrorEntry , SingleErrorSource , Entries , BranchContainer , SingleSource
13+ from .types import SingleNext , NextNode , ErrorEntry , SingleErrorSource , Entries , BranchContainer , SingleSource , Source , Types , ResolvedNext
1414from .hooks import GraphHook
1515from .branches import Branch
1616
@@ -92,14 +92,26 @@ def __init__(self,
9292 self .index_branches ()
9393
9494 def index_branches (self ) -> None :
95- for branch in self .edges :
95+ for branch_container in self .edges :
9696
97- source = branch [ 0 ] [0 ][0 ]
97+ source = branch_container [0 ][0 ]
9898
99- start_nodes = [ source ] if isinstance ( source , ( Node , type )) else source
99+ if Types [ T , S ]. is_single_source ( source ):
100100
101- for start_node in start_nodes :
102- self .branch_registry [start_node ].append (Branch [T , S ](branch [0 ], branch [1 ]))
101+ branch = Branch [T , S ](branch_container [:- 1 ], source , branch_container [- 1 ])
102+ self .branch_registry [source ].append (branch )
103+
104+ elif Types [T , S ].is_single_source_sequence (source ):
105+
106+ for start_node in source :
107+
108+ branch = Branch [T , S ](branch_container [:- 1 ], start_node , branch_container [- 1 ])
109+ self .branch_registry [start_node ].append (branch )
110+
111+ else :
112+ raise ValueError (f"Invalid source type: { source } " )
113+
114+ print (self .branch_registry )
103115
104116
105117 async def __call__ (self , state : T , shared : S ) -> tuple [T , S ]:
@@ -157,7 +169,9 @@ async def run_branch(self, state: T, shared: S, branch: Branch[T, S]) -> None:
157169
158170 try :
159171
160- next_nodes : list [NextNode [T , S ]] = await self .get_next (state , shared , START , branch )
172+ next_nodes : list [NextNode [T , S ]] = await self .get_next (state , shared , branch .start , branch )
173+
174+ print ("INITIAL NEXT:" , next_nodes )
161175
162176
163177 while next_nodes :
@@ -197,7 +211,7 @@ async def run_branch(self, state: T, shared: S, branch: Branch[T, S]) -> None:
197211
198212 else :
199213
200- next_nodes = await self .get_next (state , shared , next_nodes , branch )
214+ next_nodes = await self .get_next (state , shared , [ n . node for n in next_nodes ] , branch )
201215
202216 finally :
203217
@@ -215,6 +229,8 @@ async def run_branch(self, state: T, shared: S, branch: Branch[T, S]) -> None:
215229
216230 if e :
217231 raise e
232+
233+ print (" --- BRANCH RESULT --- " )
218234
219235 branch .result .set_result (Diff .recursive_diff (initial_state .model_dump (), state .model_dump ()))
220236
@@ -333,7 +349,12 @@ async def spawn_branches(self, state: T, shared: S, next_nodes: list[NextNode[T,
333349
334350 for node in next_nodes :
335351 for branch in self .branch_registry [node .node ]:
352+
353+ for h in self .hooks : await h .on_spawn_branch_start (state , shared , branch , node , self .branch_registry , self .join_registry )
354+
336355 self .spawn_branch (state , shared , branch )
356+
357+ for h in self .hooks : await h .on_spawn_branch_end (state , shared , branch , node , self .branch_registry , self .join_registry )
337358
338359
339360 async def join_branches (self , state : T , next_nodes : list [NextNode [T , S ]]) -> T :
@@ -366,7 +387,7 @@ async def join_branches(self, state: T, next_nodes: list[NextNode[T, S]]) -> T:
366387
367388
368389
369- async def get_next (self , state : T , shared : S , current_nodes : Sequence [ NextNode [ T , S ]] | type [ START ], branch : Branch [T , S ]) -> list [NextNode [T , S ]]:
390+ async def get_next (self , state : T , shared : S , current_nodes : Source [ T , S ], branch : Branch [T , S ]) -> list [NextNode [T , S ]]:
370391 """
371392 Get the next nodes to run based on the current nodes and the graph's edges.
372393
@@ -381,22 +402,26 @@ async def get_next(self, state: T, shared: S, current_nodes: Sequence[NextNode[T
381402 The list of the next nodes including their edges that they were reached by.
382403 """
383404
405+ print (f"GET NEXT FOR CURRENT NODES: { current_nodes } " )
384406
385407 next_list : list [NextNode [T , S ]] = []
386408
387- if isinstance (current_nodes , type ): # START
388- next_list .extend (
389- await self .resolve_entries (state , shared , branch .edge_index [START ])
390- )
391-
392- else : # Regular nodes
409+ if Types [T , S ].is_single_source_sequence (current_nodes ):
410+ print ("IS SINGLE SOURCE SEQUENCE" )
393411 for current_node in current_nodes :
394-
395- # Find the edge corresponding to the current node
396412 next_list .extend (
397- await self .resolve_entries (state , shared , branch .edge_index [current_node . node ])
413+ await self .resolve_entries (state , shared , branch .edge_index [current_node ])
398414 )
399415
416+ elif Types [T , S ].is_single_source (current_nodes ):
417+ print ("IS SINGLE SOURCE" )
418+ next_list .extend (
419+ await self .resolve_entries (state , shared , branch .edge_index [current_nodes ])
420+ )
421+
422+ else :
423+ raise ValueError (f"Invalid current_nodes type: { type (current_nodes )} " )
424+
400425
401426 # Instant nodes
402427 current_instant_next_list : list [NextNode [T , S ]] = []
@@ -489,23 +514,16 @@ def match_error(self, e: Exception, source: SingleErrorSource[T, S], source_node
489514
490515 async def resolve_entries (self , state : T , shared : S , entries : Sequence [Entries [T , S ]]) -> list [NextNode [T , S ]]:
491516
492- next_nodes : list [ NextNode [ T , S ]] = []
517+ print ( f"RESOLVE: { entries } " )
493518
494- for entry in entries :
495- next_list = await self .resolve_entry (state , shared , entry )
519+ return [
520+ next_node
521+ for entry in entries
522+ for next_node in await self .resolve_entry (state , shared , entry )
523+ ]
496524
497- for x in next_list :
498525
499- match x :
500- case NextNode ():
501- next_nodes .append (x )
502- case Branch ():
503- self .spawn_branch (state , shared , x )
504-
505- return next_nodes
506-
507-
508- async def resolve_entry (self , state : T , shared : S , entry : Entries [T , S ]) -> list [NextNode [T , S ] | Branch [T , S ]]:
526+ async def resolve_entry (self , state : T , shared : S , entry : Entries [T , S ]) -> list [NextNode [T , S ]]:
509527 """
510528 Resolve the next to nodes.
511529
@@ -519,36 +537,60 @@ async def resolve_entry(self, state: T, shared: S, entry: Entries[T, S]) -> list
519537 The resolved nodes.
520538 """
521539
522- next_nodes : list [NextNode [T , S ] | Branch [T , S ]] = []
540+ print (f"RESOLVING: { entry } " )
541+
523542 next = entry .next
524543
525- match next :
526544
527- case None :
528- pass # END
529545
530- case type ():
531- assert next is END , "Only END is allowed as a type here"
532-
533- case Node ():
534- next_nodes .append (NextNode [T , S ](node = next , reached_by = entry ))
546+ if not Types [T , S ].is_resolved_next (next ):
547+ print ("CALLABLE" )
548+ next = cast (Callable [[T , S ], ResolvedNext [T , S ]], next )
549+ next = next (state , shared )
550+
551+ if inspect .isawaitable (next ):
552+ next = await next
553+
554+
555+ return [
556+ NextNode [T , S ](node = node , reached_by = entry )
557+ for node in self .get_next_nodes (next )
558+ ]
559+
560+
561+
562+ def get_next_nodes (self , next : ResolvedNext [T , S ]) -> list [Node [T , S ]]:
563+
564+ next_nodes : list [Node [T , S ]] = []
565+
566+ def match (x : SingleNext [T , S ]) -> None :
535567
536- case Sequence ():
537- for n in next :
538- if isinstance (n , Node ):
539- next_nodes .append (NextNode [T , S ](node = n , reached_by = entry ))
568+ match x :
540569
570+ case None :
571+ print ("None" )
572+ pass # END
541573
542- case _: # callable
543- next = next
544- res = next (state , shared )
545- if inspect .isawaitable (res ):
546- res = await res # for awaitables
574+ case type ():
575+ print ("END" )
576+ assert next is END , "Only END is allowed as a type here"
547577
548- if isinstance (res , Node ):
549- next_nodes .append (NextNode [T , S ](node = res , reached_by = entry ))
578+ case Node ():
579+ print ("Node" )
580+ next_nodes .append (x )
550581
551582
583+ if Types [T , S ].is_single_next_sequence (next ):
584+ print ("Sequence" )
585+ for x in next :
586+ match (x )
587+
588+ elif Types [T , S ].is_single_next (next ):
589+ print ("Single" )
590+ match (next )
591+
552592 return next_nodes
593+
594+
553595
554596
0 commit comments