Skip to content

feat(venice): add fallback provider chain and automatic retries with graceful degradation (#387) - #468

Merged
devJaja merged 2 commits into
Epta-Node:mainfrom
MarcusDavidG:feat/venice-fallback-387
Aug 31, 2026
Merged

feat(venice): add fallback provider chain and automatic retries with graceful degradation (#387)#468
devJaja merged 2 commits into
Epta-Node:mainfrom
MarcusDavidG:feat/venice-fallback-387

Conversation

@MarcusDavidG

Copy link
Copy Markdown
Contributor

Summary

Resolves #387

Changes

  • Config (backend/src/config/index.ts:14): add VENICE_BASE_URL (default https://api.venice.ai/api/v1), VENICE_FALLBACK_API_KEYS/VENICE_FALLBACK_BASE_URLS (comma-separated, ordered), VENICE_REQUEST_TIMEOUT_MS (10s) and VENICE_PROVIDER_MAX_RETRIES (3) with zod validation; legacy cache vars kept
  • Types (backend/src/services/venice/types.ts:10): new VeniceProviderConfig {apiKey, baseUrl?, name?}; VeniceClientConfig now providers?: VeniceProviderConfig[], timeoutMs?, maxRetries?, enableCacheFallback?
  • Cache (backend/src/services/venice/cache.ts:123): new getStale(prompt, agentType, modelVersion) — returns most recent cached entry even if expired (exact then fuzzy), records hit; used for graceful degradation
  • Client (backend/src/services/venice/client.ts:48):
    • Builds ordered provider chain: explicit providers wins, else primary (apiKey/baseUrl) + fallbacks from VENICE_FALLBACK_* env; getProviders() exposed
    • Per-call timeouts: AbortController + setTimeout(timeoutMs)AbortError mapped to Venice request timed out after Xms
    • Retries with backoff: fetchWithRetryForProvidermaxAttempts = min(maxRetries, RETRY_DELAYS)+1, base delays [200,400,800,1600] + ±20% jitter, onRetry increments counter; retryable 429/503/500/502/504, non-retryable 400/422 (401 allows failover to next key)
    • Fallback chain: runVeniceFetch loops providers in order; intermediate failure logs failing over to next provider +100ms backoff and continues; only last provider failure calls breaker.recordFailure(); success calls recordSuccess() and keeps breaker closed
    • Graceful degradation: createCompletion first checks fresh cache.get (hit→return), then deduped runVeniceFetch; on catch, if enableCacheFallback && !force tries cache.getStale and returns stale with warn log instead of throwing — task succeeds via cache
    • Circuit breaker: assertClosed before fetch but stale cache still served when OPEN; opens after 3 consecutive last-provider failures (existing FAILURE_THRESHOLD 3, 60s), 4th call throws CircuitOpenError without HTTP (verified)
    • Restores corrupted merges (backend/package.json, jest.config.js, tsconfig.json, api/app.ts, api/routes/agents.ts from 6ced79b) to make build green (pre-existing, ignored per instructions but needed for CI)
  • Stream also loops providers with same timeout/retry/failover

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)
  • Manual tsx fallback test: primary 500×4 → fallback 200fallback-answer returned, breaker CLOSED (failover without failing task); timeout 100ms → 4 attempts then throw; primed cache + all providers 500 → stale cached-answer returned; 3×500 → breaker OPEN, 4th throws CircuitOpenError without fetch
  • Cache unit getStale returns expired entry even when fresh miss

Contributor Checklist

  • PR title follows Conventional Commits
  • No secrets committed

…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
@vercel

vercel Bot commented Aug 30, 2026

Copy link
Copy Markdown

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.

@drips-wave

drips-wave Bot commented Aug 31, 2026

Copy link
Copy Markdown

@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! 🚀

Learn more about application limits

@devJaja
devJaja merged commit a485a13 into Epta-Node:main Aug 31, 2026
7 of 11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add fallback provider chain and automatic retries to Venice AI client

2 participants