Skip to content
Packages Examples Agents Blog Get started

AI Relay Gateway (oridecon-ai-relay-gateway)

Protocol-facing relay gateway for the Oridecon AI relay — channel selection, orchestration, upstream I/O, and SSE handling


Protocol-facing relay gateway for the Oridecon AI relay. Composes the gateway service from a channel registry, payload codec, upstream HTTP adapter, and the conversion engine, and exposes it behind RelayGatewayProtocol through RelayGatewayModule / RelayGatewayProvider.

One request is orchestrated end to end: authorization, channel selection, billing admission, request conversion, the protected upstream call, response conversion, billing settlement, and result metadata assembly. Streaming requests run the same preflight and then consume the upstream SSE stream lazily.

Full documentation: oridecon.dev

Terminal window
uv add oridecon-ai-relay-gateway
from oridecon import Application
from oridecon.di.module import Module, module
from oridecon.ai.relay.gateway import RelayGatewayConfig, RelayGatewayModule
@module(
imports=[
RelayGatewayModule.configure(
RelayGatewayConfig.from_mapping(
{
"channels": [
{
"name": "primary",
"upstream_base_url": "https://api.anthropic.com",
"target_format": "CLAUDE",
"models": ["claude-3-5-sonnet"],
}
],
}
)
)
]
)
class AppModule(Module):
pass
async with Application.boot(modules=[AppModule]) as app:
# use app.container to resolve services
...

The gateway registers its inbound routes automatically through the oridecon.web.contributors entry point; bind it through a web-enabled Application to serve /v1/chat/completions, /v1/responses, /v1/messages, and the Gemini /v1beta surface.

Explicit-only configuration: the gateway is not bound to a OrideconConfig section — it declares no config_key / config_model and reads no environment variables. Configuration is supplied to RelayGatewayModule.configure() as a RelayGatewayConfig.

from oridecon.ai.relay.gateway import RelayGatewayConfig
config = RelayGatewayConfig.from_mapping(
{
"channels": [...],
"auto_test_channels": True,
"auto_test_interval_seconds": 300,
"require_auth": True,
}
)

When a host binds a RelayChannelStoreProtocol, the DurableChannelLoader reconciles every durable row over the static table by name at boot; an empty store leaves the static table untouched.

FieldDefaultDescription
channels()Ordered channel table (name, upstream URL, target format, models)
model_suffix{}Channel-name to outbound model-suffix map (e.g. ":thinking")
provider_options{}Channel-name to provider options merged at conversion time
auto_test_channelsFalseBackground sweep probing channels, disabling failures
auto_test_interval_seconds600Delay between auto-test sweeps
max_upstream_retries0Retries across other channels after a retryable upstream failure
load_balancing"deterministic""deterministic" or "weighted" channel tie-breaking
job_ttl_seconds3600Eviction age for job-relay records on next poll
require_authTrueRequire a bound RelayAuthVerifierProtocol on relay routes; when no config is registered (or no container is available) the guard fails closed with 503 AUTH_REQUIRED_BUT_UNBOUND rather than passing through
rate_limits{}Per-model {"max", "window_seconds"} budgets
auto_disable_on_failuresFalseTake a channel out of service on consecutive failures
failover_failure_threshold3Consecutive failures that disable a channel
MethodDescription
RelayGatewayModule.configure(config=None)Gateway with the built-in relay routes; empty config = no channels
  • Inbound relay routes: OpenAI Chat (/v1/chat/completions), OpenAI Responses (/v1/responses), Anthropic (/v1/messages), Gemini (/v1beta/models/{model}:generateContent) plus model list/detail surfaces
  • Channel selection: deterministic priority + weight resolution over a runtime override table, with weighted load balancing
  • Full request lifecycle: auth, channel selection, billing admission, conversion, upstream call, settlement, metadata
  • SSE streaming: lazy upstream stream consumption through a stream session; billing settles exactly once
  • Credential injection: per-channel credential providers behind an injecting HTTP client
  • Passthrough routes: /v1/embeddings, /v1/rerank, /v1/moderations, /v1/audio/*, /v1/images/*
  • Job relay: submit-then-poll routes (POST /v1/videos, GET /v1/videos/{job_id}) with eviction TTL
  • Operations: channel health probing, background auto-tester, route metrics, operator controls, failover tracking
  • Governance: per-model rate limiting and an optional auth guard on inbound routes
  • Durable channels: store-backed channel reconciliation at boot via DurableChannelLoader
  • Admin surface: channel CRUD pages and actions for the admin UI
async with Application.boot(modules=[RelayGatewayModule.configure()]) as app:
# your test code
...
FileWhat it contains
src/oridecon/ai/relay/gateway/module.pyRelayGatewayModule.configure()
src/oridecon/ai/relay/gateway/config.pyRelayGatewayConfig and channel-table validation
src/oridecon/ai/relay/gateway/channels.pyRelayChannelRegistry — deterministic channel selection
src/oridecon/ai/relay/gateway/service.pyRelayGatewayService — request lifecycle orchestration
src/oridecon/ai/relay/gateway/upstream.pyHTTPUpstreamAdapter and upstream I/O
src/oridecon/ai/relay/gateway/codec.pyRelayPayloadCodec — payload encode/decode
src/oridecon/ai/relay/gateway/stream.pyrelay_stream and UpstreamEventParser for SSE
src/oridecon/ai/relay/gateway/loader.pyDurableChannelLoader — store reconciliation at boot
src/oridecon/ai/relay/gateway/operations/Health, metrics, controls, auto-test, failover, stream registry
src/oridecon/ai/relay/gateway/web/Relay routes, SSE, audio and image endpoints
src/oridecon/ai/relay/gateway/ratelimit.pyPer-model rate-limit guard (Redis-backed variant alongside)
src/oridecon/ai/relay/gateway/admin/Admin pages and actions for channel CRUD