Metadata-Version: 2.4
Name: beav3r-sdk-langchain
Version: 0.1.0
Summary: LangChain middleware for routing sensitive tool calls through Beav3r approvals.
Author: Beav3r
License: MIT
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: beav3r-sdk>=0.1.0
Requires-Dist: langchain>=1.0.0
Requires-Dist: langgraph>=0.4.0

# beav3r-sdk-langchain

`beav3r-sdk-langchain` adds Beav3r approval enforcement to LangChain tool execution.

Use it when an agent can call tools that should be approved, denied, or delayed based on
Beav3r policy before the underlying tool actually runs.

## Requirements

- Python 3.10+
- `beav3r-sdk`
- `langchain`
- `langgraph`
- an LLM model provider package such as `langchain-openai`

## Install

### From PyPI

```bash
python3 -m pip install beav3r-sdk beav3r-sdk-langchain langchain-openai
```

### From source

Install the base SDK first, then this adapter:

```bash
cd ../beav3r-sdk-py
python3 -m pip install .

cd ../beav3r-sdk-langchain
python3 -m pip install .
python3 -m pip install langchain-openai
```

This integration is model-provider agnostic. It works with any LangChain-compatible LLM
provider as long as the agent can invoke tools through LangChain middleware. The examples in
this repository use `langchain-openai` only as one concrete provider adapter.

## Runtime requirements

From the agent process, you typically need:

- `BEAV3R_BASE_URL`
- `BEAV3R_API_KEY`
- credentials for the selected LLM model provider

For the included example script in this repository, that means:

- `XIAOMI_MIMO_API_KEY`
- optionally `XIAOMI_MIMO_BASE_URL`

You do not need signer device credentials in the LangChain app for normal request-time
approval checks. Device credentials are only needed for signer-side flows such as approval
submission or signed device reads.

On the Beav3r side you still need:

- a running Beav3r server
- a project API key with permission to request actions
- policy rules that match the `actionType` and attributes you send
- at least one paired signer when a policy path requires human approval

## Quick start

```python
from __future__ import annotations

import os

from beav3r_sdk import Beav3r
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI

from langchain_beav3r import Beav3rApprovalMiddleware, Beav3rToolConfig


def send_usdt(amount: int, recipient: str) -> str:
    """Send a USDT payment."""

    return f"Sent {amount} USDT to {recipient}"


client = Beav3r(
    base_url=os.environ["BEAV3R_BASE_URL"],
    agent_id="langchain_demo",
    api_key=os.environ["BEAV3R_API_KEY"],
)

model = ChatOpenAI(
    model=os.environ["MODEL_NAME"],
    api_key=os.environ["LLM_PROVIDER_API_KEY"],
    base_url=os.environ.get("LLM_PROVIDER_BASE_URL"),
    temperature=0,
)

agent = create_agent(
    model=model,
    tools=[send_usdt],
    middleware=[
        Beav3rApprovalMiddleware(
            client,
            tool_configs={
                "send_usdt": Beav3rToolConfig(action_type="payments.send_usdt"),
            },
        )
    ],
)

result = agent.invoke(
    {
        "messages": [
            {
                "role": "user",
                "content": "Send 25 USDT to 0x1111111111111111111111111111111111111111.",
            }
        ]
    }
)

print(result)
```

The snippet above uses `ChatOpenAI` as a generic LangChain chat-model adapter. The same
middleware pattern applies to other LangChain-compatible model providers.

## Behavior specification

`Beav3rApprovalMiddleware` intercepts each LangChain tool call before execution.

For each protected tool call it sends a Beav3r action request with:

- `actionType`
- `payload`
- `attributes`
- optional `actionId`

The default mapping is:

- `actionType`: the configured `Beav3rToolConfig.action_type`, otherwise the tool name, or
  `"{action_namespace}.{tool_name}"` if `action_namespace` is configured
- `payload`: the tool arguments
- `attributes`: `tool_name` plus scalar tool arguments

For example, this tool call:

```python
send_usdt(amount=25, recipient="0x1111...")
```

becomes this Beav3r request by default:

```python
{
    "actionType": "payments.send_usdt",
    "payload": {
        "amount": 25,
        "recipient": "0x1111..."
    },
    "attributes": {
        "tool_name": "send_usdt",
        "amount": 25,
        "recipient": "0x1111..."
    }
}
```

### Decision handling

- `approved` or `executed`: the original tool handler runs
- `denied`, `rejected`, `expired`, or any other non-approved final status: the middleware
  returns a `ToolMessage` explaining that the tool was blocked
- `pending`: the middleware raises `Beav3rApprovalPendingError`

## Configuration reference

`Beav3rApprovalMiddleware(...)` accepts:

- `client`: a configured `beav3r_sdk.Beav3r` client
- `tool_configs`: per-tool configuration map
- `protect_all_tools`: when `True`, every tool is protected unless explicitly disabled
- `action_namespace`: optional prefix used to derive default action types
- `poll_interval_ms`: polling interval passed to `guard_and_wait`
- `timeout_ms`: timeout passed to `guard_and_wait`

`Beav3rToolConfig(...)` accepts:

- `action_type`: explicit Beav3r action type
- `payload_builder`: callable that builds the Beav3r payload from the LangChain request
- `attributes_builder`: callable that builds the Beav3r attributes from the LangChain request
- `action_id_builder`: callable that supplies a custom Beav3r action id
- `poll_interval_ms`: per-tool polling override
- `timeout_ms`: per-tool timeout override

### Protecting specific tools

```python
middleware = Beav3rApprovalMiddleware(
    client,
    protect_all_tools=False,
    tool_configs={
        "send_usdt": Beav3rToolConfig(action_type="payments.send_usdt"),
        "search_docs": False,
    },
)
```

## Example agent

A runnable example lives in `examples/simple_agent.py`.

Run it after installing the base SDK, this package, and a LangChain model provider package.
The example script currently uses `langchain-openai` with Xiaomi MiMo-compatible settings:

```bash
export BEAV3R_BASE_URL=http://127.0.0.1:3000
export BEAV3R_API_KEY=bvr_test_...
export XIAOMI_MIMO_API_KEY=...
# Optional override if the provider endpoint differs:
# export XIAOMI_MIMO_BASE_URL=https://api.xiaomimimo.com/v1

python3 examples/simple_agent.py
```

If policy marks `payments.send_usdt` above a threshold as approval-gated, the middleware
will pause execution until Beav3r returns a final decision.

## Docker example

For an isolated local run without installing into the active Python environment:

```bash
export BEAV3R_BASE_URL=http://127.0.0.1:3000
export BEAV3R_API_KEY=bvr_test_...
export XIAOMI_MIMO_API_KEY=...
# Optional override if the provider endpoint differs:
# export XIAOMI_MIMO_BASE_URL=https://api.xiaomimimo.com/v1

bash scripts/run_simple_agent_docker.sh
```

The script uses Python 3.11 in Docker, installs the sibling `beav3r-sdk-py` repository,
installs this adapter plus `langchain-openai`, and runs the example agent.
