LangGraph connector for Noxy human-in-the-loop decisions via interrupt/resume and webhooks.
This project has been archived.
The maintainers of this project have marked this project as archived. No new releases are expected.
Project description
Noxy LangGraph Connector
LangGraph connector for Noxy human-in-the-loop guardrails. Pauses agent graphs with interrupt(), delivers encrypted approval prompts to user devices, and resumes execution when Noxy fires a webhook.
Flow
sequenceDiagram
participant G as LangGraph
participant N as Noxy Relay
participant P as User Phone
participant S as Your Server
G->>N: send_decision (push payload)
N->>P: Push notification
G->>G: interrupt() — state saved to checkpointer
Note over G: Graph suspended
alt User responds
P->>N: Approve / Reject
N->>S: Webhook (outcome)
S->>G: Command(resume=decision)
G->>G: Continue with decision in state
else Timeout
N->>S: Webhook (timeout / expired)
S->>G: Command(resume=timeout)
G->>G: Continue with default behaviour
end
- Graph reaches the HITL node.
- Noxy routes an encrypted actionable to the user's device (push).
- The node calls
interrupt()— LangGraph suspends and persists state via a checkpointer. - User responds on mobile or the decision TTL expires.
- Noxy fires a webhook to your server.
- Your server calls
NoxyGraphResumeHandler.resume_from_webhook()→Command(resume=...). - The graph continues with the human decision (or timeout default) in state.
Requirements
- Python >= 3.10
- A LangGraph graph compiled with a checkpointer (required for
interrupt()) - noxy-sdk credentials (
NOXY_APP_TOKEN, target identity)
Target identity can be a phone number, email, user id, or wallet address (0x…).
Installation
pip install noxy-langgraph
For the FastAPI webhook example:
pip install "noxy-langgraph[examples]"
Local development against the monorepo SDK:
pip install -e ../../sdks/python-sdk
pip install -e ".[dev,examples]"
Quick start
import uuid
from typing import Optional, TypedDict
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.constants import END, START
from langgraph.graph import StateGraph
from noxy import NoxyConfig, init_noxy_agent_client
from noxy_langgraph import NoxyLangGraphBridge, build_tool_call_actionable
class State(TypedDict, total=False):
task: str
noxy_decision: Optional[dict]
_noxy_sent_decision_id: Optional[str]
def build_actionable(state: State) -> dict:
return build_tool_call_actionable(
tool="run_task",
args={"task": state["task"]},
title="Approve task?",
summary=state["task"],
)
client = init_noxy_agent_client(
NoxyConfig(
endpoint="https://relay.noxy.network",
auth_token="your-app-token",
decision_ttl_seconds=3600,
)
)
# Phone, email, user id, or wallet address
bridge = NoxyLangGraphBridge(client, "user@example.com")
builder = StateGraph(State)
builder.add_node("noxy_hitl", bridge.create_hitl_node(build_actionable))
builder.add_edge(START, "noxy_hitl")
builder.add_edge("noxy_hitl", END)
graph = builder.compile(checkpointer=InMemorySaver())
resume_handler = bridge.create_resume_handler(graph)
config = {"configurable": {"thread_id": str(uuid.uuid4())}}
paused = graph.invoke({"task": "Send 1 wei"}, config)
# paused["__interrupt__"] contains the pending decision_id
# Later, in your webhook handler:
final = resume_handler.resume_from_webhook({
"decisionId": "<decisionId>",
"identityId": "user@example.com",
"outcome": "approved", # or "rejected", "expired"
})
Graph state
Include optional _noxy_sent_decision_id in your state schema. The resume handler sets it via Command(update=...) so the HITL node does not re-send the push when LangGraph re-executes the node after resume (LangGraph always re-runs the node body from the top).
from noxy_langgraph import NOXY_SENT_DECISION_ID_KEY
class State(TypedDict, total=False):
...
_noxy_sent_decision_id: Optional[str] # or use NOXY_SENT_DECISION_ID_KEY
Webhook payload
Noxy delivers JSON to your registered webhook URL:
| Field | Type | Description |
|---|---|---|
decisionId |
str |
Decision to resume (snake_case decision_id also accepted) |
identityId |
str |
Identity that took the decision (phone, email, user id, or wallet) |
outcome |
str |
approved, rejected, expired, or timeout |
receivedAt |
str |
Optional ISO timestamp |
On timeout/expired, pass an on_timeout callback to create_hitl_node to apply default behaviour:
def on_timeout(state, resume):
return {"noxy_decision": resume.to_state(), "approved": False}
bridge.create_hitl_node(build_actionable, on_timeout=on_timeout)
API
| Symbol | Description |
|---|---|
NoxyLangGraphBridge |
Wires client, registry, HITL node factory, and resume handler |
create_noxy_hitl_node(...) |
Low-level HITL node factory |
NoxyGraphResumeHandler |
Resume paused graphs from webhook payloads |
PendingInterruptRegistry |
Maps decision_id → thread_id for resume |
build_tool_call_actionable(...) |
Standard propose_tool_call payload builder |
parse_webhook_payload(...) |
Parse raw webhook JSON |
Examples
examples/basic.py— end-to-end demo with a mock Noxy clientexamples/webhook_server.py— FastAPI server with/runsand/webhooks/noxy
python examples/basic.py
Configure the webhook server:
export NOXY_APP_TOKEN="your-app-token"
export NOXY_IDENTITY_ID="user@example.com" # or phone, user id, 0x…
uvicorn examples.webhook_server:app --reload
Development
make dev # editable install with dev + examples extras
make test # run pytest
make build # build sdist + wheel
make publish-check # build and validate with twine
Publishing
- Bump version in
pyproject.tomlandnoxy_langgraph/_version.py. - Update
CHANGELOG.md. - Merge to
main— the GitHub Actions publish workflow uploads to PyPI whenPYPI_API_TOKENis configured.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file noxy_langgraph-1.0.0.tar.gz.
File metadata
- Download URL: noxy_langgraph-1.0.0.tar.gz
- Upload date:
- Size: 13.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
316c480cec7f4f2079dd827447e9f03c93616bed621af0ec811a8ac8760c4b1e
|
|
| MD5 |
c92468c3ad46b5abe664d8c18343afb6
|
|
| BLAKE2b-256 |
b25cdebd91c6044946809f045f0be626bdc1bf8df296e6237ad1f7c6e6354a68
|
File details
Details for the file noxy_langgraph-1.0.0-py3-none-any.whl.
File metadata
- Download URL: noxy_langgraph-1.0.0-py3-none-any.whl
- Upload date:
- Size: 11.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8884084d55e285303f026c18b787f64a29ecacf87fab10dd25de90aadfa3b140
|
|
| MD5 |
0d5b9797b3e28af9f85a4d4a40a54000
|
|
| BLAKE2b-256 |
0f53a137c5c4d4cc4220cbc2638eee902dddd2458fab425d4178ff3c066dd9c4
|