Skip to content

LangChain adapter

Returns standard LangChain BaseTool (specifically StructuredTool) instances — drop-in compatible with any agent, chain, or AgentExecutor. Two integration patterns:

  1. as_governed_tool — wrap a single Python callable as a governed BaseTool
  2. govern_toolkit — wrap an existing list of BaseTool instances (e.g. from a LangChain community toolkit) without mutating them

Both produce tools whose _run / _arun paths run through the policy gate before the underlying function executes.

Install

bash
pip install kitelogik langchain-core
# or for the full LangChain umbrella:
pip install kitelogik langchain

langchain-core is not a hard dependency — only imported on first adapter call. If missing, you get a clear ImportError with the install command.

Setup

python
from kitelogik import OPAClient, PolicyGate, SessionContext

gate = PolicyGate(opa_client=OPAClient())
context = SessionContext(
    session_id="sess_001",
    user_role="support_agent",
    session_scopes=["read_customer", "approve_refund_under_100"],
)

Pattern 1 — as_governed_tool

Wrap a single callable. Sync or async — both produce a StructuredTool that supports both invoke() and ainvoke().

python
from kitelogik.adapters.langchain import as_governed_tool

def approve_refund(customer_id: str, amount: float) -> str:
    return f"Refunded ${amount:.2f} to {customer_id}"

refund_tool = as_governed_tool(
    name="approve_refund",
    fn=approve_refund,
    gate=gate,
    context=context,
    description="Approve a refund. Args: customer_id (str), amount (float).",
)

Pass it to any LangChain agent:

python
from langchain.agents import create_react_agent

agent = create_react_agent(llm, tools=[refund_tool])

The adapter inspects fn's signature and builds a Pydantic args_schema automatically, so per-argument type validation works the same way it would on a hand-written StructuredTool.

Pattern 2 — govern_toolkit

Wrap an existing list of BaseTools — useful when you're consuming a LangChain community toolkit:

python
from kitelogik.adapters.langchain import govern_toolkit
from langchain_community.agent_toolkits import SQLDatabaseToolkit

raw_tools = SQLDatabaseToolkit(db=db, llm=llm).get_tools()
governed_tools = govern_toolkit(raw_tools, gate=gate, context=context)

agent = create_react_agent(llm, tools=governed_tools)

Original tool objects are not mutated — new wrapper objects are returned. Each wrapper preserves the original tool's args_schema, name, and description, falling back to inferring from _run / _arun signatures when no schema is exposed.

The adapter prefers the public ainvoke / invoke API on the wrapped tool (forward-compatible with langchain-core ≥ 0.3 which requires a config kwarg in _arun), and falls back to the private accessors only on older versions.

What happens on a deny

A denied call returns the string "[BLOCKED] <error message>" from the tool — the agent loop continues, the model sees the refusal and can react.

text
Pattern 1 — as_governed_tool
  ALLOW: Refunded $42.00 to cust_001
  BLOCK: [BLOCKED] Governance denied tool call: Refunds over $200 require manager approval

Pattern 2 — govern_toolkit
  ALLOW: Refunded $42.00 to cust_001
  BLOCK: [BLOCKED] Governance denied tool call: Refunds over $200 require manager approval

Function signatures

python
as_governed_tool(
    name: str,
    fn: Callable,
    gate: PolicyGate,
    context: SessionContext,
    description: str = "",
    action: str | None = None,
    sanitize: bool = True,
) -> BaseTool
python
govern_toolkit(
    tools: list[BaseTool],
    gate: PolicyGate,
    context: SessionContext,
    sanitize: bool = True,
) -> list[BaseTool]
ParamUse
actionOverride the OPA action name (defaults to name)
sanitizeRun prompt-injection scan on string returns (default True)

Runnable example

examples/04_langchain_agent.py in the OSS repo demonstrates both patterns end-to-end.

Source

kitelogik/adapters/langchain.py on GitHub.

Released under the Apache 2.0 License.