Skip to main content

LangChain connector for Noxy human-in-the-loop — interrupt/resume with relay outcome polling via noxy-sdk.

Project description

Noxy LangChain Connector

LangChain connector for Noxy human-in-the-loop guardrails. Uses HumanInTheLoopMiddleware to pause agent tool calls, routes encrypted approval prompts to all devices registered for the identity (web, iOS, Android, Telegram), and resumes execution when you poll relay for the settled outcome via noxy-sdk (GetDecisionOutcome).

Installing langchain-noxy pulls in noxy-sdk automatically; you do not need a separate checkout of the Noxy SDK.

Flow

sequenceDiagram
    participant A as LangChain Agent
    participant SDK as noxy-sdk
    participant N as Noxy Relay
    participant D as User Devices

    A->>SDK: send_decision (tool approval)
    SDK->>N: RouteDecision
    N->>D: Deliver to registered devices
    A->>A: interrupt() — state saved to checkpointer
    Note over A: Agent suspended

    loop Poll GetDecisionOutcome
        SDK->>N: get_decision_outcome
        N-->>SDK: pending / approved / rejected / expired
    end

    SDK->>A: Command(resume=HITLResponse)
    A->>A: Continue with approved/rejected tool calls
  1. The agent proposes a guarded tool call.
  2. NoxyHumanInTheLoopMiddleware routes an encrypted actionable to all devices for the identity.
  3. LangChain calls interrupt() — the agent suspends and persists state via a checkpointer.
  4. User responds on any registered device or the decision TTL expires on relay.
  5. Your process calls wait_for_decision_outcome (SDK) or bridge.wait_and_resume(...).
  6. The agent continues with approved or rejected tool calls.

Relay delivers outcomes via gRPC polling (GetDecisionOutcome), with exponential backoff in the SDK.

Requirements

  • Python >= 3.10
  • A LangChain agent created with create_agent and a checkpointer
  • A Noxy app token and target identity (phone, email, user id, or wallet 0x…)

Installation

pip install langchain-noxy

Optional FastAPI example server:

pip install "langchain-noxy[examples]"

Configuration

Variable Required Default Description
NOXY_APP_TOKEN Yes App token from the Noxy dashboard
NOXY_IDENTITY_ID Yes* Target identity: phone, email, user id, or wallet
NOXY_ENDPOINT No https://relay.noxy.network Relay gRPC endpoint
MODEL No openai:gpt-4o-mini Chat model for the poll_resume_server example

*Identity is passed to NoxyLangChainBridge(client, identity_id) in code; use the env var when your app reads it from the environment.

Copy .env.example to .env when running repository examples locally (never commit real tokens).

import os
from noxy import NoxyConfig, init_noxy_agent_client

client = init_noxy_agent_client(
    NoxyConfig(
        endpoint=os.environ.get("NOXY_ENDPOINT", "https://relay.noxy.network"),
        auth_token=os.environ["NOXY_APP_TOKEN"],
        decision_ttl_seconds=3600,
    )
)

Quick start

import uuid

from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain.tools import tool
from langgraph.checkpoint.memory import InMemorySaver
from noxy import NoxyConfig, init_noxy_agent_client

from langchain_noxy import NoxyLangChainBridge

@tool
def transfer_funds(to: str, amount: str) -> str:
    """Transfer funds."""
    return f"Sent {amount} to {to}"

client = init_noxy_agent_client(
    NoxyConfig(
        endpoint="https://relay.noxy.network",
        auth_token="your-app-token",
        decision_ttl_seconds=3600,
    )
)
bridge = NoxyLangChainBridge(client, "user@example.com")

agent = create_agent(
    init_chat_model("openai:gpt-4o-mini"),
    tools=[transfer_funds],
    middleware=[bridge.create_hitl_middleware({"transfer_funds": True})],
    checkpointer=InMemorySaver(),
)

config = {"configurable": {"thread_id": str(uuid.uuid4())}}
# agent.invoke(...) suspends when transfer_funds is proposed; read decision_id from interrupt

final = bridge.wait_and_resume(agent, "<decision_id>")

Manual polling

from noxy.decision_outcome import WaitForDecisionOutcomeOptions

resume_handler = bridge.create_resume_handler(agent)
response = client.wait_for_decision_outcome(
    WaitForDecisionOutcomeOptions(decision_id="<decision_id>", identity_id="user@example.com")
)
final = resume_handler.resume_from_poll_response(
    response, decision_id="<decision_id>", identity_id="user@example.com"
)

Outcome mapping

Noxy relay outcomes map to LangChain HITL decisions (via hitl_response_from_outcome):

  • approved{"type": "approve"}
  • rejected / expired / timeout{"type": "reject", "message": "..."}

Poll tuning

Pass WaitForDecisionOutcomeOptions to bridge.wait_and_resume (same fields as noxy-sdk):

Field Default Description
initial_poll_interval_ms 400 First delay between polls
max_poll_interval_ms 30000 Cap between polls
max_wait_ms 900000 Stop polling and resume with timeout outcome
backoff_multiplier 1.6 Exponential backoff factor

API

Symbol Description
NoxyLangChainBridge Wires client, registry, middleware factory, and wait_and_resume
NoxyHumanInTheLoopMiddleware LangChain middleware that routes tool approvals to Noxy
NoxyAgentResumeHandler.wait_and_resume SDK poll loop + Command(resume=...)
NoxyAgentResumeHandler.resume_from_poll_response Resume from one terminal poll
PendingDecisionRegistry Maps decision_idthread_id for resume
build_hitl_actionable(...) Build actionable payload from HITL action requests
hitl_response_from_outcome(...) Map Noxy outcome to LangChain HITL resume value
parse_webhook_payload(...) Optional: parse webhook-shaped JSON if you bridge events yourself

Examples

Example scripts are maintained in the GitHub repository (they are not shipped inside the PyPI wheel). Clone the repo to run them:

git clone https://github.com/noxy-network/langchain-connector.git
cd langchain-connector
pip install ".[examples]"
cp .env.example .env   # set NOXY_APP_TOKEN and NOXY_IDENTITY_ID
  • examples/basic.py — mock client, no relay required
  • examples/poll_resume_server.py — FastAPI: POST /runs, then POST /runs/wait
python examples/basic.py

export NOXY_APP_TOKEN="your-app-token"
export NOXY_IDENTITY_ID="user@example.com"
uvicorn examples.poll_resume_server:app --reload

Development

For contributors working on this repository:

git clone https://github.com/noxy-network/langchain-connector.git
cd langchain-connector
pip install ".[dev,examples]"
make test
make build
make publish-check

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

langchain_noxy-1.0.2.tar.gz (16.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

langchain_noxy-1.0.2-py3-none-any.whl (14.0 kB view details)

Uploaded Python 3

File details

Details for the file langchain_noxy-1.0.2.tar.gz.

File metadata

  • Download URL: langchain_noxy-1.0.2.tar.gz
  • Upload date:
  • Size: 16.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for langchain_noxy-1.0.2.tar.gz
Algorithm Hash digest
SHA256 a8487b92f86d2163ba2b68c96142432fe94591f50d5cd17c525f52eaf9901182
MD5 2365fd312e10f07a7d9dc14070d0494c
BLAKE2b-256 6cc6f6ca3b2137305893521f52e8e1ea3275722f6ae76a2aacc4ce46fc53b1fe

See more details on using hashes here.

File details

Details for the file langchain_noxy-1.0.2-py3-none-any.whl.

File metadata

  • Download URL: langchain_noxy-1.0.2-py3-none-any.whl
  • Upload date:
  • Size: 14.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for langchain_noxy-1.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 8eacada7aa69523ac507d28b810f49e2a0c83b0ae8331dcd57bcc09c58d08ddc
MD5 01fe1c1801cd58a0f557394871e758da
BLAKE2b-256 d4eadc0755a80a8f70f7b1a4aa0998f14fe89551fe1320506ca62635020357aa

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page