Metadata-Version: 2.4
Name: noxy-langchain
Version: 1.0.0
Summary: LangChain connector for Noxy human-in-the-loop — routes to all registered devices via agent middleware and webhooks.
Author: Noxy Network
License-Expression: MIT
Project-URL: Homepage, https://noxy.network
Project-URL: Documentation, https://noxy.network/docs.html
Project-URL: Repository, https://github.com/noxy-network/langchain-connector
Project-URL: Issues, https://github.com/noxy-network/langchain-connector/issues
Keywords: noxy,noxy-network,langchain,human-in-the-loop,ai-agents,middleware,webhook
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: noxy-sdk>=2.1.0
Requires-Dist: langchain>=0.3.0
Requires-Dist: langgraph>=0.2.0
Requires-Dist: langgraph-checkpoint>=2.0.0
Provides-Extra: examples
Requires-Dist: fastapi>=0.110.0; extra == "examples"
Requires-Dist: uvicorn>=0.27.0; extra == "examples"
Provides-Extra: dev
Requires-Dist: build>=1.0.0; extra == "dev"
Requires-Dist: langchain-core>=0.3.0; extra == "dev"
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
Requires-Dist: twine>=5.0.0; extra == "dev"
Dynamic: license-file

# Noxy LangChain Connector

LangChain connector for [Noxy](https://noxy.network) **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 Noxy fires a webhook.

## Flow

```mermaid
sequenceDiagram
    participant A as LangChain Agent
    participant N as Noxy Relay
    participant D as User Devices
    participant S as Your Server

    A->>N: send_decision (tool approval)
    N->>D: Route to registered devices (web, iOS, Android, Telegram)
    A->>A: interrupt() — state saved to checkpointer
    Note over A: Agent suspended

    alt User responds
        D->>N: Approve / Reject
        N->>S: Webhook (outcome)
        S->>A: Command(resume=HITLResponse)
        A->>A: Continue with approved/rejected tool calls
    else Timeout
        N->>S: Webhook (timeout / expired)
        S->>A: Command(resume=reject)
        A->>A: Continue with default behaviour
    end
```

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.
5. Noxy fires a webhook to your server.
6. Your server calls `NoxyAgentResumeHandler.resume_from_webhook()` → `Command(resume=...)`.
7. The agent continues with approved, edited, or rejected tool calls.

## Requirements

- Python **>= 3.10**
- A LangChain agent created with `create_agent` and a **checkpointer**
- [noxy-sdk](https://pypi.org/project/noxy-sdk/) credentials (`NOXY_APP_TOKEN`, target identity)

Target identity can be a **phone number**, **email**, **user id**, or **wallet address** (`0x…`).

## Installation

```bash
pip install noxy-langchain
```

For the FastAPI webhook example:

```bash
pip install "noxy-langchain[examples]"
```

Local development against the monorepo SDK:

```bash
pip install -e ../../sdks/python-sdk
pip install -e ".[dev,examples]"
```

## Quick start

```python
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 noxy_langchain 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(),
)
resume_handler = bridge.create_resume_handler(agent)

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

# Later, in your webhook handler:
final = resume_handler.resume_from_webhook({
    "decisionId": "<decisionId>",
    "identityId": "user@example.com",
    "outcome": "approved",  # or "rejected", "expired"
})
```

## Webhook payload

| 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 |

Noxy outcomes map to LangChain HITL decisions:

- `approved` → `{"type": "approve"}`
- `rejected` / `expired` / `timeout` → `{"type": "reject", "message": "..."}`

## API

| Symbol | Description |
|--------|-------------|
| `NoxyLangChainBridge` | Wires client, registry, middleware factory, and resume handler |
| `NoxyHumanInTheLoopMiddleware` | LangChain middleware that routes tool approvals to Noxy |
| `NoxyAgentResumeHandler` | Resume paused agents from webhook payloads |
| `PendingDecisionRegistry` | Maps `decision_id` → `thread_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(...)` | Parse raw webhook JSON |

## Examples

- `examples/basic.py` — middleware + webhook resume demo with a mock Noxy client
- `examples/webhook_server.py` — FastAPI server with `/runs` and `/webhooks/noxy`

```bash
python examples/basic.py
```

## Development

```bash
make dev
make test
make build
```

## License

MIT
