agentguard-llm 0.1.0__tar.gz

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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 AgentGuard Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,370 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentguard-llm
3
+ Version: 0.1.0
4
+ Summary: Production-grade fault tolerance for AI agents — circuit breakers, LLM-aware retry, idempotency, and loop detection
5
+ Home-page: https://github.com/agentguard-ai/agentguard
6
+ Author: AgentGuard Contributors
7
+ Project-URL: Bug Reports, https://github.com/agentguard-ai/agentguard/issues
8
+ Project-URL: Source, https://github.com/agentguard-ai/agentguard
9
+ Keywords: ai agents,llm,fault tolerance,circuit breaker,retry,idempotency,agent reliability,ai production,langchain,autogen,crewai,agent failure,llm retry
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.8
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
21
+ Requires-Python: >=3.8
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Dynamic: author
25
+ Dynamic: classifier
26
+ Dynamic: description
27
+ Dynamic: description-content-type
28
+ Dynamic: home-page
29
+ Dynamic: keywords
30
+ Dynamic: license-file
31
+ Dynamic: project-url
32
+ Dynamic: requires-python
33
+ Dynamic: summary
34
+
35
+ # agentguard — Production-Grade Fault Tolerance for AI Agents
36
+
37
+ [![PyPI version](https://badge.fury.io/py/agentguard.svg)](https://pypi.org/project/agentguard/)
38
+ [![Python Versions](https://img.shields.io/pypi/pyversions/agentguard.svg)](https://pypi.org/project/agentguard/)
39
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
40
+
41
+ **agentguard** is a production-ready Python library that adds circuit breakers, LLM-aware retry logic, idempotency, loop detection, and timeout enforcement to any AI agent or LLM pipeline.
42
+
43
+ > AI agents fail at 91%+ rates in production. agentguard stops that.
44
+
45
+ ---
46
+
47
+ ## The Problem
48
+
49
+ AI agents built with LangChain, AutoGen, CrewAI, or custom LLM pipelines fail catastrophically in production due to:
50
+
51
+ - **Infinite loops** — agents repeat the same tool calls indefinitely
52
+ - **Silent failures** — LLM errors swallowed without retry or alerting
53
+ - **Duplicate actions** — the same expensive LLM call fires multiple times
54
+ - **Rate limit crashes** — no intelligent backoff for 429/503 errors
55
+ - **Token limit blindness** — agents don't know when to stop or summarize
56
+ - **No circuit breaking** — one bad model call cascades into total failure
57
+
58
+ Existing tools like `tenacity`, LangGraph, and CrewAI are not LLM-aware and don't address agent-specific failure modes.
59
+
60
+ ---
61
+
62
+ ## Features
63
+
64
+ - **Circuit Breaker** — Automatically opens after N failures, protecting downstream LLM APIs from cascading overload
65
+ - **LLM-Aware Retry** — Classifies errors (rate limit, token limit, provider outage, hallucinated tool call) and applies appropriate backoff
66
+ - **Idempotency** — Caches results by key to prevent duplicate expensive LLM executions
67
+ - **Loop Detection** — Detects and halts infinite agent action loops before they run up your API bill
68
+ - **Timeout Enforcement** — Hard timeouts on any agent step, with clean error propagation
69
+ - **Zero Dependencies** — Pure Python standard library only; works with any LLM framework
70
+ - **Full Observability** — Built-in stats, logging at every layer, structured error types
71
+
72
+ ---
73
+
74
+ ## Installation
75
+
76
+ ```bash
77
+ pip install agentguard
78
+ ```
79
+
80
+ ---
81
+
82
+ ## Quick Start
83
+
84
+ ### GuardedAgent — Full Protection in One Wrapper
85
+
86
+ ```python
87
+ from agentguard import GuardedAgent
88
+
89
+ agent = GuardedAgent(
90
+ name="my_llm_agent",
91
+ max_retries=3,
92
+ circuit_threshold=5,
93
+ timeout=30.0,
94
+ loop_detection=True,
95
+ max_repeated_actions=3,
96
+ )
97
+
98
+ def call_llm(prompt: str) -> str:
99
+ # Your actual LLM call here (OpenAI, Anthropic, etc.)
100
+ return f"Response to: {prompt}"
101
+
102
+ result = agent.run(call_llm, "What is the capital of France?", action_label="llm_call")
103
+ print(result)
104
+ print(agent.get_stats())
105
+ # {'name': 'my_llm_agent', 'total_calls': 1, 'total_failures': 0, 'circuit': {...}}
106
+ ```
107
+
108
+ ### Circuit Breaker — Standalone
109
+
110
+ ```python
111
+ from agentguard import CircuitBreaker
112
+
113
+ cb = CircuitBreaker(failure_threshold=3, recovery_timeout=60.0, name="openai")
114
+
115
+ try:
116
+ response = cb.call(call_llm, "Hello")
117
+ except Exception as e:
118
+ print(f"Protected from cascading failure: {e}")
119
+
120
+ print(cb.get_stats())
121
+ # {'name': 'openai', 'state': 'closed', 'failure_count': 0, ...}
122
+ ```
123
+
124
+ ### LLM-Aware Retry — Decorator Style
125
+
126
+ ```python
127
+ from agentguard import llm_retry
128
+
129
+ @llm_retry(max_attempts=3)
130
+ def my_agent_step(query: str) -> str:
131
+ # Automatically retries on rate limits (429), provider outages (503)
132
+ # Stops immediately on token limit errors (non-retryable)
133
+ return call_llm(query)
134
+
135
+ result = my_agent_step("Summarize this document")
136
+ ```
137
+
138
+ ### LLM-Aware Retry — Programmatic
139
+
140
+ ```python
141
+ from agentguard import LLMRetry, FailureClassifier
142
+
143
+ retry = LLMRetry(max_attempts=5, on_retry=lambda attempt, ftype, err: print(f"Retry {attempt}: {ftype}"))
144
+ result = retry.execute(call_llm, "Hello")
145
+ ```
146
+
147
+ ### Idempotency — Prevent Duplicate Executions
148
+
149
+ ```python
150
+ from agentguard import IdempotentAgent
151
+
152
+ agent = IdempotentAgent(ttl=3600.0)
153
+
154
+ # First call executes the function
155
+ result1 = agent.run(call_llm, "Summarize report", idempotency_key="report-summary-v1")
156
+
157
+ # Second call with same key returns cached result instantly
158
+ result2 = agent.run(call_llm, "Summarize report", idempotency_key="report-summary-v1")
159
+
160
+ assert result1 == result2 # True — function only ran once
161
+ ```
162
+
163
+ ### Failure Classifier — Understand What Went Wrong
164
+
165
+ ```python
166
+ from agentguard import FailureClassifier, FailureType
167
+
168
+ fc = FailureClassifier()
169
+
170
+ err = Exception("429 Too Many Requests: rate limit exceeded")
171
+ ftype = fc.classify(err)
172
+ # FailureType.RATE_LIMIT
173
+
174
+ print(fc.is_retryable(ftype)) # True
175
+ print(fc.get_retry_delay(ftype, attempt=1)) # 10.0 seconds
176
+ ```
177
+
178
+ ---
179
+
180
+ ## Architecture
181
+
182
+ ```
183
+ agentguard/
184
+ ├── agent_wrapper.py # GuardedAgent — orchestrates all protections
185
+ ├── circuit_breaker.py # CircuitBreaker — CLOSED/OPEN/HALF_OPEN state machine
186
+ ├── retry.py # LLMRetry + llm_retry decorator — intelligent backoff
187
+ ├── idempotency.py # IdempotentAgent + IdempotencyStore — result caching
188
+ ├── failure_classifier.py # FailureClassifier — LLM error pattern recognition
189
+ └── exceptions.py # Typed exception hierarchy
190
+ ```
191
+
192
+ ### Data Flow
193
+
194
+ ```
195
+ Agent call
196
+ │
197
+ ├─► Loop Detector (infinite loop check)
198
+ │
199
+ ├─► Circuit Breaker (OPEN? → reject immediately)
200
+ │
201
+ ├─► Idempotency Store (seen this key? → return cached)
202
+ │
203
+ ├─► LLM Retry (classify failure → smart backoff → retry)
204
+ │
205
+ └─► Timeout Thread (hard deadline enforcement)
206
+ ```
207
+
208
+ ### Failure Classification Logic
209
+
210
+ | Error Pattern | Classified As | Retryable | Backoff |
211
+ |---|---|---|---|
212
+ | `429`, `rate limit`, `quota exceeded` | RATE_LIMIT | Yes | Exponential (5s base, max 60s) |
213
+ | `503`, `service unavailable`, `overloaded` | PROVIDER_OUTAGE | Yes | Exponential (10s base, max 120s) |
214
+ | `context length`, `token limit` | TOKEN_LIMIT | No | — |
215
+ | `tool not found`, `invalid tool` | HALLUCINATED_TOOL_CALL | No | — |
216
+ | anything else | UNKNOWN | Yes | Exponential (1s base, max 30s) |
217
+
218
+ ---
219
+
220
+ ## API Reference
221
+
222
+ ### `GuardedAgent`
223
+
224
+ ```python
225
+ GuardedAgent(
226
+ name: str = "agent",
227
+ max_retries: int = 3,
228
+ circuit_threshold: int = 5,
229
+ circuit_recovery: float = 60.0,
230
+ timeout: Optional[float] = None,
231
+ loop_detection: bool = True,
232
+ max_repeated_actions: int = 3,
233
+ idempotency_ttl: float = 3600.0,
234
+ enable_idempotency: bool = True,
235
+ )
236
+ ```
237
+
238
+ **Methods:**
239
+ - `run(func, *args, action_label=None, idempotency_key=None, **kwargs)` — Execute with all protections
240
+ - `get_stats()` — Returns dict with call counts, failure counts, circuit state
241
+ - `reset_loop_detector()` — Clear the loop detection history
242
+
243
+ ### `CircuitBreaker`
244
+
245
+ ```python
246
+ CircuitBreaker(
247
+ failure_threshold: int = 5,
248
+ recovery_timeout: float = 60.0,
249
+ half_open_max_calls: int = 1,
250
+ name: str = "default"
251
+ )
252
+ ```
253
+
254
+ **Methods:**
255
+ - `call(func, *args, **kwargs)` — Protected function call
256
+ - `get_stats()` — Returns state, failure count, last failure time
257
+
258
+ ### `LLMRetry`
259
+
260
+ ```python
261
+ LLMRetry(
262
+ max_attempts: int = 3,
263
+ classifier: Optional[FailureClassifier] = None,
264
+ on_retry: Optional[Callable] = None,
265
+ )
266
+ ```
267
+
268
+ **Methods:**
269
+ - `execute(func, *args, **kwargs)` — Execute with retry logic
270
+
271
+ ### `llm_retry` decorator
272
+
273
+ ```python
274
+ @llm_retry(max_attempts=3, classifier=None)
275
+ def my_func(): ...
276
+ ```
277
+
278
+ ### `IdempotentAgent`
279
+
280
+ ```python
281
+ IdempotentAgent(store=None, ttl=3600.0)
282
+ ```
283
+
284
+ **Methods:**
285
+ - `run(func, *args, idempotency_key=None, **kwargs)` — Execute with deduplication
286
+
287
+ ### `FailureClassifier`
288
+
289
+ **Methods:**
290
+ - `classify(error: Exception) -> FailureType`
291
+ - `is_retryable(failure_type: FailureType) -> bool`
292
+ - `get_retry_delay(failure_type: FailureType, attempt: int) -> float`
293
+
294
+ ### Exceptions
295
+
296
+ | Exception | When Raised |
297
+ |---|---|
298
+ | `AgentGuardError` | Base class for all agentguard errors |
299
+ | `CircuitOpenError` | Circuit breaker is OPEN, call rejected |
300
+ | `MaxRetriesExceededError` | All retry attempts exhausted |
301
+ | `IdempotencyError` | Idempotency store conflict |
302
+ | `AgentTimeoutError` | Agent exceeded timeout limit |
303
+
304
+ ---
305
+
306
+ ## Real-World Integration Examples
307
+
308
+ ### With OpenAI
309
+
310
+ ```python
311
+ import openai
312
+ from agentguard import GuardedAgent
313
+
314
+ agent = GuardedAgent(name="openai-agent", max_retries=3, timeout=30.0)
315
+
316
+ def gpt_call(prompt: str) -> str:
317
+ response = openai.chat.completions.create(
318
+ model="gpt-4",
319
+ messages=[{"role": "user", "content": prompt}]
320
+ )
321
+ return response.choices[0].message.content
322
+
323
+ result = agent.run(gpt_call, "Explain quantum computing", action_label="gpt_call")
324
+ ```
325
+
326
+ ### With LangChain
327
+
328
+ ```python
329
+ from agentguard import llm_retry
330
+
331
+ @llm_retry(max_attempts=3)
332
+ def run_chain(input_text: str):
333
+ return my_langchain_chain.invoke({"input": input_text})
334
+ ```
335
+
336
+ ### With CrewAI / AutoGen
337
+
338
+ ```python
339
+ from agentguard import GuardedAgent
340
+
341
+ guard = GuardedAgent(name="crew-agent", loop_detection=True, max_repeated_actions=5)
342
+
343
+ # Wrap any crew task execution
344
+ result = guard.run(crew.kickoff, action_label="crew_task")
345
+ ```
346
+
347
+ ---
348
+
349
+ ## Contributing
350
+
351
+ Contributions are welcome. Please open an issue first to discuss what you would like to change.
352
+
353
+ 1. Fork the repository
354
+ 2. Create your feature branch (`git checkout -b feature/your-feature`)
355
+ 3. Run tests: `pytest tests/ -v`
356
+ 4. Submit a pull request
357
+
358
+ ---
359
+
360
+ ## License
361
+
362
+ MIT License. See [LICENSE](LICENSE) for details.
363
+
364
+ ---
365
+
366
+ ## Links
367
+
368
+ - **PyPI**: https://pypi.org/project/agentguard/
369
+ - **GitHub**: https://github.com/agentguard-ai/agentguard
370
+ - **Issues**: https://github.com/agentguard-ai/agentguard/issues