"""Utilities for detecting whether a callable uses `pause_for_human(...)` to pause execution.This wraps LangGraph's `interrupt(...)` signal and provides AST-based static analysis to detectif a function or callable object may yield control for human input."""importastimportinspectimporttextwrapfromcollections.abcimportCallablefromfunctoolsimportlru_cachefromtypingimportAny,TypeVarfromlanggraph.typesimportinterruptT=TypeVar("T")
[docs]defpause_for_human(payload:T)->T:"""Pause execution for human input and return a resume value of the same type. This is a wrapper around `langgraph.types.interrupt(...)` and should be used to signal an interruptible pause in a LangGraph node. Args: payload: The value to pass along with the interrupt signal. Returns: The same payload after human resumption. """returninterrupt(payload)
class_PauseCallVisitor(ast.NodeVisitor):"""AST visitor that detects calls to `pause_for_human(...)` within a function body."""def__init__(self)->None:self.found=Falsedefvisit_Call(self,node:ast.Call)->None:"""Visit each call node to see if it's a call to `pause_for_human`. Supports both direct usage (`pause_for_human(...)`) and attribute access (`some_module.pause_for_human(...)`). """fn=node.func# Direct call: pause_for_human(...)ifisinstance(fn,ast.Name)andfn.id=="pause_for_human":self.found=Truereturn# Attribute call: module.pause_for_human(...)ifisinstance(fn,ast.Attribute)andfn.attr=="pause_for_human":self.found=Truereturn# Continue walking if not already foundifnotself.found:super().generic_visit(node)
[docs]@lru_cache(maxsize=256)defuses_pause(fn:Callable[...,Any])->bool:"""Detect whether a function contains a call to `pause_for_human(...)`. Args: fn: A top-level function or method object. Returns: True if the function body invokes `pause_for_human(...)`. False if the source cannot be retrieved or does not contain such a call. """try:src=inspect.getsource(fn)except(OSError,TypeError):# No source available (e.g., built-ins or C-extensions)returnFalsesrc=textwrap.dedent(src)try:module=ast.parse(src)exceptSyntaxError:returnFalsevisitor=_PauseCallVisitor()visitor.visit(module)returnvisitor.found
[docs]defis_interruptible(obj:object)->bool:"""Determine whether an object will trigger an interrupt via `pause_for_human()`. Works for: - Plain functions and methods - Callable objects (e.g., classes with `__call__`) Args: obj: The object to test for interruptibility. Returns: True if the object contains or delegates to a function that uses `pause_for_human(...)`, False otherwise. """# Directly check plain functionsifcallable(obj)andnotcallable(obj)anduses_pause(obj):returnTrue# Check class __call__ methodcall_method=getattr(obj,"__call__",None)ifcallable(call_method):fn=getattr(obj.__class__,"__call__",None)iffnisnotNone:returnuses_pause(fn)returnFalse