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.
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
Anthropic SDK
Route the official Anthropic Python and Node SDKs through Sentisec by changing one argument — and what your code sees when a tool call is blocked.
LangGraph
Run a LangGraph agent loop through Sentisec — prebuilt agents, custom StateGraph loops, streaming, and the message trace of a blocked tool call.