Rust

Rust has no first-party OpenAI SDK. This page uses async-openai, the community-maintained crate most Rust projects use. Its OpenAIConfig carries both settings you need to change — with_api_base and with_api_key — and nothing else about your code changes. The provider key stays in the vault.

Install

cargo add async-openai

Configure

use async_openai::{
    config::OpenAIConfig,
    types::{
        ChatCompletionRequestUserMessageArgs, CreateChatCompletionRequestArgs,
    },
    Client,
};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let org = std::env::var("VULNETIX_ORG_UUID")?;

    let config = OpenAIConfig::new()
        .with_api_base(format!("https://guardrails.vulnetix.com/openai/{org}/v1"))
        .with_api_key(std::env::var("VULNETIX_API_KEY")?); // Vulnetix key, not an OpenAI key

    let client = Client::with_config(config);

    let request = CreateChatCompletionRequestArgs::default()
        .model("gpt-4o-mini")
        .messages([ChatCompletionRequestUserMessageArgs::default()
            .content("Hello")
            .build()?
            .into()])
        .build()?;

    let response = client.chat().create(request).await?;
    println!("{}", response.choices[0].message.content.clone().unwrap_or_default());
    Ok(())
}

Swap openai in the path for any other provider slug and pass that provider’s own model string — the gateway forwards model verbatim.

Streaming

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

use futures::StreamExt;

let mut stream = client.chat().create_stream(request).await?;
while let Some(result) = stream.next().await {
    let chunk = result?;
    for choice in chunk.choices {
        if let Some(content) = choice.delta.content {
            print!("{content}");
        }
    }
}

Handling a guardrail block

A request your policy refuses never reaches the provider. It returns an OpenAI-shaped 403, which the crate surfaces as OpenAIError::ApiError, carrying the parsed error object.

use async_openai::error::OpenAIError;

match client.chat().create(request).await {
    Ok(response) => { /* … */ }
    Err(OpenAIError::ApiError(err)) => {
        match err.code.as_deref() {
            Some("request_blocked") => {
                // A content guardrail matched. The rule name is in the message.
                eprintln!("AI firewall blocked this request: {}", err.message);
            }
            Some("provider_denied") | Some("model_denied")
            | Some("model_not_allowed") | Some("provider_key_missing") => {
                eprintln!("AI firewall policy refused this call: {}", err.message);
            }
            _ => eprintln!("provider error: {}", err.message),
        }
    }
    Err(e) => return Err(e.into()),
}

err.code is what tells your organisation refused this apart from the provider refused this:

codeMeaning
request_blockedA content guardrail 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.

ApiError models the standard OpenAI error fields (message, type, param, code) and drops Vulnetix’s blocked_by and violations. The rule name also appears in message; if you need the structured fields, call the endpoint with reqwest and deserialise the body yourself — the envelope is in curl & raw HTTP.

Note Redaction is silent — you get Ok, not Err. A pii_redact rule rewrites the matched spans in your prompt to the literal [REDACTED] and forwards the request. The completion comes back normally, about redacted text.

Tool / function calling

Tools are forwarded untouched. Guardrails inspect the request you send, which includes the tool-result messages you push onto the message list before the next call.

Gotchas

  • with_api_key takes your Vulnetix key. A provider key here is the most common 401.
  • OpenAIConfig::new() falls back to OPENAI_API_KEY from the environment. Set the key explicitly so a stale provider key on the machine cannot win.
  • with_api_base must include the /v1 suffix.
  • This is a community crate. If you use a different one, the two settings are the same — point its base URL at …/{orgUuid}/v1 and give it the Vulnetix key.