Metadata-Version: 2.3
Name: hiddenlayer-langchain-guardrails
Version: 0.2.0
Summary: Guardrails for LangChain
Author: HiddenLayer
Author-email: HiddenLayer <sdks@hiddenlayer.com>
License: Apache-2.0
Classifier: Typing :: Typed
Classifier: Intended Audience :: Developers
Classifier: Development Status :: 4 - Beta
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: Programming Language :: Python :: 3.14
Classifier: Operating System :: OS Independent
Classifier: Operating System :: POSIX
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: Microsoft :: Windows
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: License :: OSI Approved :: Apache Software License
Requires-Dist: hiddenlayer-sdk>=3.1.0
Requires-Dist: langchain>=1.2.7
Requires-Dist: langchain-openai>=1.1.7
Requires-Python: >=3.10
Description-Content-Type: text/markdown

## HiddenLayer Guardrails for 🦜🔗 LangChain & LangGraph (Beta)

This package provides a LangChain agent middleware that integrates with the
[HiddenLayer Python SDK](https://github.com/hiddenlayerai/hiddenlayer-sdk-python) to scan, redact, and/or block content before and after the agent executes.

It follows the official LangChain [custom guardrails](https://docs.langchain.com/oss/python/langchain/guardrails#custom-guardrails) middleware pattern using wrap-style hooks to intercept model and tool request and responses.

### Installation

```bash
pip install hiddenlayer-langchain-guardrails
```

### Configuration
Set your credentials in your environment variables to authenticate with HiddenLayer via the SDK:

* `HIDDENLAYER_CLIENT_ID`
* `HIDDENLAYER_CLIENT_SECRET`

---

### Usage
```python
from langchain.agents import create_agent
from langchain.tools import tool
from hiddenlayer_langchain_guardrails import HiddenLayerGuardrail, HiddenLayerParams

@tool
def get_weather(city: str) -> str:
    """Return simple weather info for the specified city."""
    return f"The weather in {city} is sunny."

agent = create_agent(
    model="gpt-4o-mini",
    tools=[get_weather],
    middleware=[HiddenLayerGuardrail(
        params=HiddenLayerParams(
            model="gpt-4o-mini",
            project_id=None,          # or your HL project id
            requester_id="example",   # optional but recommended
        )
    )],
)

result = agent.invoke(
    {
        "messages": [
            {"role": "system", "content": "Always respond in haiku form."},
            {"role": "user", "content": "What's the weather in Austin? Use the get_weather tool."},
        ]
    }
)

print(result["messages"][-1].content)
```

#### LangGraph Agent with Memory

Use `InMemorySaver` as a checkpointer to give your agent persistent conversation history across turns:

```python
from langchain.agents import create_agent
from langchain.tools import tool
from langchain_core.runnables import RunnableConfig
from langgraph.checkpoint.memory import InMemorySaver

from hiddenlayer_langchain_guardrails import HiddenLayerGuardrail, HiddenLayerParams

@tool
def calculator(expression: str) -> str:
    """Evaluate a basic math expression. Example: '(3 + 5) * 2'."""
    try:
        result = eval(expression, {"__builtins__": {}}, {})  # noqa: S307
        return str(result)
    except Exception as exc:
        return f"Error evaluating expression: {exc}"

agent = create_agent(
    model="gpt-4o-mini",
    tools=[calculator],
    middleware=[HiddenLayerGuardrail(
        params=HiddenLayerParams(requester_id="calculator-agent")
    )],
    checkpointer=InMemorySaver(),
    system_prompt="You are a helpful calculator assistant.",
)

config: RunnableConfig = {"configurable": {"thread_id": "session-1"}}

result = agent.invoke(
    {"messages": [{"role": "user", "content": "What is (12 * 34) + 1348? Use the calculator tool."}]},
    config=config,
)
print(result["messages"][-1].content)
```

#### Async Usage
```python
from hiddenlayer_langchain_guardrails import (
    AsyncHiddenLayerGuardrail,
    HiddenLayerParams,
)

@tool
def get_weather(city: str) -> str:
    """Return simple weather info for the specified city."""
    return f"The weather in {city} is sunny."

guardrail = AsyncHiddenLayerGuardrail(
    params=HiddenLayerParams(
        model="gpt-4o-mini",
        project_id=None,          # or your HL project id
        requester_id="example",   # optional but recommended
    )
)

agent = create_agent(
    model="gpt-4o-mini",
    tools=[get_weather],
    middleware=[guardrail],
)

async def main() -> None:
    result = await agent.ainvoke(
        {
            "messages": [
                {"role": "system", "content": "Always respond in haiku form."},
                {
                    "role": "user",
                    "content": "What's the weather in Austin? Use the get_weather tool.",
                },
            ]
        }
    )

    print(result["messages"][-1].content)

if __name__ == "__main__":
    import asyncio
    asyncio.run(main())

```

### Capability Matrix

| | Alert | Block | Redact |
|---|:---:|:---:|:---:|
| **Input Guardrails** | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| **Output Guardrails** | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| **Streaming Output Guardrails** | :white_check_mark: | :x: | :x: |

### Known Limitations

#### Streaming not supported
Due to a [bug in LangChain](https://github.com/langchain-ai/langchain/issues/35011), middleware guardrails do not run before tokens are streamed to the caller. This means that when using `agent.stream()` or `agent.astream()`, output guardrails cannot intercept content before it reaches the user, defeating their purpose for streaming workflows.

**Workaround:** Use `agent.invoke()` or `agent.ainvoke()` instead of the streaming variants to ensure guardrails are applied correctly.

### Development
Run tests after installing dev deps (`pytest` and `pytest-asyncio`): `pytest tests`
Code lives in [src/hiddenlayer_langchain_guardrails/middleware.py](./src/hiddenlayer_langchain_guardrails/middleware.py); tests are under the [tests](./tests/) directory.
