LangChain

LangChain’s OpenAI integration is a thin wrapper over the OpenAI SDK, so the firewall is two constructor arguments away. Your chains, agents, retrievers, and tools do not change — they are downstream of the model object, and the model object is the only thing that knows where the gateway is.

Install

Python
pip install langchain-openai
JavaScript
npm install @langchain/openai

Configure

Have the CLI print the Python snippet with your organisation UUID already in it:

vulnetix ai-firewall snippet --lang python --sdk langchain --provider openai
Python
import os
from langchain_openai import ChatOpenAI

org = os.environ["VULNETIX_ORG_UUID"]

llm = ChatOpenAI(
    base_url=f"https://guardrails.vulnetix.com/openai/{org}/v1",
    api_key=os.environ["VULNETIX_API_KEY"],  # Vulnetix key, not an OpenAI key
    model="gpt-4o-mini",
)

print(llm.invoke("Hello").content)
JavaScript
import { ChatOpenAI } from '@langchain/openai'

const org = process.env.VULNETIX_ORG_UUID

const llm = new ChatOpenAI({
  apiKey: process.env.VULNETIX_API_KEY, // Vulnetix key, not an OpenAI key
  model: 'gpt-4o-mini',
  configuration: {
    baseURL: `https://guardrails.vulnetix.com/openai/${org}/v1`,
  },
})

const response = await llm.invoke('Hello')
console.log(response.content)

Note that in JavaScript the base URL goes inside configuration, which LangChain passes straight through to the OpenAI SDK.

Any OpenAI-compatible provider works through the same class — point the slug at groq, mistral, deepseek, openrouter, and use that provider’s model string. You do not need ChatGroq or ChatMistralAI to reach them through the firewall, and those classes will not route through it: they have their own base URLs.

Streaming

Unchanged.

for chunk in llm.stream("Hello"):
    print(chunk.content, end="", flush=True)

Handling a guardrail block

LangChain does not wrap SDK exceptions, so a guardrail refusal surfaces as the underlying OpenAI error: an HTTP 403 raised as openai.PermissionDeniedError. It is raised from invoke, stream, ainvoke, and from any chain or agent step that calls the model.

import openai

try:
    result = chain.invoke({"question": question})
except openai.PermissionDeniedError as e:          # HTTP 403
    err = e.response.json().get("error", {})
    code = err.get("code")                          # request_blocked, model_denied, ...
    rule = err.get("blocked_by")                    # the rule that stopped it
    if code == "request_blocked":
        raise RuntimeError(f"AI firewall blocked this chain: rule {rule!r}") from e
    raise
codeMeaning
request_blockedA content guardrail matched. blocked_by names the rule; violations lists every rule that matched.
provider_deniedPolicy does not allow this provider.
model_denied / model_not_allowedPolicy does not allow this model.
provider_key_missingNo provider key in the vault for this provider.

In an agent loop this matters more than in a single call: the block can land on any turn, including one triggered by a tool result the model asked for. Catch it around the agent invocation, not just around the first prompt.

Note Redaction is silent — nothing is raised. A pii_redact rule rewrites the matched spans to the literal [REDACTED] and lets the request through. A RAG chain whose retrieved documents contain PII will therefore run to completion with those spans redacted, and the model will answer as if it had never seen them. That is usually what you want — but it is worth knowing before you debug a chain that “ignored” its context.

Tool / function calling

bind_tools, tool-calling agents, and LangGraph nodes all pass through untouched. Guardrails inspect the request LangChain sends, and on a tool-calling turn that request contains the ToolMessage results from the previous step — so a tool that reads a secret and returns it into the conversation produces a request the firewall can block or redact, before it ever reaches the provider.

Gotchas

  • api_key is your Vulnetix key. A provider key here is the most common 401.
  • langchain-openai reads OPENAI_API_BASE and OPENAI_API_KEY from the environment, so vulnetix ai-firewall install shell can route an existing chain with no code change at all. Passing base_url explicitly is the version that survives a shell that was never re-sourced, and the version you can review in a pull request.
  • Provider-specific chat classes (ChatAnthropic, ChatGroq, ChatMistralAI) do not route through the gateway unless you also give them its base URL. For the Anthropic one, remember its base URL takes no /v1 — see Anthropic (Python).
  • Embeddings are not proxied by the firewall — OpenAIEmbeddings pointed at the gateway will not find an endpoint. Leave embeddings talking to their provider directly.