fix(adapters): replace NodeInterrupt with FirewallBlocked exception - #61
fix(adapters): replace NodeInterrupt with FirewallBlocked exception#61Pranjal0410 wants to merge 3 commits into
Conversation
NodeInterrupt is deprecated in LangGraph V1 and silently fails — returns a dict instead of raising. BLOCK decisions were turning into pauses. FirewallBlocked(FirewallError) actually propagates so callers can catch it. 22/22 adapter tests, full suite 110/110.
Two runnable examples showing the adapter in action: prompt_guard.py (BLOCK halts the graph via FirewallBlocked) and context_filter.py (per-chunk RAG filtering, poisoned chunk dropped, clean ones pass through).
|
Few things to be fixed: FirewallBlocked is not exported from the top-level acf package. The fix is two lines in Two minor things also worth noting: langgraph_prompt_guard.py has duplicate imports, from acf import Firewall and from acf.adapters.langgraph import FirewallNode each appear twice (lines 20–21 and 25–26). |
Added FirewallBlocked to acf/__init__.py exports. Removed duplicate imports in langgraph_prompt_guard.py. Reported by @tharindupr.
|
both fixed @tharindupr. FirewallBlocked added to init.py exports and all. duplicate imports in the example cleaned up. 110/110 passing. |
NodeInterrupt is deprecated in LangGraph V1. Worse - it doesn't actually raise. It returns
{'__interrupt__': [...]}in the result dict and the graph keeps going. So the adapter's BLOCK decisions were silently becoming pauses. The attack text stayed in state.Found this while writing examples for the adapter after PR #51 merged.
What changed
sdk/python/acf/models.pyFirewallBlocked(FirewallError)- a proper exception that propagates out ofinvoke(). Callers catch it with normal try/except.sdk/python/acf/adapters/langgraph.pyNodeInterruptwithFirewallBlockedon BLOCK decisions. No more silent failures.sdk/python/tests/adapters/test_langgraph.pyWhy this matters
A firewall that reports BLOCK but doesn't actually stop the request is worse than no firewall. At least without a firewall you know you're unprotected. With the old behaviour you think you're safe and you're not.
Examples
Two runnable examples in
examples/adapters/:langgraph_prompt_guard.py- clean prompt reaches the model, jailbreak gets stopped at the guard via FirewallBlockedlanggraph_context_filter.py- 3 RAG chunks retrieved, one poisoned. Poisoned chunk filtered out, model runs on the clean two.