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,7 +124,6 @@ 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__" ,
@@ -139,19 +134,15 @@ def get_workflow_schema(workflow: Workflow) -> UiPathRuntimeGraph:
139134 )
140135 )
141136
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 )
137+ steps = workflow ._get_steps ()
147138
148139 # Track if we need external step for human interaction
149140 has_human_interaction = False
150141 current_stop_event : type | None = None
151142
152143 # 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 )
144+ for name , step_func in steps .items ():
145+ step_config : StepConfig | None = _get_step_config ( name , step_func )
155146 if step_config is None :
156147 continue
157148
@@ -167,7 +158,7 @@ def get_workflow_schema(workflow: Workflow) -> UiPathRuntimeGraph:
167158
168159 # Create step nodes (all steps are type "node")
169160 for step_name , step_func in steps .items ():
170- step_config = getattr ( step_func , "__step_config" , None )
161+ step_config : StepConfig | None = _get_step_config ( step_name , step_func )
171162 if step_config is None :
172163 continue
173164
@@ -204,12 +195,22 @@ def get_workflow_schema(workflow: Workflow) -> UiPathRuntimeGraph:
204195 )
205196 )
206197
198+ nodes .append (
199+ UiPathRuntimeNode (
200+ id = "__end__" ,
201+ name = "__end__" ,
202+ type = "__end__" ,
203+ metadata = {},
204+ subgraph = None ,
205+ )
206+ )
207+
207208 # Create edges based on event flow
208209 start_event_class = workflow ._start_event_class
209210 first_step_found = False
210211
211212 for step_name , step_func in steps .items ():
212- step_config = getattr ( step_func , "__step_config" , None )
213+ step_config : StepConfig | None = _get_step_config ( step_name , step_func )
213214 if step_config is None :
214215 continue
215216
@@ -230,33 +231,32 @@ def get_workflow_schema(workflow: Workflow) -> UiPathRuntimeGraph:
230231 if return_type is type (None ):
231232 continue
232233
234+ # If this returns StopEvent, connect to __end__
235+ if issubclass (return_type , StopEvent ):
236+ if current_stop_event and return_type == current_stop_event :
237+ edges .append (
238+ UiPathRuntimeEdge (
239+ source = step_name ,
240+ target = "__end__" ,
241+ label = return_type .__name__ ,
242+ )
243+ )
244+ continue # Don't look for steps that accept StopEvent
245+
233246 # Find steps that accept this return type
234247 for target_step_name , target_step_func in steps .items ():
235- target_config : StepConfig | None = getattr (
236- target_step_func , "__step_config" , None
237- )
248+ target_config : StepConfig | None = _get_step_config (target_step_name , target_step_func )
238249 if target_config is None :
239250 continue
240251
241252 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- )
253+ edges .append (
254+ UiPathRuntimeEdge (
255+ source = step_name ,
256+ target = target_step_name ,
257+ label = return_type .__name__ ,
259258 )
259+ )
260260
261261 # If this returns InputRequiredEvent, add edge to external_step
262262 if issubclass (return_type , InputRequiredEvent ):
@@ -281,6 +281,26 @@ def get_workflow_schema(workflow: Workflow) -> UiPathRuntimeGraph:
281281
282282 return UiPathRuntimeGraph (nodes = nodes , edges = edges )
283283
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 (step_func , '__step_config' , None )
284304
285305def _resolve_refs (
286306 schema : dict [str , Any ],
0 commit comments