Source code in pydantic_graph/pydantic_graph/nodes.py
27282930313233343536
@dataclassclassGraphRunContext(Generic[StateT,DepsT]):"""Context for a graph."""# TODO: Can we get rid of this struct and just pass both these things around..?state:StateT"""The state of the graph."""deps:DepsT"""Dependencies for the graph."""
classBaseNode(ABC,Generic[StateT,DepsT,NodeRunEndT]):"""Base class for a node."""docstring_notes:ClassVar[bool]=False"""Set to `True` to generate mermaid diagram notes from the class's docstring. While this can add valuable information to the diagram, it can make diagrams harder to view, hence it is disabled by default. You can also customise notes overriding the [`get_note`][pydantic_graph.nodes.BaseNode.get_note] method. """@abstractmethodasyncdefrun(self,ctx:GraphRunContext[StateT,DepsT])->BaseNode[StateT,DepsT,Any]|End[NodeRunEndT]:"""Run the node. This is an abstract method that must be implemented by subclasses. !!! note "Return types used at runtime" The return type of this method are read by `pydantic_graph` at runtime and used to define which nodes can be called next in the graph. This is displayed in [mermaid diagrams](mermaid.md) and enforced when running the graph. Args: ctx: The graph context. Returns: The next node to run or [`End`][pydantic_graph.nodes.End] to signal the end of the graph. """...defget_snapshot_id(self)->str:ifsnapshot_id:=getattr(self,'__snapshot_id',None):returnsnapshot_idelse:self.__dict__['__snapshot_id']=snapshot_id=generate_snapshot_id(self.get_node_id())returnsnapshot_iddefset_snapshot_id(self,snapshot_id:str)->None:self.__dict__['__snapshot_id']=snapshot_id@classmethod@cachedefget_node_id(cls)->str:"""Get the ID of the node."""returncls.__name__@classmethoddefget_note(cls)->str|None:"""Get a note about the node to render on mermaid charts. By default, this returns a note only if [`docstring_notes`][pydantic_graph.nodes.BaseNode.docstring_notes] is `True`. You can override this method to customise the node notes. """ifnotcls.docstring_notes:returnNonedocstring=cls.__doc__# dataclasses get an automatic docstring which is just their signature, we don't want thatifdocstringandis_dataclass(cls)anddocstring.startswith(f'{cls.__name__}('):docstring=None# pragma: no coverifdocstring:# pragma: no branch# remove indentation from docstringimportinspectdocstring=inspect.cleandoc(docstring)returndocstring@classmethoddefget_node_def(cls,local_ns:dict[str,Any]|None)->NodeDef[StateT,DepsT,NodeRunEndT]:"""Get the node definition."""type_hints=get_type_hints(cls.run,localns=local_ns,include_extras=True)try:return_hint=type_hints['return']exceptKeyErrorase:raiseexceptions.GraphSetupError(f'Node {cls} is missing a return type hint on its `run` method')fromenext_node_edges:dict[str,Edge]={}end_edge:Edge|None=Nonereturns_base_node:bool=Falseforreturn_typein_utils.get_union_args(return_hint):return_type,annotations=_utils.unpack_annotated(return_type)edge=next((aforainannotationsifisinstance(a,Edge)),Edge(None))return_type_origin=get_origin(return_type)orreturn_typeifreturn_type_originisEnd:end_edge=edgeelifreturn_type_originisBaseNode:# TODO: Should we disallow this?returns_base_node=Trueelifissubclass(return_type_origin,BaseNode):next_node_edges[return_type.get_node_id()]=edgeelse:raiseexceptions.GraphSetupError(f'Invalid return type: {return_type}')returnNodeDef(cls,cls.get_node_id(),cls.get_note(),next_node_edges,end_edge,returns_base_node,)defdeep_copy(self)->Self:"""Returns a deep copy of the node."""returncopy.deepcopy(self)
Set to True to generate mermaid diagram notes from the class's docstring.
While this can add valuable information to the diagram, it can make diagrams harder to view, hence
it is disabled by default. You can also customise notes overriding the
get_note method.
This is an abstract method that must be implemented by subclasses.
Return types used at runtime
The return type of this method are read by pydantic_graph at runtime and used to define which
nodes can be called next in the graph. This is displayed in mermaid diagrams
and enforced when running the graph.
The next node to run or End to signal the end of the graph.
Source code in pydantic_graph/pydantic_graph/nodes.py
505152535455565758596061626364656667
@abstractmethodasyncdefrun(self,ctx:GraphRunContext[StateT,DepsT])->BaseNode[StateT,DepsT,Any]|End[NodeRunEndT]:"""Run the node. This is an abstract method that must be implemented by subclasses. !!! note "Return types used at runtime" The return type of this method are read by `pydantic_graph` at runtime and used to define which nodes can be called next in the graph. This is displayed in [mermaid diagrams](mermaid.md) and enforced when running the graph. Args: ctx: The graph context. Returns: The next node to run or [`End`][pydantic_graph.nodes.End] to signal the end of the graph. """...
@classmethoddefget_note(cls)->str|None:"""Get a note about the node to render on mermaid charts. By default, this returns a note only if [`docstring_notes`][pydantic_graph.nodes.BaseNode.docstring_notes] is `True`. You can override this method to customise the node notes. """ifnotcls.docstring_notes:returnNonedocstring=cls.__doc__# dataclasses get an automatic docstring which is just their signature, we don't want thatifdocstringandis_dataclass(cls)anddocstring.startswith(f'{cls.__name__}('):docstring=None# pragma: no coverifdocstring:# pragma: no branch# remove indentation from docstringimportinspectdocstring=inspect.cleandoc(docstring)returndocstring
@classmethoddefget_node_def(cls,local_ns:dict[str,Any]|None)->NodeDef[StateT,DepsT,NodeRunEndT]:"""Get the node definition."""type_hints=get_type_hints(cls.run,localns=local_ns,include_extras=True)try:return_hint=type_hints['return']exceptKeyErrorase:raiseexceptions.GraphSetupError(f'Node {cls} is missing a return type hint on its `run` method')fromenext_node_edges:dict[str,Edge]={}end_edge:Edge|None=Nonereturns_base_node:bool=Falseforreturn_typein_utils.get_union_args(return_hint):return_type,annotations=_utils.unpack_annotated(return_type)edge=next((aforainannotationsifisinstance(a,Edge)),Edge(None))return_type_origin=get_origin(return_type)orreturn_typeifreturn_type_originisEnd:end_edge=edgeelifreturn_type_originisBaseNode:# TODO: Should we disallow this?returns_base_node=Trueelifissubclass(return_type_origin,BaseNode):next_node_edges[return_type.get_node_id()]=edgeelse:raiseexceptions.GraphSetupError(f'Invalid return type: {return_type}')returnNodeDef(cls,cls.get_node_id(),cls.get_note(),next_node_edges,end_edge,returns_base_node,)
@dataclassclassEnd(Generic[RunEndT]):"""Type to return from a node to signal the end of the graph."""data:RunEndT"""Data to return from the graph."""defdeep_copy_data(self)->End[RunEndT]:"""Returns a deep copy of the end of the run."""ifself.dataisNone:returnselfelse:end=End(copy.deepcopy(self.data))end.set_snapshot_id(self.get_snapshot_id())returnenddefget_snapshot_id(self)->str:ifsnapshot_id:=getattr(self,'__snapshot_id',None):returnsnapshot_idelse:self.__dict__['__snapshot_id']=snapshot_id=generate_snapshot_id('end')returnsnapshot_iddefset_snapshot_id(self,set_id:str)->None:self.__dict__['__snapshot_id']=set_id
Source code in pydantic_graph/pydantic_graph/nodes.py
152153154155156157158159
defdeep_copy_data(self)->End[RunEndT]:"""Returns a deep copy of the end of the run."""ifself.dataisNone:returnselfelse:end=End(copy.deepcopy(self.data))end.set_snapshot_id(self.get_snapshot_id())returnend
Edgedataclass
Annotation to apply a label to an edge in a graph.
Source code in pydantic_graph/pydantic_graph/nodes.py
177178179180181182
@dataclassclassEdge:"""Annotation to apply a label to an edge in a graph."""label:str|None"""Label for the edge."""