Python

The official openai Python SDK talks to the firewall exactly as it talks to OpenAI. You change base_url and api_key; your model strings, messages, tools, and response parsing all stay as they are. The gateway holds your provider key server-side and injects it upstream, so this process never carries one.

Install

pip install openai

Configure

Have the CLI print the snippet, with your organisation UUID already filled in:

vulnetix ai-firewall snippet --lang python --sdk openai --provider openai

Or write it by hand — two settings, and only two:

import os
from openai import OpenAI

client = OpenAI(
    base_url=f"https://guardrails.vulnetix.com/openai/{os.environ['VULNETIX_ORG_UUID']}/v1",
    api_key=os.environ["VULNETIX_API_KEY"],  # your Vulnetix key, not an OpenAI key
)

response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "Hello"}],
)
print(response.choices[0].message.content)

Swap openai in the path for any other provider slug — groq, mistral, deepseek, openrouter — and change the model string to whatever that provider calls its model. The gateway forwards model verbatim.

Streaming

Unchanged. The gateway relays the provider’s SSE stream.

stream = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "Hello"}],
    stream=True,
)
for chunk in stream:
    delta = chunk.choices[0].delta.content
    if delta:
        print(delta, end="", flush=True)

Handling a guardrail block

A request your policy refuses never reaches the provider. It comes back as an OpenAI-shaped 403, which the SDK raises as openai.PermissionDeniedError. This is the one thing worth adding to your code.

import openai

try:
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": prompt}],
    )
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 request: rule {rule!r}") from e
    raise

Read code to tell your organisation refused this apart from the provider refused this:

codeMeaning
request_blockedA content guardrail matched. blocked_by names the rule; violations lists every rule that matched.
provider_deniedYour organisation’s policy does not allow this provider.
model_denied / model_not_allowedYour organisation’s policy does not allow this model.
provider_key_missingNo provider key is stored in the vault for this provider.
Note Redaction is silent — it is not an error. A pii_redact rule rewrites the matched spans in your prompt to the literal [REDACTED] and lets the request proceed. Your code sees no exception and no warning; it simply gets a completion about redacted text. If a model seems to be answering a question you did not quite ask, check your redaction rules before you check the model.

Tool / function calling

Tools pass through untouched — tools, tool_choice, and the tool_calls you get back are all the provider’s own. Guardrails inspect the request you send, which includes the tool messages you send back with results. A tool that returns a secret and hands it to the next turn is a request the firewall will see, and can block or redact.

Gotchas

  • api_key is your Vulnetix key. Passing an OpenAI key here is the most common 401. The provider key belongs in the vault, not in this process.
  • The SDK reads OPENAI_BASE_URL and OPENAI_API_KEY from the environment if you omit the arguments — so vulnetix ai-firewall install shell alone can route an existing script through the gateway with no code change at all. Passing base_url explicitly is the version that survives a shell that was never re-sourced.
  • Use openai.APIStatusError if you want one handler for every HTTP failure; PermissionDeniedError is the 403 subclass of it.