Skip to main content

tawaguard-sdk

The official Python SDK for Tawa Guard, prompt-injection detection for LLM and agent traffic. It scans prompts, retrieved context, tool calls and tool output, and returns an allow or block decision for each.

Documentation: https://docs.tawaguard.com

Installation

pip install tawaguard-sdk

Python 3.9+ is required. The only runtime dependency is httpx.

Quick Start

Sync

from tawaguard import TawaGuard

client = TawaGuard("YOUR_API_KEY")

result = client.analyze("Hello, world!")

print(result.action)      # "allow" | "block" | "soft_block" | "hard_block"
                           # | "review" | "sandbox" | "ambiguous" | "ood"
print(result.confidence)  # 0-1
print(result.rationale)   # human-readable explanation

Async

import asyncio
from tawaguard import AsyncTawaGuard


async def main():
    async with AsyncTawaGuard("YOUR_API_KEY") as client:
        result = await client.analyze("Hello, world!")
        print(result.action)


asyncio.run(main())

By default, the sync TawaGuard constructor verifies the API key against /verify immediately, credential issues surface at construction time rather than on first use. For AsyncTawaGuard, use await AsyncTawaGuard.create(...) or async with AsyncTawaGuard(...) to verify eagerly; constructing it directly defers verification to the first request.

How It Works

Each call to analyze() scans a plain prompt (provenance="user_prompt") and returns a ScanResult, the same result shape every scan method returns (scan_context, scan_chunk, scan_tool_call, scan_tool_output all share it, since they all hit the worker's scan pipeline under the hood, just with different provenance):

Field Type Description
action ScanAction What the gateway enforced: "allow" or "block" from a current gateway. An older gateway can still return "soft_block" | "hard_block" | "review" | "sandbox" | "ambiguous" | "ood"
engine_action ScanAction The engine's own action before your app's review setting (for example "review" or "hard_block"). Equals action against an older gateway
needs_review bool True when the engine was not confident enough to call this one cleanly. A tag on an allow or a block, not a hold. Derived as action == "review" against an older gateway
confidence float Confidence score (0-1)
suggestion "allow" | "block" | None Coarse hint for the undecided actions (ambiguous/review/sandbox)
rationale str Human-readable explanation of the decision
policy PolicyVerdict | None Directives signaled by the worker's policy engine, if any
language LanguageInfo | None Language identification detail, None while language ID is disabled
score_breakdown dict[str, Any] | None Diagnostic detail behind the decision, shape varies by which decision path fired (fusion, regex-only, OOD, ...)

The SDK retries automatically (up to 3 attempts with exponential backoff + jitter) for rate limits (429) and transient server errors (5xx), honoring Retry-After when present.

Configuration

client = TawaGuard(
    "YOUR_API_KEY",
    base_url="https://gateway.tawaguard.com/v1",     # the default; override for self-hosted or staging
    max_retries=3,
    timeout=30.0,
    verify_auth=True,
)
Option Type Default Description
detector str | None None Opaque detector name, reserved for future use. The SDK does not send it to the service or interpret it; the service picks the detector
base_url str "https://gateway.tawaguard.com/v1" API endpoint. When omitted, the TAWA_BASE_URL environment variable is used if set
max_retries int 3 Max retry attempts for 429/5xx responses
timeout float 30.0 Request timeout in seconds
verify_auth bool True Verify the API key against /verify on construction (sync client only)

analyze() (and every other scan method) also accepts cuid, tenant_id, policy_overrides, and language as keyword arguments, forwarded straight to the worker per call.

Error Handling

All API and network errors raise TawaAPIError:

from tawaguard import TawaGuard, TawaAPIError

try:
    client = TawaGuard("YOUR_API_KEY")
    result = client.analyze("user input")
except TawaAPIError as err:
    print(err.message)     # error description
    print(err.status_code) # HTTP status code (or None for network errors)
    print(err.retryable)   # whether a retry could succeed

Usage Examples

Basic

from tawaguard import TawaGuard

def moderate_content(text: str):
    client = TawaGuard("YOUR_API_KEY")

    result = client.analyze(text)

    if result.action == "allow":
        print(f"Safe ({result.confidence:.2f} confidence)")
    elif result.action in ("block", "hard_block", "soft_block"):
        print(f"Blocked: {result.rationale}")
    elif result.action in ("ambiguous", "review", "sandbox"):
        # suggestion tells you which way the model leans
        print(f"Undecided ({result.action}), suggested action: {result.suggestion}")

    return result


moderate_content("Hello, world!")

With OpenAI (raw client)

Screen user prompts before they reach the model. If Tawa blocks or leans toward blocking, the request is rejected without an OpenAI call.

from openai import OpenAI
from tawaguard import TawaGuard

openai = OpenAI(api_key="...")
tawa = TawaGuard("YOUR_API_KEY")


_BLOCKED_ACTIONS = {"block", "hard_block", "soft_block"}


def chat(user_message: str):
    scan = tawa.analyze(user_message)

    if scan.action in _BLOCKED_ACTIONS or scan.suggestion == "block":
        raise ValueError("Message blocked by content policy.")

    return openai.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": user_message}],
    )

With the OpenAI Agents SDK

tawaguard.agents_sdk ships tawa_input_guardrail/tawa_output_guardrail, thin wrappers over analyze() shaped as Agents SDK input_guardrail/output_guardrail. Install with pip install "tawaguard-sdk[agents]" (an optional extra, openai-agents isn't a dependency of the base package). See examples/openai_guard.py.

from agents import Agent, InputGuardrailTripwireTriggered, Runner
from tawaguard import AsyncTawaGuard
from tawaguard.agents_sdk import tawa_input_guardrail, tawa_output_guardrail

tawa = await AsyncTawaGuard.create("YOUR_API_KEY")

agent = Agent(
    name="Assistant",
    instructions="You are a helpful assistant.",
    input_guardrails=[tawa_input_guardrail(tawa)],
    output_guardrails=[tawa_output_guardrail(tawa)],
)

try:
    result = await Runner.run(agent, user_message)
except InputGuardrailTripwireTriggered as err:
    scan = err.guardrail_result.output.output_info  # the ScanResult that tripped it
    raise ValueError(f"Message blocked: {scan.action}") from err

Both guardrails accept TawaGuard or AsyncTawaGuard (the sync client's blocking analyze() call runs off-thread via asyncio.to_thread so it doesn't stall the Agents SDK's event loop), plus should_block, extract_text, and cuid/tenant_id/policy_overrides/language overrides, see the docstrings in src/tawaguard/agents_sdk.py for the full option set.

The review flag

needs_review marks results the engine was less sure about. The call is still allowed or blocked as action says; the flag only tells you the verdict is worth a second look. It is set on every result the SDK returns (analyze, scan_context, scan_chunk, scan_tool_call, scan_tool_output, each entry of scan_retrieval().per_chunk), and scan_retrieval().aggregate adds needs_review and review_chunk_ids. When a response omits the new fields (an older gateway), engine_action is action and needs_review is action == "review".

Both guardrails take on_review, called with every flagged result so you can route it to a second model for an opinion:

from tawaguard.agents_sdk import tawa_input_guardrail


async def ask_second_model(text: str) -> str:
    """Your own callback: ask another model or a human queue. Placeholder."""
    ...


async def on_review(scan):
    opinion = await ask_second_model(scan.rationale)
    audit_log.append({"risk": scan.risk, "engine_action": scan.engine_action, "opinion": opinion})


guardrail = tawa_input_guardrail(
    tawa,
    on_review=on_review,  # sync or async
    on_error=lambda exc: print("review hook failed:", exc),
)

When on_review fires:

  • On every scan whose needs_review is True, whether the input is then allowed or blocked. It does not fire when needs_review is False, including clean allows, clean blocks and "ood" results.
  • Once per scan, before the guardrail returns its result, and awaited if it is async, so a slow callback delays that call.
  • It cannot change the decision: the result is a frozen dataclass and the return value is ignored.
  • It never raises into the guardrail. An exception goes to on_error(exc), or is swallowed when on_error is not set.

default_should_block is unchanged: a flagged result is blocked only if the gateway blocked it (or, against an older gateway, a review result carries suggestion == "block").

With FastAPI

from fastapi import FastAPI, HTTPException
from tawaguard import AsyncTawaGuard

app = FastAPI()
tawa = AsyncTawaGuard("YOUR_API_KEY")


@app.post("/chat")
async def chat(message: str):
    scan = await tawa.analyze(message)

    if scan.action in ("block", "hard_block", "soft_block") or scan.suggestion == "block":
        raise HTTPException(status_code=403, detail="Message blocked by content policy.")

    return {"ok": True}


@app.on_event("shutdown")
async def shutdown():
    await tawa.aclose()

API Reference

TawaGuard / AsyncTawaGuard

analyze(text, *, cuid=None, tenant_id=None, policy_overrides=None, language=None) -> ScanResult

Scans a plain prompt and returns the decision. analyze on AsyncTawaGuard is a coroutine.

scan_context / scan_chunk / scan_retrieval / scan_tool_call / scan_tool_output

RAG- and agent-specific scan methods, same ScanResult shape (scan_retrieval returns a per-chunk list plus an aggregate verdict instead), but carry that provenance through to the decision. See the docstrings on each method for details.

AsyncTawaGuard.create(api_key, **kwargs) -> AsyncTawaGuard (classmethod, async)

Constructs a client and verifies the API key against /verify. Raises TawaAPIError if authentication fails.

close() / aclose()

Closes the underlying HTTP connection pool. Both clients support context manager usage (with / async with) which calls this automatically.

TawaAPIError

Attribute Type Description
message str Error description
status_code int | None HTTP status code
retryable bool Whether retrying could succeed

Development

pip install -e ".[dev]"
pytest          # run tests
ruff check .    # lint
pyright         # type-check

License

MIT. See LICENSE. This licence covers the SDK only; use of the Tawa Guard service is governed by its Terms.

Metadata

Release files for tawaguard-sdk 0.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 tawaguard-sdk 0.1.0
File Size Uploaded
tawaguard_sdk-0.1.0.tar.gz 27.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for tawaguard-sdk 0.1.0
File Interpreter ABI Platform
tawaguard_sdk-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 52.5 kB

Release files / tawaguard_sdk-0.1.0.tar.gz

Download URL tawaguard_sdk-0.1.0.tar.gz
Size 27.3 kB
Tags Source
SHA-256 checksum
How to use checksums
288552700fd0dec71d4738ea78a0f69c513c8c1e1e28692ae9bf0b220806c335
BLAKE2b-256 checksum
How to use checksums
67320ed84f41e8bb0c6e22a232aa8dbb70897c2ac1d990689388c7efa5bc23f9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / tawaguard_sdk-0.1.0-py3-none-any.whl

Download URL tawaguard_sdk-0.1.0-py3-none-any.whl
Size 25.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
edb62db5fa2c9b1611a3914a90855abca75e06fc480a4cd5ec3c9ec57e3804a1
BLAKE2b-256 checksum
How to use checksums
d7e351b0250b862c71266cf12deec5dbe9a0078690b2788975ab4b341e32c426
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Release history Release notifications | RSS feed

0.3.1

2 release files

0.3.0

2 release files

0.2.0

2 release files

This release

0.1.0 This release

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