Integrations

LangChain

Route LangChain through Sentisec with a base-URL change, and understand exactly what a LangChain agent loop does when a tool call is blocked.

LangChain's chat-model classes take a base_url argument. Setting it routes every model call your chain makes through Sentisec — including every tool call an agent tries to run. Your chains, tools, prompts, and callbacks are untouched.

Point the model at your workspace

from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    model="gpt-5.6-terra",
    base_url="https://api.sentisec.ch/ws/<your-workspace-id>/v1",
    api_key="sk_sentisec_xxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
)
from langchain_anthropic import ChatAnthropic

llm = ChatAnthropic(
    model="claude-sonnet-4",
    base_url="https://api.sentisec.ch/ws/<your-workspace-id>",
    api_key="sk_sentisec_xxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    max_tokens=512,
)

The /v1 suffix belongs on the OpenAI-shaped base URL and not on the Anthropic-shaped one — the Anthropic client appends its own path. The workspace id in the URL is what carries your policy; why it matters.

The api_key is your Sentisec key. Sentisec holds the upstream provider credential, so your provider key never ships in your application config.

That is the whole integration for chains, RunnableSequence pipelines, LCEL, and anything else that calls the model through this object.

Agent loops

Agent loops need one more thing from you: knowing which loop you are on, because only the current one supports every blocked-call setting.

Warning
`AgentExecutor` is not part of LangChain 1.x

On LangChain 1.x, from langchain.agents import AgentExecutor raises an ImportError — the class lives in the separate langchain-classic package now. langchain.agents exports create_agent, which builds a LangGraph-based loop. Both are covered below.

Current LangChain — create_agent

from langchain.agents import create_agent

agent = create_agent(llm, tools)

result = agent.invoke(
    {"messages": [("user", "Reconcile the quarterly ledger.")]}
)

This builds the same graph LangGraph's prebuilt agent builds.

Under a block, by default, the run ends with the explanation. Sentisec withholds the call and puts [Sentisec blocked tool_use: …] in the model's message. With no tool call left, the agent finishes and that message is the last one in result["messages"]. Your tool function never runs, so nothing half-executes and nothing needs unwinding.

With the rename opt-in, the model re-plans. The call comes back as sentisec_tool_call_blocked, create_agent answers it as an unknown tool, and Sentisec replaces that answer with its ruling before the model reads it. The model learns that a policy stopped the call and why, and finishes the task another way. See the supported versions.

Legacy AgentExecutor — via langchain-classic

from langchain_classic.agents import AgentExecutor, create_tool_calling_agent

agent = create_tool_calling_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools)

result = executor.invoke({"input": "Reconcile the quarterly ledger."})

By default a block ends the run here too: the executor reads the explanation as the model's final answer, so it lands in output. If your application shows output to a user, expect that sentence there. The rename opt-in is not supported on AgentExecutor; for a new build, use create_agent.

LangGraph

LangGraph gets its own page — LangGraph — covering the prebuilt agent, custom StateGraph loops, streaming, and the blocked-call message trace.

See also

Protected docs