Fallback With Translation
Demonstrates provider failover with Responses-to-Chat Completions protocol translation using the iterative_request_router
Category: Setup-dependent integration
Task: Demonstrates provider failover with Responses-to-Chat Completions protocol translation using the iterative_request_router
Prerequisites: The external service, credentials, or certificates referenced by this configuration.
This configuration comes from the selected release. The example has not been run here; external services are not bundled.
Download the source file.
# Inference Fallback with Protocol Translation
# Requires `--features openai-responses` because these filters are opt-in.
#
# Demonstrates provider failover with Responses-to-Chat Completions
# protocol translation using the iterative_request_router. When the
# primary backend returns a retryable status, the request automatically
# retries against a fallback provider. Both backends receive translated
# Chat Completions requests with isolated credentials.
#
# Step boundary: the IRR resets per-step metadata for credential
# isolation but preserves extensions across steps. This means:
#
# - ResponsesState (extension, set by openai_responses_validate)
# survives step transitions.
# - openai_responses_format.format (metadata) does not.
#
# Because responses_to_chat_completions reads both metadata and
# extensions, each step must re-run openai_responses_format and
# openai_responses_validate to repopulate its metadata. Without
# them the translation filter returns 500: "request pipeline state
# is unavailable".
#
# Filter ordering within each step:
#
# 1. openai_responses_format classify, set metadata
# 2. openai_responses_model_rewrite rewrite model (optional)
# 3. openai_responses_validate parse body, set metadata + extension
# 4. responses_to_chat_completions translate body using both
# 5. path_rewrite /v1/responses -> /v1/chat/completions
# 6. router select cluster
# 7. credential_injection inject per-cluster credentials
# 8. load_balancer select endpoint
#
# Key ordering constraints:
# - 1 before 2: model rewrite reads openai_responses_format.stream
# - 2 before 3: validate parses after rewrite, so ResponsesState
# carries the effective model
# - 3 before 4: translation reads responses.response_id metadata
# and ResponsesState extension, both set by validate
# - 6 before 7: credential injection matches on ctx.cluster,
# which the router sets
#
# Pre-IRR filters:
# openai_responses_format rejects non-Responses requests early.
# openai_responses_validate rejects malformed bodies before the
# IRR allocates sub-request resources.
#
# Adapting for real providers:
# - Add openai_responses_model_rewrite between format and validate
# in the fallback step to map model names across providers.
# - Use env_var instead of value in credential_injection to read
# API keys from the environment at startup.
# - Add a headers filter (request_set Host) before load_balancer
# for external HTTPS upstreams.
# - Add tls: { sni: "hostname" } to the cluster for HTTPS.
# - Adjust the path_rewrite replacement per provider (e.g.
# /api/v1/chat/completions for OpenRouter).
#
# Usage:
# cargo run -p praxis-ai-proxy --features openai-responses -- -c examples/configs/inference/fallback-with-translation.yaml
listeners:
- name: ai-gateway
address: "127.0.0.1:8080"
filter_chains: [inference-failover]
filter_chains:
- name: inference-failover
filters:
- filter: openai_responses_format
on_invalid: reject
headers:
format: x-praxis-ai-format
model: x-praxis-ai-model
stream: x-praxis-ai-stream
- filter: openai_responses_validate
- filter: iterative_request_router
initial_step: primary
steps:
- name: primary
filters:
- filter: openai_responses_format
- filter: openai_responses_validate
- filter: responses_to_chat_completions
max_rewritten_body_bytes: 67108864
- filter: path_rewrite
replace:
pattern: "^/v1/responses/?$"
replacement: "/v1/chat/completions"
conditions:
- when:
path_prefix: "/v1/responses"
methods: [POST]
- filter: router
routes:
- path_prefix: "/"
cluster: primary-backend
- filter: credential_injection
clusters:
- name: primary-backend
header: Authorization
value: "primary-key"
header_prefix: "Bearer "
strip_client_credential: true
- filter: load_balancer
clusters:
- name: primary-backend
endpoints:
- "127.0.0.1:3001"
on_result:
- status: [429, 502, 503, 504]
next: fallback
- default: true
done: true
- name: fallback
filters:
- filter: openai_responses_format
- filter: openai_responses_validate
- filter: responses_to_chat_completions
max_rewritten_body_bytes: 67108864
- filter: path_rewrite
replace:
pattern: "^/v1/responses/?$"
replacement: "/v1/chat/completions"
conditions:
- when:
path_prefix: "/v1/responses"
methods: [POST]
- filter: router
routes:
- path_prefix: "/"
cluster: fallback-backend
- filter: credential_injection
clusters:
- name: fallback-backend
header: Authorization
value: "fallback-key"
header_prefix: "Bearer "
strip_client_credential: true
- filter: load_balancer
clusters:
- name: fallback-backend
endpoints:
- "127.0.0.1:3002"
on_result:
- default: true
done: true
insecure_options:
allow_private_endpoints: true # example proxies to local backends