Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | 17x 6x 6x 6x 43x 43x 43x 30x 4x 3x 13x 13x 2x 2x 11x 11x 11x 6x 8x 8x 8x | import type { ErrorCode } from '../types/index.js';
import { sanitizeErrorMessage } from './errors.js';
const DEFAULT_MAX_CONSECUTIVE_PROVIDER_FAILURES = 5;
type CircuitBreakerCode = Extract<ErrorCode, 'auth_failed' | 'provider_unavailable' | 'invalid_model_selector'>;
export interface CircuitBreakerReason {
code: CircuitBreakerCode;
message: string;
}
interface ProviderFailureCircuitBreakerOptions {
maxConsecutiveProviderFailures?: number;
abortController?: AbortController;
}
function providerUnavailableMessage(count: number, lastMessage: string): string {
const detail = sanitizeErrorMessage(lastMessage).trim();
const suffix = detail ? ` Last error: ${detail}` : '';
return `Provider unavailable after ${count} consecutive failures. Warden stopped early.${suffix}`;
}
/**
* Tracks unrecoverable provider failures across a Warden run.
*/
export class ProviderFailureCircuitBreaker {
private consecutiveProviderFailures = 0;
private openReason?: CircuitBreakerReason;
private readonly maxConsecutiveProviderFailures: number;
private readonly abortController?: AbortController;
constructor(options: ProviderFailureCircuitBreakerOptions = {}) {
this.maxConsecutiveProviderFailures =
options.maxConsecutiveProviderFailures ?? DEFAULT_MAX_CONSECUTIVE_PROVIDER_FAILURES;
this.abortController = options.abortController;
}
get reason(): CircuitBreakerReason | undefined {
return this.openReason;
}
recordSuccess(): void {
if (this.openReason) return;
this.consecutiveProviderFailures = 0;
}
recordFailure(code: ErrorCode, message: string): void {
Iif (this.openReason) return;
if (code === 'auth_failed' || code === 'invalid_model_selector') {
this.open({ code, message });
return;
}
Iif (code !== 'provider_unavailable') return;
this.consecutiveProviderFailures++;
if (this.consecutiveProviderFailures >= this.maxConsecutiveProviderFailures) {
this.open({
code,
message: providerUnavailableMessage(this.consecutiveProviderFailures, message),
});
}
}
private open(reason: CircuitBreakerReason): void {
this.openReason = reason;
Eif (!this.abortController?.signal.aborted) {
this.abortController?.abort();
}
}
}
|