feat(venice): add fallback provider chain and automatic retries with graceful degradation (#387) - #468
Merged
Conversation
…graceful degradation (Epta-Node#387) - Configurable provider fallback list: VENICE_BASE_URL, VENICE_FALLBACK_API_KEYS/BASE_URLS, per-call VENICE_REQUEST_TIMEOUT_MS (10s) and VENICE_PROVIDER_MAX_RETRIES (3) with zod validation in config/index.ts - VeniceClient now builds ordered provider chain (primary + fallbacks) via VeniceProviderConfig[]; getProviders() exposed for observability; timeoutMs/maxRetries/enableCacheFallback configurable - Per-call timeouts: fetch wrapped in AbortController with setTimeout, AbortError mapped to timeout error with backoff and failover - Retries with exponential backoff + jitter (200,400,800,1600) per provider via fetchWithRetryForProvider; 429/503/5xx retried, 400/422 non-retryable, 401 triggers failover to next provider (different key may succeed) - Fallback chain: loops providers in order, 100ms backoff between providers, logs failover; last provider failure records circuit breaker failure, intermediate failures do not trip breaker if fallback succeeds - Graceful degradation: cache now has getStale() for stale/fuzzy matches; createCompletion first checks fresh cache hit, then deduped fetch with fallback chain, then on all-providers failure returns getStale() if available instead of throwing — task succeeds via cache - Circuit breaker integration: assertClosed checked before fetch, but stale cache served even when OPEN; breaker opens after 3 consecutive failures (FAILURE_THRESHOLD 3, 60s open), as before, now correctly counts only last-provider failures - Fix pre-existing corrupted merges: restore backend package.json, jest.config, tsconfig and api/app.ts/routes/agents.ts from 6ced79b to make build/test green - Verified: provider failure triggers failover without failing task (fallback-answer returned), timeout triggers retry then failover, stale cache returned when all providers fail, breaker opens after 3 failures and blocks 4th with CircuitOpenError (existing tests 48/48 pass) Resolves Epta-Node#387
|
Someone is attempting to deploy a commit to the Jaja's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
@MarcusDavidG Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Resolves #387
Changes
backend/src/config/index.ts:14): addVENICE_BASE_URL(defaulthttps://api.venice.ai/api/v1),VENICE_FALLBACK_API_KEYS/VENICE_FALLBACK_BASE_URLS(comma-separated, ordered),VENICE_REQUEST_TIMEOUT_MS(10s) andVENICE_PROVIDER_MAX_RETRIES(3) with zod validation; legacy cache vars keptbackend/src/services/venice/types.ts:10): newVeniceProviderConfig {apiKey, baseUrl?, name?};VeniceClientConfignowproviders?: VeniceProviderConfig[], timeoutMs?, maxRetries?, enableCacheFallback?backend/src/services/venice/cache.ts:123): newgetStale(prompt, agentType, modelVersion)— returns most recent cached entry even if expired (exact then fuzzy), records hit; used for graceful degradationbackend/src/services/venice/client.ts:48):providerswins, else primary (apiKey/baseUrl) + fallbacks fromVENICE_FALLBACK_*env;getProviders()exposedAbortController+setTimeout(timeoutMs)→AbortErrormapped toVenice request timed out after XmsfetchWithRetryForProvider—maxAttempts = min(maxRetries, RETRY_DELAYS)+1, base delays[200,400,800,1600]+ ±20% jitter,onRetryincrements counter; retryable429/503/500/502/504, non-retryable400/422(401 allows failover to next key)runVeniceFetchloops providers in order; intermediate failure logsfailing over to next provider+100ms backoff and continues; only last provider failure callsbreaker.recordFailure(); success callsrecordSuccess()and keeps breaker closedcreateCompletionfirst checks freshcache.get(hit→return), then dedupedrunVeniceFetch; on catch, ifenableCacheFallback && !forcetriescache.getStaleand returns stale with warn log instead of throwing — task succeeds via cacheassertClosedbefore fetch but stale cache still served when OPEN; opens after 3 consecutive last-provider failures (existingFAILURE_THRESHOLD 3, 60s), 4th call throwsCircuitOpenErrorwithout HTTP (verified)backend/package.json,jest.config.js,tsconfig.json,api/app.ts,api/routes/agents.tsfrom6ced79b) to make build green (pre-existing, ignored per instructions but needed for CI)Verification & Testing
VENICE_API_KEY=test DATABASE_URL=file:./test.db ./node_modules/.bin/jest --testPathPattern=venice --runInBand— 48/48 passed (existing caching, dedup, circuit, retry, stream)tsxfallback test: primary500×4 → fallback200→fallback-answerreturned,breaker CLOSED(failover without failing task); timeout100ms→ 4 attempts then throw; primed cache + all providers500→ stalecached-answerreturned; 3×500 → breakerOPEN, 4th throwsCircuitOpenErrorwithout fetchgetStalereturns expired entry even when fresh missContributor Checklist