def _get_start_nodes(self) -> List[Union[TaskNode, DecisionFunc, DecisionLLM]]:
        """
        Gets the starting nodes of the graph (those with no predecessors).
        
        Returns:
            List of start nodes
        """
        # Get all IDs that appear as targets in the edges dictionary
        all_target_ids = {target_id for targets in self.edges.values() for target_id in targets}
        
        # Also get all IDs that appear as targets in TaskNode.next_nodes
        for n in self.nodes:
            if isinstance(n, TaskNode):
                for next_node in n.next_nodes:
                    all_target_ids.add(next_node.id)
        
        # Return nodes that don't appear as targets
        return [node for node in self.nodes if node.id not in all_target_ids]

        Gets the starting nodes of the graph (those with no predecessors).
        
        Returns:
            List of start nodes
        
src/upsonic/client/graph.py