2121 InputRequiredEvent ,
2222 StopEvent ,
2323)
24- from workflows .utils import (
25- get_steps_from_class ,
26- get_steps_from_instance ,
27- )
2824
2925
3026def get_entrypoints_schema (workflow : Workflow ) -> dict [str , Any ]:
@@ -128,30 +124,24 @@ def get_workflow_schema(workflow: Workflow) -> UiPathRuntimeGraph:
128124 nodes : list [UiPathRuntimeNode ] = []
129125 edges : list [UiPathRuntimeEdge ] = []
130126
131- # Add __start__ node
132127 nodes .append (
133128 UiPathRuntimeNode (
134129 id = "__start__" ,
135130 name = "__start__" ,
136131 type = "__start__" ,
137- metadata = {},
138132 subgraph = None ,
139133 )
140134 )
141135
142- # Get all steps from the workflow
143- steps = get_steps_from_class (workflow )
144- if not steps :
145- # If no steps are defined in the class, try to get them from the instance
146- steps = get_steps_from_instance (workflow )
136+ steps = workflow ._get_steps ()
147137
148138 # Track if we need external step for human interaction
149139 has_human_interaction = False
150140 current_stop_event : type | None = None
151141
152142 # First pass: find the StopEvent used in this workflow and check for human interaction
153- for _ , step_func in steps .items ():
154- step_config : StepConfig | None = getattr ( step_func , "__step_config" , None )
143+ for name , step_func in steps .items ():
144+ step_config : StepConfig | None = get_step_config ( name , step_func )
155145 if step_config is None :
156146 continue
157147
@@ -167,7 +157,7 @@ def get_workflow_schema(workflow: Workflow) -> UiPathRuntimeGraph:
167157
168158 # Create step nodes (all steps are type "node")
169159 for step_name , step_func in steps .items ():
170- step_config = getattr ( step_func , "__step_config" , None )
160+ step_config = get_step_config ( step_name , step_func )
171161 if step_config is None :
172162 continue
173163
@@ -199,17 +189,25 @@ def get_workflow_schema(workflow: Workflow) -> UiPathRuntimeGraph:
199189 id = "external_step" ,
200190 name = "external_step" ,
201191 type = "external" ,
202- metadata = {},
203192 subgraph = None ,
204193 )
205194 )
206195
196+ nodes .append (
197+ UiPathRuntimeNode (
198+ id = "__end__" ,
199+ name = "__end__" ,
200+ type = "__end__" ,
201+ subgraph = None ,
202+ )
203+ )
204+
207205 # Create edges based on event flow
208206 start_event_class = workflow ._start_event_class
209207 first_step_found = False
210208
211209 for step_name , step_func in steps .items ():
212- step_config = getattr ( step_func , "__step_config" , None )
210+ step_config = get_step_config ( step_name , step_func )
213211 if step_config is None :
214212 continue
215213
@@ -230,33 +228,34 @@ def get_workflow_schema(workflow: Workflow) -> UiPathRuntimeGraph:
230228 if return_type is type (None ):
231229 continue
232230
231+ # If this returns StopEvent, connect to __end__
232+ if issubclass (return_type , StopEvent ):
233+ if current_stop_event and return_type == current_stop_event :
234+ edges .append (
235+ UiPathRuntimeEdge (
236+ source = step_name ,
237+ target = "__end__" ,
238+ label = return_type .__name__ ,
239+ )
240+ )
241+ continue # Don't look for steps that accept StopEvent
242+
233243 # Find steps that accept this return type
234244 for target_step_name , target_step_func in steps .items ():
235- target_config : StepConfig | None = getattr (
236- target_step_func , "__step_config" , None
245+ target_config : StepConfig | None = get_step_config (
246+ target_step_name , target_step_func
237247 )
238248 if target_config is None :
239249 continue
240250
241251 if return_type in target_config .accepted_events :
242- # Special handling for StopEvent - only connect to the actual StopEvent being used
243- if issubclass (return_type , StopEvent ):
244- if current_stop_event and return_type == current_stop_event :
245- edges .append (
246- UiPathRuntimeEdge (
247- source = step_name ,
248- target = target_step_name ,
249- label = return_type .__name__ ,
250- )
251- )
252- else :
253- edges .append (
254- UiPathRuntimeEdge (
255- source = step_name ,
256- target = target_step_name ,
257- label = return_type .__name__ ,
258- )
252+ edges .append (
253+ UiPathRuntimeEdge (
254+ source = step_name ,
255+ target = target_step_name ,
256+ label = return_type .__name__ ,
259257 )
258+ )
260259
261260 # If this returns InputRequiredEvent, add edge to external_step
262261 if issubclass (return_type , InputRequiredEvent ):
@@ -282,6 +281,30 @@ def get_workflow_schema(workflow: Workflow) -> UiPathRuntimeGraph:
282281 return UiPathRuntimeGraph (nodes = nodes , edges = edges )
283282
284283
284+ def get_step_config (step_name : str , step_func : Any ) -> StepConfig | None :
285+ """
286+ Get the step configuration from a step function.
287+
288+ Returns None if:
289+ - The step name starts with underscore (internal method)
290+ - No step config is found
291+
292+ Args:
293+ step_name: Name of the step
294+ step_func: The step function
295+
296+ Returns:
297+ StepConfig if found and valid, None otherwise
298+ """
299+ # Skip internal methods
300+ if step_name .startswith ("_" ):
301+ return None
302+
303+ return getattr (step_func , "_step_config" , None ) or getattr (
304+ step_func , "__step_config" , None
305+ )
306+
307+
285308def _resolve_refs (
286309 schema : dict [str , Any ],
287310 root : dict [str , Any ] | None = None ,
0 commit comments