Skip to main content

AgentGauntlet

Every agent framework ships with a false sense of safety: pass the eval once, ship it, forget it. Real agents run in a world where context gets dropped, tools time out, and APIs return garbage — continuously, not once. AgentGauntlet treats that as the default assumption, not an edge case, and gives you two ways to watch it happen to your own agent.

Two ways to run it

In-process (Python agents on requests/httpx) — LangGraph, CrewAI, AutoGen, or any custom Python agent that calls the OpenAI/Anthropic SDKs:

import agentgauntlet
agentgauntlet.init(probability=0.1, frameworks=["openai", "requests"])

Two lines, before you build the graph/crew/agent. No other code changes.

Proxy (any agent, any language) — OpenClaw, Hermes Agent, or literally anything with an OPENAI_BASE_URL / ANTHROPIC_BASE_URL override, which is most agent runtimes regardless of what they're written in:

agentgauntlet proxy --upstream https://api.openai.com --port 8888
export OPENAI_BASE_URL=http://localhost:8888/v1
your-agent-here   # unmodified, any language

The proxy is the honest way to make good on "run this against whatever agent you have" — the in-process patch only ever reaches Python code in the same interpreter, so a Node.js or Go agent is invisible to it no matter how the hooks are written.

Injectors

Injector Targets Sabotage
Amnesia LLM calls drops 10-30% of history, preserving the system prompt and current turn
Distractor LLM calls splices a contradictory instruction into the system prompt or the latest user turn
Gaslighter LLM/tool calls simulated timeout, 429, or 503
Mutator LLM/tool responses per-field probability of flipping booleans, shifting numbers, mangling keys — in proxy mode this includes corrupting tool_call arguments the model itself returned

In proxy mode all four compete in a single pool on every call, since everything reaching the proxy is presumed to be an LLM API call. In-process mode splits them: Amnesia/Distractor target LLM calls, Gaslighter/Mutator target tool calls. Either way, one intercepted call triggers at most one event — the blast radius is one weighted random draw per call, not an independent roll per injector.

Measuring what actually matters: task success, not process survival

with agentgauntlet.run():
    result = my_agent(user_query)
    if result.balance == expected_balance:
        agentgauntlet.mark_success()
    else:
        agentgauntlet.mark_failure("wrong balance reported")

If you never call mark_success/mark_failure, AgentGauntlet falls back to crash-detection and labels the score (unverified) — a much weaker signal, since an agent can process corrupted data, produce a wrong answer, and exit cleanly. examples/basic_agent.py --chaos reproduces exactly that: the Mutator corrupts an account balance, the agent reports the wrong number, nothing crashes, and the scorecard still shows a failure because the harness checks the actual answer.

🔥 CHAOS EVENT: MUTATOR 🧬 corrupted payload <- http://127.0.0.1:8931/tools/fetch_balance
...
        AgentGauntlet Post-Mortem Scorecard
┌────────────────────────────────┬──────────────────────────────────────────┐
│ Total Chaos Events Injected     │ 3                                        │
│ Task Outcome                    │ ❌ failure (reported 12505.0 vs 1250.5)   │
│ Resilience Score                │ 0%                                       │
└────────────────────────────────┴──────────────────────────────────────────┘

Configuration

agentgauntlet.init(
    probability=0.1,                      # shorthand: same weight for all four injectors
    blast_radius={"amnesia": 0.1, "distractor": 0.05, "gaslighter": 0.15, "mutator": 0.1},
    frameworks=["openai", "requests"],    # "httpx" also covers httpx.AsyncClient
    injectors=["amnesia", "gaslighter"],  # optional allow-list, default = all four
    targets=["api.mytools.com"],          # optional: restrict Gaslighter/Mutator to matching URLs
    seed=7,                               # reproducible runs
    timeout_range=(0, 30),                # Gaslighter's simulated timeout sleep, in seconds
    amnesia_strategy="random",            # or "oldest_first" for deterministic degradation
    mutation_rates={"boolean_flip": 0.5, "numeric_shift": 0.5, "key_mangle": 0.2},
)
agentgauntlet proxy --upstream https://api.openai.com --port 8888 \
    --probability 0.15 --seed 7 --timeout-min 0 --timeout-max 30

Values above ~0.25 on --probability mean chaos on essentially every call through the proxy (all four injectors share one pool there) — a legitimate "maximum chaos" demo setting, not a bug, but worth knowing before you crank it to 1.0 for a screenshot and wonder why every single call got hit.

How classification works, and its limits

A request body shaped like {"messages": [...]} or containing a system key is treated as an LLM call; anything else is a tool call, restricted to targets if you set one. This is a payload-shape heuristic, not real provider/client identification — it can misclassify an unrelated API that happens to send a messages field.

The proxy mode has its own, different limit: it only sees traffic an agent is explicitly configured to send through it (the base_url override pattern). It does not do transparent HTTPS interception via a system HTTP_PROXY

  • generated CA certificate — that would require installing a locally-trusted root cert before anything works, which is exactly the setup friction that kills a "point it and go" first impression. Real, stated limitation, not a hidden one.

Known limitations (being upfront about scope)

  • In-process mode is "any Python code on requests/httpx," not literally arbitrary tool mechanisms — subprocess tools, database drivers, and browser automation aren't touched, since there's no HTTP call to intercept in-process.
  • OpenClaw and Hermes Agent are standalone runtimes, not Python libraries — the in-process patch cannot reach them at all; only proxy mode does, and only for their LLM API traffic specifically, not their internal tool/skill execution.
  • No integration tests against LangGraph/CrewAI/AutoGen/OpenClaw/Hermes yet — this release has unit tests for the injectors and blast-radius logic (tests/) and a hand-run demo against a local mock server (examples/), not automated tests against real third-party frameworks.
  • Crash attribution (sys.excepthook) blames whichever chaos event fired most recently before an unhandled exception — a heuristic, not a causal trace. agentgauntlet.run() + mark_success/mark_failure avoids needing it when the caller can check task correctness directly.

See examples/basic_agent.py (a real two-step tool-calling loop against a local mock LLM server, examples/mock_llm_server.py) for a runnable before/after demo, and tests/test_injectors.py for the injector and blast-radius unit tests.

Download files

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

Source Distribution

agentgauntlet-0.2.0.tar.gz (23.1 kB view details)

Uploaded Source

Built Distribution

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

agentgauntlet-0.2.0-py3-none-any.whl (22.3 kB view details)

Uploaded Python 3

File details

Details for the file agentgauntlet-0.2.0.tar.gz.

File metadata

  • Download URL: agentgauntlet-0.2.0.tar.gz
  • Upload date:
  • Size: 23.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for agentgauntlet-0.2.0.tar.gz
Algorithm Hash digest
SHA256 8e72c44bf4d7a305eb6e8d65c594f35ac5d65e4a8a3b3ff9638afe14347dc0b6
MD5 26cbf9c2d066ed97922eaacf594fae07
BLAKE2b-256 406044d909068eefa4357b5e22eb8ea775c15923b123dd34767eb3fbee92ed48

See more details on using hashes here.

Provenance

The following attestation bundles were made for agentgauntlet-0.2.0.tar.gz:

Publisher: python-publish.yml on Sub2mval/AgentGauntlet

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file agentgauntlet-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: agentgauntlet-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 22.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for agentgauntlet-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 68dd9d9ab014a085fad5ffce51e8cdab89cc259b7a609c0ed3a9efdaac0eb4dd
MD5 70c69554bff23cdd38aeb6faecd9e3c7
BLAKE2b-256 935ab6372531c8e41eb0df176557a550180294b17122380b25cdc3c71627f138

See more details on using hashes here.

Provenance

The following attestation bundles were made for agentgauntlet-0.2.0-py3-none-any.whl:

Publisher: python-publish.yml on Sub2mval/AgentGauntlet

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

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