langchain-chaos-middleware 0.1.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
chaos_middleware.py
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import random
|
|
3
|
+
from typing import List, Optional, Any, Type, Callable
|
|
4
|
+
try:
|
|
5
|
+
from typing import TypedDict
|
|
6
|
+
except ImportError:
|
|
7
|
+
from typing_extensions import TypedDict
|
|
8
|
+
|
|
9
|
+
# Import LangChain v1 middleware types
|
|
10
|
+
try:
|
|
11
|
+
from langchain.agents.middleware import (
|
|
12
|
+
AgentMiddleware,
|
|
13
|
+
ModelRequest,
|
|
14
|
+
ModelResponse,
|
|
15
|
+
)
|
|
16
|
+
# ToolCallRequest might be in a different location or named differently
|
|
17
|
+
# We'll handle this gracefully
|
|
18
|
+
try:
|
|
19
|
+
from langchain.agents.middleware import ToolCallRequest
|
|
20
|
+
except ImportError:
|
|
21
|
+
# If ToolCallRequest doesn't exist, we'll use the request object directly
|
|
22
|
+
ToolCallRequest = Any
|
|
23
|
+
except ImportError:
|
|
24
|
+
# Fallback for development/testing without LangChain installed
|
|
25
|
+
class AgentMiddleware:
|
|
26
|
+
pass
|
|
27
|
+
ModelRequest = Any
|
|
28
|
+
ModelResponse = Any
|
|
29
|
+
ToolCallRequest = Any
|
|
30
|
+
|
|
31
|
+
class ChaosConfig(TypedDict):
|
|
32
|
+
"""Configuration for the Chaos Monkey Middleware.
|
|
33
|
+
|
|
34
|
+
Attributes:
|
|
35
|
+
failure_rate: Probability of failure (0.0 to 1.0). Default is 0.1.
|
|
36
|
+
exception_types: List of exception classes to choose from when failing.
|
|
37
|
+
include_tools: List of tool names to target. If None, all tools are targeted.
|
|
38
|
+
exclude_tools: List of tool names to exclude from chaos.
|
|
39
|
+
seed: Random seed for reproducibility.
|
|
40
|
+
safety_key: Environment variable name that must be "true" to enable chaos. Default: "ENABLE_CHAOS".
|
|
41
|
+
"""
|
|
42
|
+
failure_rate: float
|
|
43
|
+
exception_types: List[Type[Exception]]
|
|
44
|
+
include_tools: Optional[List[str]]
|
|
45
|
+
exclude_tools: List[str]
|
|
46
|
+
seed: Optional[int]
|
|
47
|
+
safety_key: str
|
|
48
|
+
|
|
49
|
+
class ChaosMiddleware(AgentMiddleware):
|
|
50
|
+
"""Middleware that injects random failures into tool and model calls.
|
|
51
|
+
|
|
52
|
+
This middleware is designed to test the resilience of an agent by simulating
|
|
53
|
+
network errors, service outages, or other unexpected exceptions.
|
|
54
|
+
|
|
55
|
+
!!!Important!!!
|
|
56
|
+
The middleware will ONLY be active if the environment variable specified
|
|
57
|
+
by `safety_key` (default: "ENABLE_CHAOS") is set to "true".
|
|
58
|
+
"""
|
|
59
|
+
def __init__(self, config: ChaosConfig):
|
|
60
|
+
self.config = config
|
|
61
|
+
self.failure_rate = config.get("failure_rate", 0.1)
|
|
62
|
+
self.exception_types = config.get("exception_types", [])
|
|
63
|
+
self.include_tools = config.get("include_tools", None)
|
|
64
|
+
self.exclude_tools = config.get("exclude_tools", [])
|
|
65
|
+
self.seed = config.get("seed", None)
|
|
66
|
+
self.safety_key = config.get("safety_key", "ENABLE_CHAOS")
|
|
67
|
+
|
|
68
|
+
if self.seed is not None:
|
|
69
|
+
random.seed(self.seed)
|
|
70
|
+
|
|
71
|
+
def wrap_tool_call(
|
|
72
|
+
self,
|
|
73
|
+
request: ToolCallRequest,
|
|
74
|
+
handler: Callable[[ToolCallRequest], Any]
|
|
75
|
+
) -> Any:
|
|
76
|
+
"""Intercepts tool calls and potentially raises an exception.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
request: The ToolCallRequest object containing tool information.
|
|
80
|
+
handler: The next handler in the chain.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
The result of the handler if no exception is raised.
|
|
84
|
+
|
|
85
|
+
Raises:
|
|
86
|
+
Exception: A random exception from `exception_types` if chaos is triggered.
|
|
87
|
+
"""
|
|
88
|
+
# Safety Check
|
|
89
|
+
if os.environ.get(self.safety_key, "").lower() != "true":
|
|
90
|
+
return handler(request)
|
|
91
|
+
|
|
92
|
+
# Extract tool name from request
|
|
93
|
+
# The request object should have a 'tool' attribute with the BaseTool instance
|
|
94
|
+
tool_name = getattr(request, 'tool', None)
|
|
95
|
+
if tool_name is not None:
|
|
96
|
+
tool_name = getattr(tool_name, 'name', str(tool_name))
|
|
97
|
+
|
|
98
|
+
# Target Check
|
|
99
|
+
if tool_name and tool_name in self.exclude_tools:
|
|
100
|
+
return handler(request)
|
|
101
|
+
|
|
102
|
+
if self.include_tools is not None and (not tool_name or tool_name not in self.include_tools):
|
|
103
|
+
return handler(request)
|
|
104
|
+
|
|
105
|
+
# Roll Dice
|
|
106
|
+
if random.random() <= self.failure_rate:
|
|
107
|
+
if self.exception_types:
|
|
108
|
+
# Instantiate the exception class
|
|
109
|
+
raise random.choice(self.exception_types)()
|
|
110
|
+
else:
|
|
111
|
+
raise Exception("Chaos Monkey triggered!")
|
|
112
|
+
|
|
113
|
+
return handler(request)
|
|
114
|
+
|
|
115
|
+
def wrap_model_call(
|
|
116
|
+
self,
|
|
117
|
+
request: ModelRequest,
|
|
118
|
+
handler: Callable[[ModelRequest], ModelResponse]
|
|
119
|
+
) -> ModelResponse:
|
|
120
|
+
"""Intercepts model calls and potentially raises an exception.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
request: The ModelRequest object.
|
|
124
|
+
handler: The next handler in the chain.
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
The ModelResponse if no exception is raised.
|
|
128
|
+
|
|
129
|
+
Raises:
|
|
130
|
+
Exception: A random exception from `exception_types` if chaos is triggered.
|
|
131
|
+
"""
|
|
132
|
+
# Safety Check
|
|
133
|
+
if os.environ.get(self.safety_key, "").lower() != "true":
|
|
134
|
+
return handler(request)
|
|
135
|
+
|
|
136
|
+
# Roll Dice
|
|
137
|
+
if random.random() <= self.failure_rate:
|
|
138
|
+
if self.exception_types:
|
|
139
|
+
# Instantiate the exception class
|
|
140
|
+
raise random.choice(self.exception_types)()
|
|
141
|
+
else:
|
|
142
|
+
raise Exception("Chaos Monkey triggered on model call!")
|
|
143
|
+
|
|
144
|
+
return handler(request)
|
|
145
|
+
|
|
146
|
+
# Custom Exceptions for Chaos
|
|
147
|
+
class RateLimitError(Exception):
|
|
148
|
+
"""Simulated Rate Limit Error"""
|
|
149
|
+
pass
|
|
150
|
+
|
|
151
|
+
class ServiceUnavailableError(Exception):
|
|
152
|
+
"""Simulated Service Unavailable Error"""
|
|
153
|
+
pass
|
|
154
|
+
|
|
155
|
+
# Default Exception Profiles
|
|
156
|
+
NETWORK_ERRORS = [TimeoutError, ConnectionError]
|
|
157
|
+
LLM_ERRORS = [RateLimitError, ServiceUnavailableError]
|
|
158
|
+
CRITICAL_ERRORS = [ValueError, KeyError]
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: langchain-chaos-middleware
|
|
3
|
+
Version: 0.1.4
|
|
4
|
+
Summary: Chaos testing middleware for LangChain agents
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: langchain>=1.0.0
|
|
8
|
+
Requires-Dist: langgraph>=1.0.0
|
|
9
|
+
|
|
10
|
+
# LangChain Chaos Middleware ( Chaos Monkey )
|
|
11
|
+
|
|
12
|
+
A middleware for LangChain agents that intentionally injects failures (exceptions) into tool and model calls to test agent resilience.
|
|
13
|
+
|
|
14
|
+
## Table of Contents
|
|
15
|
+
|
|
16
|
+
- [Features](#features)
|
|
17
|
+
- [Installation](#installation)
|
|
18
|
+
- [Usage](#usage)
|
|
19
|
+
- [Basic Example](#basic-example)
|
|
20
|
+
- [Configuration](#configuration)
|
|
21
|
+
- [Default Exception Profiles](#default-exception-profiles)
|
|
22
|
+
- [Custom Exception Profiles](#custom-exception-profiles)
|
|
23
|
+
- [Understanding Failure Rates](#understanding-failure-rates)
|
|
24
|
+
- [Important Note for Production](#important-note-for-production)
|
|
25
|
+
- [For Developers](#for-developers)
|
|
26
|
+
- [Setting Up the Development Environment](#setting-up-the-development-environment)
|
|
27
|
+
- [Running Tests](#running-tests)
|
|
28
|
+
- [Test Coverage](#test-coverage)
|
|
29
|
+
- [Contributing](#contributing)
|
|
30
|
+
|
|
31
|
+
## Features
|
|
32
|
+
|
|
33
|
+
- **Failure Injection**: Randomly raises exceptions based on a configurable failure rate.
|
|
34
|
+
- **Targeted Chaos**: Include or exclude specific tools from chaos.
|
|
35
|
+
- **Safety Mechanism**: Requires an environment variable (`ENABLE_CHAOS=true`) to be active, preventing accidental production issues.
|
|
36
|
+
- **Customizable Exceptions**: Choose which exceptions to raise (e.g., network errors, rate limits).
|
|
37
|
+
|
|
38
|
+
## Installation
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install langchain-chaos-middleware
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Usage
|
|
45
|
+
|
|
46
|
+
### Basic Example
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
import os
|
|
50
|
+
from langchain.agents import create_agent
|
|
51
|
+
from chaos_middleware import ChaosMiddleware, NETWORK_ERRORS
|
|
52
|
+
|
|
53
|
+
# 1. Enable Chaos (Safety Check)
|
|
54
|
+
os.environ["ENABLE_CHAOS"] = "true"
|
|
55
|
+
|
|
56
|
+
# 2. Configure Middleware
|
|
57
|
+
chaos_config = {
|
|
58
|
+
"failure_rate": 0.2, # 20% chance of failure
|
|
59
|
+
"exception_types": NETWORK_ERRORS,
|
|
60
|
+
"exclude_tools": ["save_memory"], # Don't break critical tools
|
|
61
|
+
}
|
|
62
|
+
chaos = ChaosMiddleware(chaos_config)
|
|
63
|
+
|
|
64
|
+
# 3. Inject into Agent
|
|
65
|
+
agent = create_agent(
|
|
66
|
+
model="gpt-4o",
|
|
67
|
+
tools=[...],
|
|
68
|
+
middleware=[chaos]
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# 4. Run Agent
|
|
72
|
+
# The agent will now experience random network errors!
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Configuration
|
|
76
|
+
|
|
77
|
+
The `ChaosMiddleware` is initialized with a `ChaosConfig` dictionary:
|
|
78
|
+
|
|
79
|
+
| Key | Type | Default | Description |
|
|
80
|
+
| :--- | :--- | :--- | :--- |
|
|
81
|
+
| `failure_rate` | `float` | `0.1` | Probability of failure (0.0 to 1.0). |
|
|
82
|
+
| `exception_types` | `List[Exception]` | `[]` | List of exceptions to randomly choose from. |
|
|
83
|
+
| `include_tools` | `List[str]` | `None` | If set, only these tools will be targeted. |
|
|
84
|
+
| `exclude_tools` | `List[str]` | `[]` | These tools will NEVER fail. |
|
|
85
|
+
| `seed` | `int` | `None` | Random seed for reproducible chaos. |
|
|
86
|
+
| `safety_key` | `str` | `"ENABLE_CHAOS"` | Env var name required to enable the middleware. |
|
|
87
|
+
|
|
88
|
+
## Default Exception Profiles
|
|
89
|
+
|
|
90
|
+
The library provides pre-built lists of exceptions for common scenarios:
|
|
91
|
+
|
|
92
|
+
- `NETWORK_ERRORS`: `[TimeoutError, ConnectionError]`
|
|
93
|
+
- `LLM_ERRORS`: `[RateLimitError, ServiceUnavailableError]`
|
|
94
|
+
- `CRITICAL_ERRORS`: `[ValueError, KeyError]`
|
|
95
|
+
|
|
96
|
+
## Custom Exception Profiles
|
|
97
|
+
|
|
98
|
+
You can define your own lists of exceptions to simulate specific scenarios (e.g., database failures, custom API errors). Just create a list of exception classes and pass it to the `exception_types` configuration:
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
class DatabaseError(Exception):
|
|
102
|
+
pass
|
|
103
|
+
|
|
104
|
+
MY_DB_ERRORS = [DatabaseError]
|
|
105
|
+
|
|
106
|
+
config = {
|
|
107
|
+
"exception_types": MY_DB_ERRORS,
|
|
108
|
+
# ...
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Understanding Failure Rates
|
|
113
|
+
|
|
114
|
+
The `failure_rate` applies to **each individual tool call**, not to the entire agent task. If your agent needs to call multiple tools to complete a task, the overall success rate will be lower than you might expect.
|
|
115
|
+
|
|
116
|
+
**Example**: With a 20% failure rate per tool:
|
|
117
|
+
- **Single tool call**: 80% chance of success
|
|
118
|
+
- **Two tool calls**: 80% × 80% = **64% chance of success**
|
|
119
|
+
- **Three tool calls**: 80% × 80% × 80% = **51% chance of success**
|
|
120
|
+
|
|
121
|
+
This means that even with a "low" 20% failure rate, an agent that needs to call 3 tools has nearly a 50% chance of experiencing at least one failure during the task.
|
|
122
|
+
|
|
123
|
+
**Tip**: Start with a low failure rate (e.g., 10-20%) and observe how it affects your agent's overall success rate before increasing it.
|
|
124
|
+
|
|
125
|
+
## Important Note for Production
|
|
126
|
+
|
|
127
|
+
To prevent accidental chaos in production, the middleware checks for an environment variable (default: `ENABLE_CHAOS`). If this variable is not set to `"true"`, the middleware acts as a pass-through and does nothing.
|
|
128
|
+
|
|
129
|
+
## For Developers
|
|
130
|
+
|
|
131
|
+
### Setting Up the Development Environment
|
|
132
|
+
|
|
133
|
+
1. **Clone the repository**:
|
|
134
|
+
```bash
|
|
135
|
+
git clone https://github.com/iroy2000/langchain-chaos-middleware.git
|
|
136
|
+
cd langchain-chaos-middleware
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
2. **Create a virtual environment**:
|
|
140
|
+
```bash
|
|
141
|
+
python3 -m venv .venv
|
|
142
|
+
source .venv/bin/activate # On Windows: .venv\Scripts\activate
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
3. **Install dependencies**:
|
|
146
|
+
```bash
|
|
147
|
+
# Install the package dependencies
|
|
148
|
+
.venv/bin/pip install langchain langgraph
|
|
149
|
+
|
|
150
|
+
# Install test dependencies
|
|
151
|
+
.venv/bin/pip install python-dotenv langchain-openai
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Running Tests
|
|
155
|
+
|
|
156
|
+
The project includes unit tests to verify the middleware functionality.
|
|
157
|
+
|
|
158
|
+
**Run all tests**:
|
|
159
|
+
```bash
|
|
160
|
+
.venv/bin/python -m unittest discover tests
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**Run only unit tests**:
|
|
164
|
+
```bash
|
|
165
|
+
.venv/bin/python -m unittest tests.test_unit
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
**Run integration tests** (requires OpenAI API key):
|
|
169
|
+
```bash
|
|
170
|
+
# Create a .env file with your OpenAI API key
|
|
171
|
+
echo "OPENAI_API_KEY=your-key-here" > .env
|
|
172
|
+
|
|
173
|
+
# Run the integration test
|
|
174
|
+
.venv/bin/python tests/test_integration.py
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Test Coverage
|
|
178
|
+
|
|
179
|
+
The test suite includes:
|
|
180
|
+
- **Safety mechanism tests**: Verifies chaos is disabled without the safety key
|
|
181
|
+
- **Failure injection tests**: Confirms failures are injected at the configured rate
|
|
182
|
+
- **Tool filtering tests**: Validates include/exclude tool lists work correctly
|
|
183
|
+
- **Model call tests**: Ensures chaos applies to model calls as well
|
|
184
|
+
|
|
185
|
+
### Contributing
|
|
186
|
+
|
|
187
|
+
Contributions are welcome! Please ensure all tests pass before submitting a pull request:
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
.venv/bin/python -m unittest discover tests
|
|
191
|
+
```
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
chaos_middleware.py,sha256=jdpWXc7-Uzr0-icueqICsq6Ns0JSdPNTUJUvGiO3Emg,5589
|
|
2
|
+
langchain_chaos_middleware-0.1.4.dist-info/METADATA,sha256=VMAVfAYikBVRkV3uDImilGuLVnyhTaCQ8XuZaqRLFc8,6127
|
|
3
|
+
langchain_chaos_middleware-0.1.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
4
|
+
langchain_chaos_middleware-0.1.4.dist-info/top_level.txt,sha256=52Tu5Ki3CseAj-exVyTM2tIDqBejIWQnJBHRgnrkyRU,17
|
|
5
|
+
langchain_chaos_middleware-0.1.4.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
chaos_middleware
|