Skip to content

SDK Resilience

This page covers the SDK resilience layer (tappass.resilience): how the SDK degrades when the TapPass server is unreachable, the three fail modes, the circuit breaker, and the local audit buffer.

from tappass.resilience import ResiliencePolicy, FailMode
policy = ResiliencePolicy(
mode=FailMode.FAIL_OPEN_CACHED,
cache_ttl_seconds=300,
max_offline_requests=100,
local_audit_path=".tappass_audit_buffer.jsonl",
circuit_failure_threshold=3,
circuit_recovery_timeout=30.0,
)
Parameter Type Default Env var Description
mode FailMode FAIL_CLOSED TAPPASS_FAIL_MODE Degradation mode (fail_closed, fail_open_cached, fail_open_logged)
cache_ttl_seconds int 300 TAPPASS_CACHE_TTL How long cached responses stay valid
max_offline_requests int 100 TAPPASS_MAX_OFFLINE_REQUESTS Hard cap on degraded-mode calls (0 = unlimited)
local_audit_path str .tappass_audit_buffer.jsonl TAPPASS_LOCAL_AUDIT_PATH Path for the local audit buffer file
circuit_failure_threshold int 3 TAPPASS_CIRCUIT_FAILURE_THRESHOLD Consecutive failures before the circuit opens
circuit_recovery_timeout float 30.0 TAPPASS_CIRCUIT_RECOVERY_TIMEOUT Seconds before OPEN → HALF_OPEN
alert_on_degradation bool True Log a WARNING when entering degraded mode

Load everything from the environment in one call:

policy = ResiliencePolicy.from_env()

An invalid TAPPASS_FAIL_MODE value logs a warning and falls back to fail_closed.

Mode Behavior Use case
FAIL_CLOSED Caller raises TapPassConnectionError. Agent stops. Regulated environments where ungoverned operation is unacceptable. Default.
FAIL_OPEN_CACHED Serves the last-known-good cached response; audit entries queued locally. Production agents that must stay available through short outages.
FAIL_OPEN_LOGGED Continues ungoverned; every call logged locally with classification DEGRADED. Development and low-risk agents.

Responses served in a fail-open mode carry:

response["tappass"]["degraded"] = True
response["tappass"]["degraded_reason"] # e.g. "TapPass unreachable — ConnectError (serving from cache)"
response["tappass"]["classification"] = "DEGRADED" # synthetic responses (no cache)

Cached responses additionally include tappass.cache_age_seconds.

Orchestrates the circuit breaker, cache, and audit buffer:

from tappass.resilience import ResilienceManager
manager = ResilienceManager(ResiliencePolicy.from_env())
if manager.should_attempt_request():
try:
data = post_to_tappass(...) # your HTTP call
manager.record_success()
manager.cache_response(cache_key, data, model="gpt-4o-mini")
except ConnectionError as exc:
degraded = manager.handle_connection_failure(
messages, model="gpt-4o-mini",
cache_key=cache_key, original_error=exc,
)
if degraded is None: # fail_closed (or offline cap hit)
raise
data = degraded

Key members:

Member Description
is_degraded True while the circuit is open
offline_request_count Calls served since the circuit opened
degraded_duration_seconds Time since the circuit opened (None when closed)
record_success() / record_failure() Drive the circuit breaker
handle_connection_failure(messages, model, cache_key, original_error) Returns a degraded response dict for fail-open modes, None for fail-closed
flush_audit_buffer(client, url) Drain buffered audit entries to {url}/audit/ingest; returns count flushed
status() Diagnostic dict (mode, circuit state, cache size, buffered entries, …)

TapPassCircuitBreaker(failure_threshold=3, recovery_timeout=30.0). States:

CLOSED ──── requests flow normally
│ N consecutive failures (default 3)
OPEN ────── fallback, no connection attempts
│ recovery timeout expires (default 30s)
HALF_OPEN ── probe one request → success: CLOSED / failure: OPEN

Public API: state (CircuitState), is_open, consecutive_failures, record_success(), record_failure(), should_attempt(), reset(), to_dict(). Thread-safe.

Thread-safe last-known-good cache. Keyed by caller-supplied key (the SDK helper _make_cache_key keys by a hash of the model and last user message; small bounded LRU-style cache). Served only in FAIL_OPEN_CACHED mode within TTL. This is a safety net, not a performance cache.

Spill buffer for AuditEvents while the server is down. Serializes to JSONL on disk; drain() returns the events and deletes the file for replay after recovery. has_pending() reports unflushed in-memory events or a non-empty buffer file. The buffer is plaintext JSONL — protect local_audit_path with filesystem permissions.