pytest-agent-chaos ⚡🤖
Deterministic chaos engineering and fault-injection plugin for multi-agent LLM workflows, tool-calling pipelines, and state graph runtimes.
💡 Why pytest-agent-chaos?
Most AI testing libraries focus on probabilistic LLM-as-a-judge assertions (like Ragas or DeepEval). While evaluating semantic accuracy is important, real-world multi-agent systems crash at the systems and transport layer:
- ⚡ Mid-Stream Network Drops: LLM providers sever SSE streams mid-generation during peak load or token limits.
- 🛑 429 Rate-Limit Storms: Cascading retries cause agent graphs to enter infinite loops and exhaust step budgets.
- 🧩 Hallucinated & Truncated Tool Payloads: LLMs omit closing brackets or mutate data types, crashing downstream Pydantic parsers.
- 🔁 Cyclic Deadlocks: Multi-agent router graphs get stuck ping-ponging between planner and tool nodes.
pytest-agent-chaos provides deterministic, zero-token fault injection directly inside your pytest suite. Verify agent self-correction, circuit breakers, and fallback routing in CI/CD without spending a penny on API calls.
🏛️ Architecture Overview
pytest-agent-chaos automatically hooks into pytest via the native pytest11 entrypoint and intercepts execution across three distinct layers:
+-----------------------------------------------------------------------------------+
| PYTEST RUNNER |
+-----------------------------------------------------------------------------------+
│ │
▼ [In-Process Zero-Token Testing] ▼ [Out-of-Process Network Chaos]
┌───────────────────────┼───────────────────────┐ +─────────────────────────────────+
▼ ▼ ▼ | L7 Chaos Proxy Sidecar |
+───────────────────+ +───────────────────+ +─────────────+ | (Standalone AsyncIO :8085) |
| Transport Hook | | Tool Corruptor | | State Graph | +─────────────────────────────────+
| (httpx / SSE) | | (Pydantic Schema) | | Auditor | │
+───────────────────+ +───────────────────+ +─────────────+ ▼
│ │ │ • Docker / Pod network drops
▼ ▼ ▼ • Mid-token raw TCP socket slice
• Mid-stream drop (SSE) • JSON truncation • Cycle detect • External API 429/504 backoff
• 429 Retry-After storms• Type schema drift• Step budget • REST Control Plane (/_chaos/v1)
• 504 Gateway timeouts • Extra param ban • Deadlock halts │
│ │ │ │
└───────────────────────┼────────────────┴─────────────────────┘
▼
+────────────────────────────────---------------+
| Agent Resilience Summary Reporter |
| Resilience Index (%) | Injected / Recovered |
+────────────────────────────────---------------+
🚀 Quickstart
1. Installation
pip install pytest-agent-chaos
2. Test SSE Stream Drops & 429 Recovery (Zero Tokens)
import httpx
import pytest
from pytest_agent_chaos import FaultType, stream_chaos_inject
@stream_chaos_inject(
fault=FaultType.SSE_MIDSTREAM_DROP,
trigger_on_call=1,
drop_after_chunks=2,
)
def test_agent_recovers_from_sse_midstream_drop(stream_sandbox):
"""Simulates OpenAI severing an SSE stream after 2 chunks; verifies resilient retry."""
# When your agent calls OpenAI/Anthropic via httpx, Call #1 will trigger RemoteProtocolError.
# On Call #2, the agent retries and completes cleanly.
...
3. Test Tool-Calling Parser Resilience
from pydantic import BaseModel, ConfigDict
import pytest
from pytest_agent_chaos import ChaosSandbox, FaultType, chaos_inject
class ExtractInvoice(BaseModel):
model_config = ConfigDict(extra="forbid")
invoice_id: str
total_amount: float
@chaos_inject(
fault=FaultType.TRUNCATED_JSON_TOOL_CALL,
trigger_on_call=1,
max_graph_steps=5,
)
def test_invoice_extractor_recovers_from_truncated_tool_json(chaos_sandbox: ChaosSandbox):
raw_tool_args = '{"invoice_id": "INV-9021", "total_amount": 450.00}'
# Call 1: Mid-string truncation simulates max_tokens cutoff
corrupted = chaos_sandbox.mutate_tool_payload(raw_tool_args)
assert not corrupted.endswith("}")
# Call 2: Agent self-correction loop emits valid JSON
clean = chaos_sandbox.mutate_tool_payload(raw_tool_args)
validated = ExtractInvoice.model_validate_json(clean)
assert validated.invoice_id == "INV-9021"
assert chaos_sandbox.telemetry.recovery_succeeded
4. Guardrail LangGraph State Machines Against Infinite Loops
import pytest
from pytest_agent_chaos import StateGraphAuditor
from pytest_agent_chaos.exceptions import InfiniteAgentLoopError
def test_multi_agent_graph_loop_guardrail():
auditor = StateGraphAuditor(max_steps=8, detect_cycles=True)
@auditor.wrap_node("planner")
def planner_node(state):
return {"action": "call_tool"}
@auditor.wrap_node("tool_node")
def tool_node(state):
return {"action": "replan"}
# Run the cycle: auditor raises InfiniteAgentLoopError when exceeding max_steps
with pytest.raises(InfiniteAgentLoopError):
state = {}
while True:
state = planner_node(state)
state = tool_node(state)
5. Out-of-Process Chaos Proxy Sidecar (Docker / Kubernetes / FastAPI Testing)
In-memory mocking verifies local Python logic, but real deployed agents run inside Docker containers or Kubernetes pods behind ingress gateways and middleboxes.
pytest-agent-chaos includes a standalone L7 Chaos Reverse Proxy Sidecar that breaks live network connections, drops TCP sockets mid-SSE stream without sending a FIN packet, and injects cascading rate limits across network boundaries:
┌────────────────────────────────────────────────────────────────────────┐
│ Pod / Container Boundary / Local Network │
│ │
│ ┌─────────────────────────┐ HTTP traffic ┌────────────────────────┐ TLS / HTTPS ┌─────────────────────────┐
│ │ Deployed Agent Service │ ───────────────> │ Chaos Proxy Sidecar │ ────────────> │ Upstream LLM Provider │
│ │ (FastAPI / LangGraph) │ │ (:8085) │ │ (api.openai.com) │
│ └─────────────────────────┘ └────────────────────────┘ └─────────────────────────┘
│ ▲ ▲
└───────────────┼────────────────────────────────────────────┼───────────────────────────────────────────────────┘
│ Test Input │ Control Plane (/_chaos/v1/rules)
│ │
┌────────────────────────┐ ┌────────────────────────┐
│ Black-Box Test │ │ Pytest Sidecar Client │
│ (POST /api/v1/run) │ │ (pytest-agent-chaos) │
└────────────────────────┘ └────────────────────────┘
Pytest Integration (chaos_sidecar Fixture)
import pytest
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import create_react_agent
from pytest_agent_chaos.sidecar.client import RemoteChaosClient
def test_deployed_agent_recovers_from_network_chaos(chaos_sidecar: RemoteChaosClient):
# 1. Program chaos rule via proxy control plane REST API
chaos_sidecar.add_rule(
fault_type="rate_limit_429",
trigger_on_call=1,
retry_after=2,
)
# 2. Point deployed container or client at the proxy sidecar
# Environment variable: OPENAI_BASE_URL=http://localhost:8085/v1
llm = ChatOpenAI(
base_url=f"{chaos_sidecar.base_url}/v1",
api_key="sk-live-mock-token",
max_retries=2,
)
agent = create_react_agent(llm, tools=[])
# 3. Execution triggers HTTP 429 on Call 1, retries over TCP, succeeds on Call 2
result = agent.invoke({"messages": [("user", "Summarize report")]})
assert result["messages"][-1].content
# 4. Verify telemetry recorded across the container boundary
telemetry = chaos_sidecar.get_telemetry()
assert telemetry["total_calls"] == 2
assert telemetry["faults_injected"] == 1
assert telemetry["recovery_succeeded"] is True
Sidecar Control Plane API Reference
The sidecar runs alongside your services, exposing control endpoints on /_chaos/v1:
| Endpoint | Method | Purpose |
|---|---|---|
/_chaos/v1/health |
GET |
Healthcheck and readiness probe for container orchestrators. |
/_chaos/v1/rules |
POST |
Program dynamic fault triggers (rate_limit_429, gateway_timeout_504, sse_midstream_drop, latency_jitter). |
/_chaos/v1/rules |
GET |
Inspect currently registered fault rules. |
/_chaos/v1/rules |
DELETE |
Flush all rules and reset proxy to transparent pass-through. |
/_chaos/v1/telemetry |
GET |
Extract cross-container metrics: total_calls, faults_injected, retries_observed, recovery_succeeded. |
📊 Terminal Summary & Chaos Resilience Index
At the end of every test session, pytest-agent-chaos prints an actionable summary table:
======================== Agent Chaos Resilience Summary ========================
Test Case Fault Type Calls Faults Retries Resilience
--------------------------------------------------------------------------------------------------
test_openai_429_backoff rate_limit_429 3 1 2 RECOVERED
test_anthropic_sse_cutoff sse_midstream_drop 2 1 1 RECOVERED
test_invoice_schema_drift schema_type_drift 2 1 1 RECOVERED
test_cyclic_graph_guardrail state_graph_budget 8 1 0 PASSTHROUGH
--------------------------------------------------------------------------------------------------
Chaos Resilience Index: 100.0% | Injected Faults Handled: 3/3 | Total Calls Intercepted: 15
======================================= =======================================
🛠️ Fault Types Supported
| Fault Identifier | Target Layer | Description |
|---|---|---|
RATE_LIMIT_429 |
HTTP Transport | Injects HTTP 429 with configurable Retry-After header. |
GATEWAY_TIMEOUT_504 |
HTTP Transport | Injects HTTP 504 Gateway Timeout from LLM backend. |
CONNECTION_TIMEOUT |
HTTP Transport | Deterministically raises httpx.ConnectTimeout. |
SSE_MIDSTREAM_DROP |
SSE Stream | Drops connection after $N$ chunks with httpx.RemoteProtocolError. |
LATENCY_JITTER |
Transport / Node | Injects synthetic latency spikes in milliseconds. |
TRUNCATED_JSON_TOOL_CALL |
Tool Parser | Slices tool arguments mid-JSON to simulate token cutoffs. |
SCHEMA_TYPE_DRIFT |
Tool Parser | Recursively flips every value to a different type family (numbers → string sentinel, strings → null, booleans → array) to fail schema validation deterministically. |
HALLUCINATED_PARAMS |
Tool Parser | Injects unexpected keys to verify extra="forbid" guardrails. |
⚙️ CLI Options
| Flag | Default | Description |
|---|---|---|
--chaos-seed |
42 |
Random seed for deterministic, reproducible fault ordering. |
--chaos-disable-report |
False |
Disables printing the Agent Chaos Resilience Summary table. |
📌 Versioning
This project is pre-1.0 (0.x) and the API is still settling — expect breaking
changes between minor versions until 1.0. The public surface is exactly what
pytest_agent_chaos.__all__ exports: the pytest fixtures and decorators, the
sandbox classes (ChaosSandbox, StreamChaosSandbox), FaultType /
ChaosProfile / telemetry models, the exception hierarchy, and the sidecar
client/server entry points (RemoteChaosClient, ChaosSidecarServer,
run_chaos_sidecar). Anything reached only via pytest_agent_chaos.interceptors.*
or pytest_agent_chaos.sidecar.server internals, or prefixed with _, is not
part of the contract and can change without notice. See CHANGELOG.md for
what changed between releases.
🧪 Development & Testing
# Clone the repository
git clone https://github.com/Adityaiyer3004/pytest-agent-chaos.git
cd pytest-agent-chaos
# Install dev dependencies
pip install -e ".[dev]"
# Run test suite
pytest
# Run under parallel workers (checks the suite is pytest-xdist safe)
pytest -n auto
# Run with a coverage gate (see [tool.coverage.report] in pyproject.toml)
coverage run --source=src -m pytest
coverage report -m
CI runs the full suite across Python 3.10–3.14, re-runs it under pytest -n auto,
enforces the coverage gate, and separately installs the exact floor versions
declared in pyproject.toml to catch dependency ranges that look fine on paper
but don't actually resolve or work together.
📄 License
Distributed under the terms of the MIT License.
Metadata
Release files for pytest-agent-chaos 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| pytest_agent_chaos-0.1.2.tar.gz | 51.7 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pytest_agent_chaos-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 83.0 kB
Release files / pytest_agent_chaos-0.1.2.tar.gz
| Download URL | pytest_agent_chaos-0.1.2.tar.gz |
|---|---|
| Size | 51.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
36c2f589446b58df7fb222f55a808dbcf0a7235bb12c4da82d4e6effb4afd574
|
|
BLAKE2b-256 checksum How to use checksums |
77338e0df34c32e9ebe8aebde070a6265592caa16797fd563c613fb65d62d61d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 28, 2026.
Transparency logRelease files / pytest_agent_chaos-0.1.2-py3-none-any.whl
| Download URL | pytest_agent_chaos-0.1.2-py3-none-any.whl |
|---|---|
| Size | 31.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5658b42528ac89216c2639363e3c4cbe0c1d1342288c75347f59c864165df675
|
|
BLAKE2b-256 checksum How to use checksums |
3a12b81944661504194d7bfd04dddb4a9f01b7eb2d5838ba75956c1570c1596b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 28, 2026.
Transparency logSecurity for pytest-agent-chaos 0.1.2
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.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for pytest_agent_chaos-0.1.2.tar.gz
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 28, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.