LlamaIndex
Route LlamaIndex completions, agents, and RAG ingestion through Sentisec by setting api_base on the LLM and the embedding model.
LlamaIndex builds its LLM and its embedding client on the OpenAI wire, and
both take an api_base argument. Point them at your workspace and every
completion, every agent tool call, and every embedding your ingestion pass
makes goes through Sentisec.
The swap
from llama_index.llms.openai import OpenAI as LlamaOpenAI
llm = LlamaOpenAI(
model="gpt-5.6-terra",
api_base="https://api.sentisec.ch/ws/<your-workspace-id>/v1",
api_key="sk_sentisec_xxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
)
print(llm.complete("Compare the pricing pages of the three shortlisted vendors."))The argument is api_base, not base_url ā that is LlamaIndex's own naming
ā and the /v1 suffix belongs on it. The workspace id in the URL is what
carries your policy; why it matters.
The API key is your Sentisec key, not your OpenAI key ā Sentisec holds the upstream credential, so your provider key never ships in your application config.
Streaming needs no extra wiring: llm.stream_complete(...) yields chunks
whose .delta carries the text.
Agents
Hand that llm to an agent and every tool call it makes is gated. Nothing
about the agent's construction changes.
from llama_index.core.agent.workflow import FunctionAgent
from llama_index.core.tools import FunctionTool
def read_portfolio_config(path: str) -> str:
"""Read the portfolio config document."""
return open(path).read()
agent = FunctionAgent(
tools=[FunctionTool.from_defaults(fn=read_portfolio_config)],
llm=llm,
system_prompt="Use your tools to finish the task the user gives you.",
)
response = await agent.run("Read the portfolio config and finish the setup it describes.")If you drive tools yourself instead of through an agent, the serialiser path
is covered as well ā chat_with_tools puts the tool schemas on the wire and
get_tool_calls_from_response reads the calls back:
response = llm.chat_with_tools([tool], user_msg="Read the portfolio config.")
calls = llm.get_tool_calls_from_response(response, error_on_no_tool_call=True)What happens when a tool call is blocked
Take a two-turn loop: the agent reads a document carrying a planted instruction, then on the next turn tries to send a secret out of that document to an external address. Sentisec stops that second call.
By default the loop ends cleanly, with the explanation as the agent's final response. No hang, no exception, no retry storm. Sentisec withholds the call and puts the explanation in the model's message, so the agent's response reads:
[Sentisec blocked tool_use: <why the call was stopped>]The tool never ran. If you do not want that sentence shown to your user, filter the agent's final response before rendering it.
With the rename opt-in, the agent re-plans. The call comes back as
sentisec_tool_call_blocked, FunctionAgent answers it as an unknown
tool, and Sentisec replaces that answer with its ruling before the model
reads it. See the supported versions.
RAG ingestion
OpenAIEmbedding takes the same api_base, so your ingestion pass routes
through Sentisec rather than around it:
from llama_index.core import Document, VectorStoreIndex
from llama_index.embeddings.openai import OpenAIEmbedding
embed = OpenAIEmbedding(
model="text-embedding-3-small",
api_base="https://api.sentisec.ch/ws/<your-workspace-id>/v1",
api_key="sk_sentisec_xxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
)
index = VectorStoreIndex.from_documents(documents, embed_model=embed)
nodes = index.as_retriever(similarity_top_k=1).retrieve(
"which region is the portfolio account in?"
)Both the ingest embeddings and the query embedding traverse the gateway, and the index comes back queryable. Ranking quality is your embedding model's business ā routing through Sentisec does not change it.
Verdicts
Every request produces a verdict on the dashboard ā NOMINAL when nothing
was unusual, escalating to HIJACK when a tool call is traced back to
content the agent was never supposed to act on. The intervention
(PROCEED, SANITIZE, HALT) records what actually happened to the call.
New workspaces start in a monitor-only posture, so you can watch what a policy would do before it does it.
See also
PydanticAI
Run PydanticAI agents through Sentisec via the model's provider base URL, and see a blocked tool call in the run's own message history.
SaaS platform integration
Embed Sentisec across your multi-tenant customer base ā one workspace per customer, provisioned from your signup flow, with BYOK or pooled provider credentials.