Skip to content

Introduce extensible provider router abstraction with retries and multi-provider support#554

Open
OsirisLord wants to merge 1 commit into
arakoodev:tsfrom
OsirisLord:feat/provider-router
Open

Introduce extensible provider router abstraction with retries and multi-provider support#554
OsirisLord wants to merge 1 commit into
arakoodev:tsfrom
OsirisLord:feat/provider-router

Conversation

@OsirisLord
Copy link
Copy Markdown

Summary

Introduces a LiteLLM-inspired provider router abstraction into the EdgeChains JS SDK. All existing APIs are preserved via an adapter pattern - new OpenAI({...}).chat() continues to work exactly as before.

What was added

Core Router (core/)

  • Router.ts - Weighted utilization scoring, deployment failover on 429/5xx, cooldown after repeated failures, retries with exponential backoff
  • RetryPolicy.ts - Error classification (auth, rate-limit, server, timeout), determines retry/failover eligibility
  • TokenTracker.ts - TokenStore interface + InMemoryTokenStore with rolling 60-second window
  • types.ts - NormalizedResponse, NormalizedError, StreamEvent, RouterConfig, DeploymentConfig, ProviderCapabilities, TokenUsage, scorable deployment state

Provider implementations (providers/)

  • BaseProvider - Abstract contract: chat(), stream(), capabilities
  • OpenAIProvider - Chat + streaming with SSE parsing and token extraction from usage
  • GeminiProvider - Chat + streaming with defensive parsing (Gemini schemas change frequently)
  • CohereProvider - Chat only (streaming deferred), token tracking via meta.tokens
  • ProviderCapabilities - Flag sets per provider (streaming, tools, images, system messages)

Transport (transport/)

  • axios.ts - Axios instance factory with error normalization interceptor
  • interceptors.ts - normalizeError() converts any HTTP/network error into NormalizedError

Backward Compatibility

  • lib/openai/openai.ts - Adapter: delegates chat(), streamedChat(), chatWithFunction() to Router internally. generateEmbeddings() and zodSchemaResponse() fall back to direct calls. All 5 public methods preserved.

Tests (8 files, 35 tests, all passing)

  • TokenTracker.test.ts (7) - Accumulation, window isolation, expiration, resetWindow
  • RetryPolicy.test.ts (8) - All error categories, shouldRetry boundary conditions
  • Router.test.ts (7) - Deployment selection, 429 to Cohere failover, all-deployments-fail, auth rejection, token tracking
  • OpenAIProvider.test.ts (3) - Normalized response, empty content
  • GeminiProvider.test.ts (4) - Defensive parsing, missing candidates, missing usage metadata
  • CohereProvider.test.ts (4) - Normalized response, missing metadata, empty response
  • streaming.test.ts (1) - OpenAI SSE delta accumulation
  • openAiEndpoints.test.ts (1) - Backward compat adapter still works

Architecture decisions

  • Retry separation: axios handles network errors (same deployment); Router handles deployment failover (switch provider on 429/5xx)
  • Weighted scoring: score = tokenUtilization x 0.5 + activeRequests x 0.3 + failurePenalty x 0.2
  • Pluggable TokenStore: Interface defined for future Redis/DB implementations
  • Jsonnet-compatible config: RouterConfig shape directly usable from jsonnet output

Excluded from this PR (Phase 2)

  • Sentry/PostHog logging
  • Jsonnet native function changes
  • Advanced load balancing (latency/cost/health)
  • Complex rate limiter with header reconciliation
  • Provider SDK dependencies (all providers use raw axios)

@github-actions
Copy link
Copy Markdown

CLA Assistant Lite bot: Thank you for your submission, we really appreciate it. Before we can accept your contribution, we ask that you sign the Arakoo Contributor License Agreement. You can sign the CLA by adding a new comment to this pull request and pasting exactly the following text.


I have read the Arakoo CLA Document and I hereby sign the CLA


You can retrigger this bot by commenting recheck in this Pull Request

@OsirisLord
Copy link
Copy Markdown
Author

I have read the Arakoo CLA Document and I hereby sign the CLA

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.

1 participant