Skip to main content

Python client for the Inhibitor policy compliance inference API

Project description

Inhibitor Python SDK

Official Python client for the Inhibitor policy compliance API. Evaluate natural-language content against your policies and get ALLOW, MODIFY, or BLOCK decisions with explanations.


Features

  • Simple API — Send only query, metadata, and additional_metadata; no extra parameters.
  • Structured metadata — Use the {"key": {"value": "val", "description": "des"}} shape for clear, documented context.
  • Sync and asyncInhibitorClient and AsyncInhibitorClient with the same interface.
  • Typed models — Pydantic request/response models for IDE support and validation.
  • LangSmith run tracking — Attach a LangSmith run_id to any inference log for end-to-end LLM trace monitoring from the Inhibitor dashboard.

Installation

pip install inhibitor

Requirements: Python 3.9+, httpx, pydantic.


Quick start

from inhibitor import InhibitorClient

client = InhibitorClient(api_key="your-api-key")

response = client.inference.query(
    "Can I share this document with external partners?",
    metadata={
        "consent": {"value": "yes", "description": "User gave consent"},
        "data_type": {"value": "PHI", "description": "Contains protected health info"},
    },
)

print(response)   # Complete Response
print(response.final_decision)   # ALLOW | MODIFY | BLOCK
print(response.explanation)      # Human-readable explanation
print(response.processing_time_seconds)
client = InhibitorClient(api_key="your-api-key", base_url="https://your-custom-api.com")

API overview

What you send (request)

Only four fields are accepted:

Field Type Required Description
query str Yes Natural language content to evaluate against your policies.
metadata dict No Context for compliance. Shape: {"key": {"value": "val", "description": "des"}}. Used by the engine for the decision.
additional_metadata dict No Stores optional user information (e.g., name, email) as key-value pairs, saved along with the inference log for auditing purposes. Example: {"user_name": "John Doe", "email": "john.doe@example.com"}
explanation bool No If True, wait for explanation generation and include in response. Defaults to False.

Metadata shape — Each key maps to an object with:

  • value (str): The actual value (e.g. "yes", "PHI").
  • description (str): Optional short description of the field.

Example:

metadata = {
    "consent": {"value": "yes", "description": "User consent obtained"},
    "classification": {"value": "internal", "description": "Document classification"},
}

What you get (response)

InferenceResponse includes:

Field Type Description
log_id str | None ID of the inference log for fetching details later.
query str Echo of the query.
final_decision str ALLOW, MODIFY, or BLOCK.
final_score float Aggregate score (0–1).
matched_policies list[str] Policies that were evaluated.
model_results list[RuleResult] Per-rule decision, confidence, and probabilities.
explanation str | None Human-readable explanation.
make_it_compliant str | None Suggested changes to make the query compliant.

Usage

Sync client

from inhibitor import InhibitorClient, InferenceRequest

client = InhibitorClient(api_key="your-api-key")

# Simple query
response = client.inference.query("Should I send this email to a competitor?")

# With metadata (authoritative context for the engine)
response = client.inference.query(
    "Share the report with the client.",
    metadata={
        "consent": {"value": "yes", "description": "Client consent on file"},
        "channel": {"value": "email", "description": "Delivery channel"},
    },
)

# With additional_metadata (stored with log only)
response = client.inference.query(
    "Upload to cloud storage.",
    additional_metadata={"user_name": "John Doe", "email": "john.doe@example.com"},
)

# Using the request model
req = InferenceRequest(
    query="Can I export this data?",
    metadata={"data_type": {"value": "PII", "description": "Personal data"}},
)
response = client.inference.run(req)

# Fetching full inference log details
if response.log_id:
    log_details = client.inference.get_log(response.log_id)
    print(log_details)

Async client

from inhibitor import AsyncInhibitorClient

async with AsyncInhibitorClient(api_key="your-api-key") as client:
    response = await client.inference.query_async(
        "Can I share this document?",
        metadata={"consent": {"value": "yes", "description": "Consent given"}},
    )
    print(response.final_decision)

# Or with run_async
req = InferenceRequest(query="...", metadata={...})
response = await client.inference.run_async(req)

# Fetching full inference log details (async)
if response.log_id:
    log_details = await client.inference.get_log_async(response.log_id)

Configuration

Parameter Description
api_key Required. Your Inhibitor API key (from the dashboard).
base_url Override API base URL. Default: https://inhibitor-be.envistudios.com.
timeout Request timeout in seconds (default: 60.0).
headers Optional extra HTTP headers.

The client sends the API key in the X-API-Key header. No other auth is required.


Exceptions

Exception When
InhibitorAuthError Invalid or expired API key (401).
InhibitorValidationError Bad request or invalid parameters (400).
InhibitorAPIError Other API errors (4xx/5xx). Has status_code, detail, response_body.
ComplianceBlockedError Raised by :func:check_compliance when the decision is BLOCK. Has response (full :class:InferenceResponse).

All extend InhibitorError (with message and optional status_code).

from inhibitor import InhibitorClient
from inhibitor.exceptions import InhibitorAuthError, InhibitorAPIError

client = InhibitorClient(api_key="your-api-key")
try:
    r = client.inference.query("Some query")
except InhibitorAuthError as e:
    print("Auth failed:", e.message)
except InhibitorAPIError as e:
    print("API error:", e.status_code, e.detail)

LangSmith run tracking

Inhibitor integrates with LangSmith to give you end-to-end visibility into your LLM executions directly from the Inhibitor dashboard.

When you run an LLM call traced through LangSmith, you can attach the resulting run_id to the corresponding Inhibitor inference log. The dashboard will then surface the full trace alongside the policy decision — inputs, outputs, token usage, latency, cost, and intermediate tool calls — without switching tabs or tools.

Why use it

  • Correlate policy decisions with the exact LLM call that triggered them.
  • Debug blocked or modified responses by inspecting the full prompt and output.
  • Monitor token usage and cost per inference from a single view.

update_log parameters

Parameter Type Required Description
log_id str Yes ID of the Inhibitor inference log. Returned as response.log_id after calling client.inference.query(...).
run_id str Yes LangSmith run ID (UUID) to attach to the log. Must be a valid UUID — validated client-side before the request is sent.

Example

from inhibitor import InhibitorClient

client = InhibitorClient(api_key="your-api-key")

# 1. Run policy inference and capture the log ID
response = client.inference.query("Can I share this document externally?")
log_id = response.log_id

# 2. Run your LLM call with LangSmith tracing and capture the run_id
#    (langsmith_run_id comes from your LangSmith callback/tracer)
langsmith_run_id = "9b7505cd-f111-4a34-8cc5-9006da96cbfc"

# 3. Attach the LangSmith run_id to the inference log
if log_id:
    updated = client.inference.update_log(
        log_id=log_id,
        run_id=langsmith_run_id,
    )
    print(updated)

The updated log is now visible in the Inference Logs section of the Inhibitor dashboard with the full LangSmith trace attached.

Async variant

from inhibitor import AsyncInhibitorClient

async with AsyncInhibitorClient(api_key="your-api-key") as client:
    response = await client.inference.query_async("Summarize this contract.")
    if response.log_id:
        updated = await client.inference.update_log_async(
            log_id=response.log_id,
            run_id="9b7505cd-f111-4a34-8cc5-9006da96cbfc",
        )
        print(updated)

Validation

update_log and update_log_async validate run_id on the client before making any network call. An invalid UUID raises ValueError immediately:

from inhibitor import InhibitorClient

client = InhibitorClient(api_key="your-api-key")

try:
    client.inference.update_log(log_id="...", run_id="not-a-uuid")
except ValueError as e:
    print(e)  # run_id must be a valid UUID, got: 'not-a-uuid'

Integration checklist

  1. Get an API key from your Inhibitor dashboard.
  2. Installpip install inhibitor.
  3. Create clientInhibitorClient(api_key="...").
  4. Callclient.inference.query(query, metadata=..., additional_metadata=...).
  5. Handle — Use response.final_decision, response.explanation, and catch InhibitorAuthError / InhibitorAPIError as needed.

Decorator: check compliance before calling your function

Use :func:check_compliance to run the inference API on a chosen argument before your function runs. If the decision is BLOCK, the decorator raises :exc:ComplianceBlockedError (or returns None); otherwise it calls your function.

from inhibitor import InhibitorClient, check_compliance

client = InhibitorClient(api_key="your-api-key")

@check_compliance(client, query_arg="message")
def handle_user_message(message: str, user_id: str):
    # Only runs if compliance allows (ALLOW or MODIFY)
    return process(message, user_id)

# BLOCK → ComplianceBlockedError (with response.explanation, response.make_it_compliant)
# ALLOW / MODIFY → your function runs
handle_user_message("Can I share this file?", "u-123")

Options: query_arg (param name to check, default "query"), on_block ("raise" or "return_none"), metadata, pass_response (inject _inhibitor_response into kwargs). Use :class:AsyncInhibitorClient with async functions.

from inhibitor import AsyncInhibitorClient, check_compliance

client = AsyncInhibitorClient(api_key="your-api-key")

@check_compliance(client, query_arg="prompt")
async def run_llm(prompt: str):
    return await model.generate(prompt)

Catch blocks with :exc:ComplianceBlockedError and use e.response.explanation or e.response.make_it_compliant to show the user why it was blocked or how to fix it.


Package structure

Each module has a single responsibility:

Module Purpose
inhibitor.constants Default base URL and API path.
inhibitor._http HTTP response helpers (e.g. error detail extraction).
inhibitor.models Request/response Pydantic models and metadata shape.
inhibitor.inference Inference API (sync/async): query, run, get_log, update_log, and async variants.
inhibitor.client Sync and async HTTP clients.
inhibitor.exceptions Auth, validation, and API exceptions.

Import the public API from inhibitor; use submodules only if you need constants or helpers.


License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

inhibitor-0.1.4.tar.gz (17.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

inhibitor-0.1.4-py3-none-any.whl (14.4 kB view details)

Uploaded Python 3

File details

Details for the file inhibitor-0.1.4.tar.gz.

File metadata

  • Download URL: inhibitor-0.1.4.tar.gz
  • Upload date:
  • Size: 17.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for inhibitor-0.1.4.tar.gz
Algorithm Hash digest
SHA256 bdef69ab5a46bb7ae595f1a655264832aeac383faf5f54df16158e6a4a4f97c0
MD5 1165688df0e739a316a4093a226138cb
BLAKE2b-256 be740278729d2c0e1cc4d3155097f8f28b23159b3115e35f6f57d9e547003386

See more details on using hashes here.

File details

Details for the file inhibitor-0.1.4-py3-none-any.whl.

File metadata

  • Download URL: inhibitor-0.1.4-py3-none-any.whl
  • Upload date:
  • Size: 14.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for inhibitor-0.1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 2df7035537eff17010b0422d6102ace3895d367d8b699adcdcac3cc603210fae
MD5 4db0a1f2304be1608170c9d0f9ff8587
BLAKE2b-256 6ed3f4de7dcfc80645b60edaceaeb07276316f5054059db02ea3f30692fc658e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page