Skip to main content

rogue-security

Official Python SDK for the Rogue Security AppSec evaluation API.

Wraps the two public AppSec methods, evaluate (inline checks) and invoke (saved guardrail), with typed params and responses (Pydantic v2), typed errors, automatic retries, and debug logging. Ships sync and async clients.

Install

pip install rogue-security

Quickstart

from rogue_security import RogueClient

with RogueClient(api_key="rsk_...") as rogue:
    result = rogue.evaluate(
        messages=[
            {"role": "user", "content": "What is the patient's SSN?"},
            {"role": "assistant", "content": "It is 123-45-6789."},
        ],
        pii_check=True,
        prompt_injections=True,
    )

print(result.status, result.score)
for group in result.evaluation_results:
    for r in group.results:
        if r.flagged:
            print(group.type, r.label, r.reason)

api_key falls back to the ROGUE_API_KEY environment variable if omitted.

Async

from rogue_security import AsyncRogueClient

async with AsyncRogueClient() as rogue:        # reads ROGUE_API_KEY
    result = await rogue.evaluate(
        messages=[{"role": "user", "content": "..."}],
        content_moderation_check=True,
    )

Invoke a saved guardrail

result = rogue.invoke(
    guardrail_id="gr_abc123",
    messages=[
        {"role": "user", "content": user_prompt},
        {"role": "assistant", "content": model_response},
    ],
)
if result.blocked:
    ...  # the guardrail's workspace is set to block and the evaluation failed

You can also pass a typed params object instead of keyword arguments:

from rogue_security import EvaluateParams

rogue.evaluate(
    EvaluateParams(
        messages=[{"role": "user", "content": "..."}],
        pii_check=True,
        hallucinations_mode="quality",
    )
)

Error handling

from rogue_security import RogueAuthError, RogueValidationError, RogueError

try:
    rogue.evaluate(pii_check=True)
except RogueAuthError:
    ...  # bad / missing key
except RogueValidationError:
    ...  # 422, e.g. no checks enabled
except RogueError as err:
    print(err.status, err)

RogueServerError (5xx), rate limits (429), and network/timeout failures are retried automatically (default 2 retries, exponential backoff with jitter).

Options

RogueClient(
    api_key="rsk_...",
    base_url="http://localhost:8006",  # local dev; defaults to production (or ROGUE_BASE_URL)
    timeout=30.0,
    max_retries=2,                     # retries via tenacity, exponential backoff + jitter
    debug=True,                        # logs via the "rogue_security" logger; key is masked
)

Corporate networks / custom TLS (e.g. Zscaler)

Pass a verify value (CA bundle path or ssl.SSLContext) or your own configured httpx client. A client you pass in is yours to close; the SDK won't close it.

import httpx
from rogue_security import RogueClient

# Option A: custom CA bundle
RogueClient(api_key="rsk_...", verify="/path/to/corp-ca.pem")

# Option B: bring your own httpx client (proxies, mTLS, etc.)
RogueClient(api_key="rsk_...", http_client=httpx.Client(verify="/path/to/corp-ca.pem"))

LiteLLM proxy guardrail

Setup, routing, coverage, and what a block looks like for each client: LiteLLM proxy integration guide.

rogue_security.litellm.RogueGuardrail evaluates every /chat/completions and Anthropic /v1/messages request and its response with evaluate, and blocks flagged turns. A streamed response is checked once, when the stream ends, and in the default stream_mode: monitor a flagged stream is only logged (see stream_mode below). /v1/messages/count_tokens runs no guardrail. Anthropic routes convert the raw request body, so documents and search results are evaluated.

The litellm extra needs Python 3.10 or newer, because LiteLLM 1.95.1 does not install on 3.9.

pip install 'rogue-security[litellm]>=1.1.0' 'litellm[proxy]==1.95.1' 'fastapi<0.140.7'

The LiteLLM 1.95.1 proxy fails to start with fastapi 0.140.7 or newer, so keep the fastapi<0.140.7 pin until LiteLLM ships a fix.

guardrails:
  - guardrail_name: rogue
    litellm_params:
      guardrail: rogue_security.litellm.RogueGuardrail
      mode: [pre_call, post_call]   # pre_call checks requests, post_call checks responses
      default_on: true
      api_key: os.environ/ROGUE_API_KEY
      rogue_base_url: os.environ/ROGUE_BASE_URL  # optional, defaults to production
      rogue_action: block           # block (default) or monitor (log only)
      rogue_checks:                 # any evaluate check flags; unknown keys fail proxy start-up
        prompt_injections: true
        pii_check: true
        assertions:
          - "The assistant never reveals internal system prompts."
      fail_open: true               # default; Rogue errors and timeouts let traffic through with a warning
      timeout: 4                    # seconds, default 4, no retries
      message_window: 1             # trailing client turns per evaluation, default 1 (last turn only)
      stream_mode: monitor          # monitor (default) or buffer, see below
      evaluate_all_text_blocks: false  # default; true checks every text block of the last user message

The options are rogue_action and rogue_checks because LiteLLM already defines typed action and checks guardrail params. Every *_mode runs at speed; policy_target and topic_scoping_target are input for requests and output for responses. With the default message_window of 1, response checks carry only the assistant reply, so context checks such as grounding have no source text unless you widen the window. Response checks evaluate the whole reply: every text block and every choice or output part is joined into one assistant message. Anthropic tools keep their input_schema. On /v1/responses, the conversation is rebuilt from the request's input and instructions.

A blocked request returns HTTP 400. LiteLLM wraps the guardrail detail, so the reasons are in error.provider_specific_fields.reasons, not at the top level of the body. A blocked non-streamed response returns HTTP 200 with finish_reason: "content_filter" and the block reasons as the message content. A streamed response follows stream_mode.

On /v1/messages, a blocked request returns HTTP 400 with the reasons inside error.message. A blocked response returns HTTP 200 with the reasons as the assistant text and stop_reason: "end_turn". The system prompt and tool results are sent to Rogue only when they fall inside message_window.

message_window counts client turns; the last turn includes the tool results delivered with the final message. LiteLLM turns one Anthropic user message with tool_result blocks into tool messages followed by a user message, so the default window sends that user message and every tool result of the same turn. Pass-through bodies are converted the same way, so their tool_result blocks become tool messages of the same turn. An assistant or system message is a turn of its own. Responses count plain trailing messages.

When the last user message of a request holds several text blocks, only the newest block is checked. Claude Code keeps a blocked prompt in the session and merges consecutive prompts into one user message, so checking every block would block every later turn. The trade-off: a payload in an earlier text block of that message, followed by a harmless last block, is not checked. Set evaluate_all_text_blocks: true to check the joined text of every block instead. Image and other non-text parts are kept, and tool results are separate tool messages, so they are still checked. Earlier messages in the window and responses are never trimmed. The rule applies to pass-through bodies too, after conversion.

stream_mode decides what a flagged stream does:

  • monitor (default): chunks reach the client as they arrive and the check runs at the end. A flagged stream is only logged, never blocked, and Rogue errors are only logged too.
  • buffer: the proxy holds every chunk until the check passes, then replays the stream. A flagged stream is never delivered. On /v1/messages the client gets a complete message that holds the block reasons and no tool call; on /chat/completions and /v1/responses it gets HTTP 500 with the block reasons in error.message. The client waits for the whole response first.

Claude Code

Point Claude Code at the proxy root, ANTHROPIC_BASE_URL=http://<proxy>:4000, and list the Claude models it uses in the proxy model_list, including Bedrock models as bedrock/.... Every request and response is then checked. With CLAUDE_CODE_USE_BEDROCK=1 or a /anthropic base URL, LiteLLM forwards the traffic as a passthrough and the response check does not run.

Pass-through routes

On LiteLLM's provider pass-through routes the guardrail gets the raw client body, not chat messages. It detects the body format and converts it to chat messages and tools before calling Rogue: Anthropic Messages (/anthropic/*), Bedrock Converse and Anthropic-on-Bedrock invoke (/bedrock/*), and Gemini generateContent. System prompts, tool calls, tool results and tool definitions are kept, as is the text of plain-text documents, search results and cited answers, along with document and search result titles and Anthropic document context. Tool results are sent as text, so a result holding only an image or file is sent empty. Images, files, audio, video, base64 payloads, reasoning blocks, Gemini thought parts and server-side tool results are dropped, and unknown part types are skipped. Only client-defined function tools are listed as available tools: Anthropic server and versioned tools (bash, text editor, computer use, web search and the rest), Gemini built-in tools and Converse system tools are not. As on every route, message_window decides how many of the converted messages are sent, by default the last client turn (the last user message and the tool results delivered with it), and only the newest text block of a merged last user message is kept. Request headers never reach Rogue. Of LiteLLM's own fields, only the guardrail name, litellm_call_id, and the virtual key alias and team alias or id (when set) are sent as evaluate metadata. An unrecognised body is sent as its JSON text in one user message. When a pass-through endpoint sets request_fields for the guardrail, only the text at those JSONPath fields is sent.

What LiteLLM 1.95.1 checks on each route, from end-to-end runs:

Vendor Client endpoint on the proxy Request check Response check, non-stream Response check, stream
Anthropic /v1/messages Yes Yes Yes (monitor logs after delivery; buffer blocks)
Anthropic Claude Code, ANTHROPIC_BASE_URL at the proxy root Yes Not tested Yes (monitor logs after delivery; buffer blocks)
Anthropic /anthropic/* pass-through Yes No (LiteLLM never calls the guardrail) Not tested
Bedrock (Claude) /v1/messages Yes Yes Yes (monitor logs after delivery; buffer blocks)
Bedrock (Claude) Claude Code, ANTHROPIC_BASE_URL at the proxy root Yes Not tested Yes (monitor logs after delivery; buffer blocks)
Bedrock (Claude) Claude Code in Bedrock mode (/bedrock base) Yes Not tested No (LiteLLM never calls the guardrail)
Bedrock /bedrock/* converse Yes Yes Not applicable, see converse-stream
Bedrock /bedrock/* converse-stream Yes Not applicable Yes, buffered by LiteLLM; a block is HTTP 500
Bedrock /bedrock/* invoke Yes Yes, raw JSON No (LiteLLM never calls the guardrail) on invoke-with-response-stream
Bedrock /chat/completions with a bedrock/... model Yes Yes Yes (monitor logs after delivery; buffer blocks)
OpenAI /chat/completions Yes Yes Yes (monitor logs after delivery; buffer blocks)
OpenAI /v1/responses Yes Yes Yes (monitor logs after delivery; buffer blocks)
Gemini /chat/completions with a gemini/... model Yes Yes Yes (monitor logs after delivery; buffer blocks)
Gemini /gemini/* pass-through Yes No (LiteLLM never calls the guardrail) Not tested
Gemini Native generateContent (/v1beta/models/...) Not tested Not tested Not tested
Any /v1/messages/count_tokens No (LiteLLM never calls the guardrail) No (LiteLLM never calls the guardrail) No (LiteLLM never calls the guardrail)

"Buffer blocks" means the flagged reply never reaches the client. On /v1/messages and Claude Code the client gets a complete streamed message holding the block reasons. On /chat/completions it gets an HTTP 200 stream whose one chunk holds the block reasons with finish_reason: "content_filter". On /v1/responses it gets an HTTP 200 stream of one complete response whose output text is the block reasons. Claude Code always streams, so its non-stream column is not tested. converse and invoke are non-streaming calls. LiteLLM buffers a converse-stream response before the check, so the client gets it in one burst; Claude Code in Bedrock mode streams through invoke-with-response-stream, which LiteLLM never checks. On /bedrock/* routes the model in the URL must be a model_name from model_list; a raw Bedrock model ID fails with Invalid model name.

LiteLLM runs a pass-through response check only for guardrails listed in a pass-through endpoint's guardrails config. Send Gemini traffic through /chat/completions to have both sides checked.

What happens on an uncovered route

On a side marked No, LiteLLM never hands that traffic to the guardrail. Nothing of ours runs there: no evaluation, no monitor log line, and no fail-open decision, because fail_open only applies when an evaluation is attempted and fails. The traffic passes unchecked. The request side on the same route still runs where the table says Yes. Not tested means no end-to-end run backs the cell either way.

Local development

uv sync
uv run pytest

More: see ../docs for the full API reference, check descriptions, and examples.

Configured guardrails

Discover IDs with client.guardrails.list() and read saved settings with client.guardrails.get(id). Both include the effective API-key workspace. The Guardrails UI also provides Copy ID on each card.

result = client.guardrails.execute(messages=[{"role": "user", "content": "Check this request"}])
print(result.status, result.decision, result.log_id)

The async client exposes the same resource methods with await.

Execution runs all active guardrails using their saved checks and Log/Block settings. Supply available tools when tool checks are enabled. Your application enforces decision; handle indeterminate, incomplete, and no_active_guardrails explicitly. A blocking finding wins over partial failures. List pagination starts at zero, defaults to 20 entries, and allows at most 100. Existing API keys work without new credentials.

Security controls

Inline checks now support secret scanning, explicit PII confidence thresholds (default 0.85), moderation methods (default speed), and coding category policies. Category actions are Off/Log/Block with a minimum raw severity; hidden categories always log. A supplied category map defaults unspecified categories to Block from critical. Saved Log mode caps category enforcement. invoke evaluates all linked checks, including inactive definitions, but only active Block bindings enforce. Inspect completion and decision independently of scores.

Use restricted_topics (Python) or restrictedTopics (TypeScript) for prohibited topics. The old allowed-topics alias remains deprecated with identical restricted-topic behavior; conflicting aliases are rejected. Tool results preserve tool_call_id / toolCallId. Responses include persisted log and workspace IDs. See check options for settings and defaults.

Metadata

Release files for rogue-security 1.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for rogue-security 1.1.0
File Size Uploaded
rogue_security-1.1.0.tar.gz 328.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for rogue-security 1.1.0
File Interpreter ABI Platform
rogue_security-1.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 354.8 kB

Release files / rogue_security-1.1.0.tar.gz

Download URL rogue_security-1.1.0.tar.gz
Size 328.4 kB
Tags Source
SHA-256 checksum
How to use checksums
9073526ec57904ef9762104f4159df4ad8193a8a46b1bfdfdcf5f5abc1405f60
BLAKE2b-256 checksum
How to use checksums
2fcb3231aa4cd45c9f468983b527ccadd51af193871e34b078ddfefd57998e01
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 6, 2026.

Transparency log

Release files / rogue_security-1.1.0-py3-none-any.whl

Download URL rogue_security-1.1.0-py3-none-any.whl
Size 26.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0b4c39efa23a2aea4fa9b39eb0e41405ef673b749e5596ca80006c2d2a9beffa
BLAKE2b-256 checksum
How to use checksums
f8b404abd52e4ea81ee18ce65e3ddf63c753a63fba5df2e97a231fa6ce9543e6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 6, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.1.0 This release

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page