agentfootprint 2.8.3 → 2.10.0
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.
- package/dist/adapters/observability/otel.js +294 -0
- package/dist/adapters/observability/otel.js.map +1 -0
- package/dist/esm/adapters/observability/otel.js +290 -0
- package/dist/esm/adapters/observability/otel.js.map +1 -0
- package/dist/esm/observability-providers.js +9 -3
- package/dist/esm/observability-providers.js.map +1 -1
- package/dist/esm/resilience/index.js +1 -0
- package/dist/esm/resilience/index.js.map +1 -1
- package/dist/esm/resilience/withCircuitBreaker.js +219 -0
- package/dist/esm/resilience/withCircuitBreaker.js.map +1 -0
- package/dist/observability-providers.js +11 -4
- package/dist/observability-providers.js.map +1 -1
- package/dist/resilience/index.js +4 -1
- package/dist/resilience/index.js.map +1 -1
- package/dist/resilience/withCircuitBreaker.js +224 -0
- package/dist/resilience/withCircuitBreaker.js.map +1 -0
- package/dist/types/adapters/observability/otel.d.ts +115 -0
- package/dist/types/adapters/observability/otel.d.ts.map +1 -0
- package/dist/types/observability-providers.d.ts +9 -3
- package/dist/types/observability-providers.d.ts.map +1 -1
- package/dist/types/resilience/index.d.ts +1 -0
- package/dist/types/resilience/index.d.ts.map +1 -1
- package/dist/types/resilience/withCircuitBreaker.d.ts +104 -0
- package/dist/types/resilience/withCircuitBreaker.d.ts.map +1 -0
- package/package.json +5 -1
|
@@ -18,4 +18,5 @@
|
|
|
18
18
|
export { withRetry, type WithRetryOptions } from './withRetry.js';
|
|
19
19
|
export { withFallback, type WithFallbackOptions } from './withFallback.js';
|
|
20
20
|
export { fallbackProvider, type FallbackProviderOptions } from './fallbackProvider.js';
|
|
21
|
+
export { withCircuitBreaker, CircuitOpenError, type WithCircuitBreakerOptions, type CircuitState, } from './withCircuitBreaker.js';
|
|
21
22
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/resilience/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/resilience/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AACvF,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,KAAK,yBAAyB,EAC9B,KAAK,YAAY,GAClB,MAAM,yBAAyB,CAAC"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* withCircuitBreaker — provider decorator that fails fast after N
|
|
3
|
+
* consecutive failures.
|
|
4
|
+
*
|
|
5
|
+
* Pattern: Circuit Breaker (Nygard, *Release It!*) — wraps an
|
|
6
|
+
* `LLMProvider` and tracks consecutive failures. After
|
|
7
|
+
* `failureThreshold` failures, the breaker OPENS and
|
|
8
|
+
* rejects all calls without invoking the wrapped provider.
|
|
9
|
+
* After `cooldownMs`, the breaker enters HALF-OPEN and
|
|
10
|
+
* allows probe calls; success closes the breaker, failure
|
|
11
|
+
* re-opens it.
|
|
12
|
+
*
|
|
13
|
+
* Role: Outer ring (Hexagonal). Composes with `withRetry` and
|
|
14
|
+
* `withFallback`:
|
|
15
|
+
*
|
|
16
|
+
* ```
|
|
17
|
+
* withFallback(
|
|
18
|
+
* withCircuitBreaker(anthropic(...)), // ← stop hammering on outage
|
|
19
|
+
* withCircuitBreaker(openai(...)),
|
|
20
|
+
* )
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* When Anthropic 503s for the 5th time, the breaker opens
|
|
24
|
+
* and `complete()` throws `CircuitOpenError` immediately —
|
|
25
|
+
* no network round-trip — which `withFallback` then
|
|
26
|
+
* catches and routes to OpenAI. After 30 seconds the
|
|
27
|
+
* breaker probes Anthropic with a single call; if it
|
|
28
|
+
* succeeds, normal operation resumes.
|
|
29
|
+
*
|
|
30
|
+
* Why a circuit breaker on top of `withRetry`?
|
|
31
|
+
* - `withRetry` keeps hammering one provider with exponential
|
|
32
|
+
* backoff — it doesn't know the vendor is down.
|
|
33
|
+
* - During a multi-minute Anthropic outage, every request still
|
|
34
|
+
* burns 3 retries + backoff = ~3 sec of latency before failing
|
|
35
|
+
* to the fallback. Multiplied by your QPS, that's a lot of
|
|
36
|
+
* wasted time + tokens (some retries DO get billed).
|
|
37
|
+
* - The breaker says: "we just saw 5 failures in a row; stop
|
|
38
|
+
* calling for 30 seconds." Subsequent requests fail in <1ms,
|
|
39
|
+
* `withFallback` routes immediately to OpenAI.
|
|
40
|
+
*
|
|
41
|
+
* Three states:
|
|
42
|
+
*
|
|
43
|
+
* CLOSED ──[ N consecutive failures ]──► OPEN
|
|
44
|
+
* ▲ │
|
|
45
|
+
* │ │ [cooldownMs elapsed]
|
|
46
|
+
* │ ▼
|
|
47
|
+
* └──[ M probe successes ]──── HALF-OPEN
|
|
48
|
+
*
|
|
49
|
+
* HALF-OPEN ──[ probe failure ]──► OPEN (cooldown restarts)
|
|
50
|
+
*
|
|
51
|
+
* `stream()` is decorated identically. `name`/`flush`/`stop` pass
|
|
52
|
+
* through unchanged (the consumer's existing observability hooks
|
|
53
|
+
* still see the underlying provider's identity).
|
|
54
|
+
*/
|
|
55
|
+
import type { LLMProvider } from '../adapters/types.js';
|
|
56
|
+
export interface WithCircuitBreakerOptions {
|
|
57
|
+
/** Consecutive failures before the breaker OPENS. Default 5. */
|
|
58
|
+
readonly failureThreshold?: number;
|
|
59
|
+
/** How long the breaker stays OPEN before probing. Default 30s. */
|
|
60
|
+
readonly cooldownMs?: number;
|
|
61
|
+
/** Successes required in HALF-OPEN to fully CLOSE. Default 2. */
|
|
62
|
+
readonly halfOpenSuccessThreshold?: number;
|
|
63
|
+
/**
|
|
64
|
+
* Predicate — does this error count toward the threshold? Default:
|
|
65
|
+
* everything except AbortError counts. Override to ignore client
|
|
66
|
+
* errors (e.g., 4xx) so a malformed request doesn't trip the
|
|
67
|
+
* breaker for everyone.
|
|
68
|
+
*/
|
|
69
|
+
readonly shouldCount?: (error: unknown) => boolean;
|
|
70
|
+
/** Hook invoked on every state transition. Useful for emitting
|
|
71
|
+
* `agentfootprint.resilience.circuit_state_changed`. */
|
|
72
|
+
readonly onStateChange?: (state: CircuitState, reason: string) => void;
|
|
73
|
+
}
|
|
74
|
+
export type CircuitState = 'closed' | 'open' | 'half-open';
|
|
75
|
+
/**
|
|
76
|
+
* Thrown by the wrapped provider when the breaker is OPEN. Carries
|
|
77
|
+
* the underlying root-cause error from the most recent failure so
|
|
78
|
+
* consumers can observe what tripped the breaker.
|
|
79
|
+
*/
|
|
80
|
+
export declare class CircuitOpenError extends Error {
|
|
81
|
+
readonly code: "ERR_CIRCUIT_OPEN";
|
|
82
|
+
/** The error that tripped the breaker (or the most recent failure
|
|
83
|
+
* during HALF-OPEN that re-opened it). */
|
|
84
|
+
readonly cause: unknown;
|
|
85
|
+
/** Wall-clock timestamp at which the breaker may next probe. */
|
|
86
|
+
readonly retryAfter: number;
|
|
87
|
+
constructor(providerName: string, cause: unknown, retryAfter: number);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Wrap a provider with a circuit breaker.
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```ts
|
|
94
|
+
* import { anthropic, openai } from 'agentfootprint/llm-providers';
|
|
95
|
+
* import { withCircuitBreaker, withFallback } from 'agentfootprint/resilience';
|
|
96
|
+
*
|
|
97
|
+
* const provider = withFallback(
|
|
98
|
+
* withCircuitBreaker(anthropic({ apiKey }), { failureThreshold: 5, cooldownMs: 30_000 }),
|
|
99
|
+
* withCircuitBreaker(openai({ apiKey })),
|
|
100
|
+
* );
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
export declare function withCircuitBreaker(inner: LLMProvider, options?: WithCircuitBreakerOptions): LLMProvider;
|
|
104
|
+
//# sourceMappingURL=withCircuitBreaker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"withCircuitBreaker.d.ts","sourceRoot":"","sources":["../../../src/resilience/withCircuitBreaker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AAEH,OAAO,KAAK,EAAY,WAAW,EAA2B,MAAM,sBAAsB,CAAC;AAI3F,MAAM,WAAW,yBAAyB;IACxC,gEAAgE;IAChE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,mEAAmE;IACnE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,iEAAiE;IACjE,QAAQ,CAAC,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAC3C;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;IACnD;6DACyD;IACzD,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACxE;AAED,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;AAI3D;;;;GAIG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,CAAC,IAAI,qBAA+B;IAC5C;+CAC2C;IAC3C,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,gEAAgE;IAChE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;gBAChB,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM;CAYrE;AAYD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,WAAW,EAClB,OAAO,GAAE,yBAA8B,GACtC,WAAW,CAsHb"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentfootprint",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.10.0",
|
|
4
4
|
"description": "The explainable agent framework — build AI agents you can explain, audit, and trust. Built on footprintjs.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Sanjay Krishna Anbalagan",
|
|
@@ -173,6 +173,7 @@
|
|
|
173
173
|
"@aws-sdk/client-cloudwatch-logs": "*",
|
|
174
174
|
"@aws-sdk/client-xray": "*",
|
|
175
175
|
"@modelcontextprotocol/sdk": "*",
|
|
176
|
+
"@opentelemetry/api": "*",
|
|
176
177
|
"footprintjs": ">=4.17.1",
|
|
177
178
|
"ioredis": "*",
|
|
178
179
|
"openai": "*",
|
|
@@ -200,6 +201,9 @@
|
|
|
200
201
|
"@modelcontextprotocol/sdk": {
|
|
201
202
|
"optional": true
|
|
202
203
|
},
|
|
204
|
+
"@opentelemetry/api": {
|
|
205
|
+
"optional": true
|
|
206
|
+
},
|
|
203
207
|
"ioredis": {
|
|
204
208
|
"optional": true
|
|
205
209
|
},
|