LiteLLM: OpenRouter та налаштування Fallbacks
0 (0)

Автор |  31/07/2026
Click to rate this post!
[Total: 0 Average: 0]

OpenRouter на днях анонсував 50% знижки на OpenAI, і вирішили спробувати його.

Переключення будемо робити на LiteLLM, на яку вже йдуть запити від всіх наших сервісів – тому переключення буде доволі простим.

До OpenRouter в LiteLLM налаштуємо fallbacks напряму до OpenAI – на ті випадки, коли реквести до OpenRouter фейляться.

Підключення OpenRouter до LiteLLM

Взагалі все, що треба – це описати новий LiteLLM Deployment, модель, і для неї вказати api_base == OpenRouter та власний API Key.

Єдина відмінність – це ім’я моделі: якщо при прямих викликах до OpenAI ми вказуємо model = openai/gpt-4.1-nano, то для OpenRouter це буде model = openrouter/openai/gpt-4.1-nano, див. OpenRouter Completion Models.

Отримати список всіх моделей можна з OpenRouter API, див. документацію Models.

$ curl -s "https://openrouter.ai/api/v1/models" | jq ".data[].id" | head
"qwen/qwen3.7-flash"
"anthropic/claude-opus-5-fast"
"anthropic/claude-opus-5"
"inclusionai/ling-3.0-flash:free"
"poolside/laguna-s-2.1"
"poolside/laguna-s-2.1:free"
"google/gemini-3.6-flash"
"google/gemini-3.6-flash:batch"
"google/gemini-3.5-flash-lite"

Створення OpenRouter API Key

Реєструємось в OpenRouter, переходимо в API Keys потрібного OpenRouter Workspace, створюємо OpenRouter API Key:

LiteLLM: OpenRouter та налаштування Fallbacks

В OpenRouter, до речі, можна задати власні ліміти на ключі:

LiteLLM: OpenRouter та налаштування Fallbacks

LiteLLM Proxy Config для OpenRouter

Додаємо нову модель, тут для тесту в model_name вкажемо власне ім’я – але в production використовуємо “загальне”, яке очікуємо від клієнтів.

В api_base перевизначаємо власний URL для OpenRouter API, див. Using the OpenRouter API, а в api_key – API Key, який створили вище:

...
    model_list:

      - model_name: or-gpt-4.1-mini
        litellm_params:
          model: openrouter/openai/gpt-4.1-mini
          api_base: https://openrouter.ai/api/v1
          api_key: os.environ/OPENROUTER_API_KEY
...

Поки тестуємо – змінну з ключем можна передати в Helm через envVars, в production це робиться через External Secrets Operator (див. AWS: Kubernetes та External Secrets Operator для AWS Secrets Manager та LiteLLM: AI Gateway в Kubernetes та метрики до VictoriaMetrics):

...
  envVars:
    OPENROUTER_API_KEY: "sk-or-v1-***"
...

Перевіряємо з curl:

$ curl -sS -D /tmp/headers 'https://aigw.test.example.co/chat/completions' -H 'Content-Type: application/json' -H "Authorization: Bearer $LITELLM_TESTING_KEY" --data ' { "model": "or-gpt-4.1-mini", "messages": [ { "role": "user", "content": "what llm are you" } ] } '  | jq
{
  "id": "gen-1785490640-Tbfwc5Rlx7cyhqpxzSB6",
  "created": 1785490640,
  "model": "or-gpt-4.1-mini",
  ...

В заголовках маємо всю цікаву інформацію (роблю через -D у файл, щоб jq нормально працював):

$ cat /tmp/headers 
...
x-litellm-model-name: openrouter/openai/gpt-4.1-mini
x-litellm-model-api-base: https://openrouter.ai/api/v1
...

Але коли викотили OpenRouter на “тестовий production” – почали ловити 429 помилки, а тому треба було додати fallbacks на OpenAI.

LiteLLM Fallbacks

Ліміти описані в Credit Limits and Rate Limits, і хоча у нас не free моделі – але помилки ловимо.

Да і в будь-якому випадку fallbacks мати треба.

OpenRouter теж підтримує Automatic failover between models, але для цього треба робити зміни в коді клієнтів – а ми хочемо максимально прозоро і з мінімум змін на клієнтах.

Тому налаштуємо з LiteLLM, див. Fallbacks (Provider Failover).

В документації LiteLLM трохи mess (вже не перший раз, btw), бо в одному прикладі описано як litellm_settings.fallbacks – а в іншому в router_settings.fallbacks.

Але більш коректним саме router_settings, бо він має пріорітет – router.py:

...

      _fallbacks = fallbacks or litellm.fallbacks

...

А fallbacks “приходить” саме із router_params в proxy_server.py:

...
router = litellm.Router(
    **router_params,
...

Формат доволі простий – “модель, для якої вказуємо fallback : список моделей, на які роутимо при проблемах”:

router_settings:
  fallbacks: [{"<QUERY_MODEL>": ["<FALLBACK_MODEL1>","<FALLBACK_MODEL2>"]}]

Плюс можна задати загальний, а не model-specific fallback, див. Default Fallbacks:

router_settings:
  default_fallbacks: ["claude-opus"]

(знов-таки – в документації він заданий в litellm_settings замість router_settings, хоча працює однаково в обох випадках)

Ну і давайте спробуємо, як це працює.

Додавання Model Fallback

Додаємо fallback для нашої тестової моделі:

router_settings:
  fallbacks: [{"or-gpt-4.1-mini": ["gpt-4.1-mini"]}]

А в самій моделі – “ламаємо” api_base через неправильний порт:

model_list:
  - model_name: or-gpt-4.1-mini
    litellm_params:
      model: openrouter/openai/gpt-4.1-mini
      api_base: https://openrouter.ai:8000/api/v1
      api_key: os.environ/OPENROUTER_API_KEY

Повторюємо запит:

$ curl -sS -D /tmp/headers 'https://aigw.test.example.co/chat/completions' -H 'Content-Type: application/json' -H "Authorization: Bearer $LITELLM_TESTING_KEY" --data ' { "model": "or-gpt-4.1-mini", "messages": [ { "role": "user", "content": "what llm are you" } ] } '  | jq
{
  "id": "chatcmpl-E7eVkBzpdbWp5n6JagYlYHTlEEI1f",
  "created": 1785492728,
  "model": "gpt-4.1-mini-2025-04-14",
  ...

І заголовки – відповідь вже від OpenAI:

$ cat /tmp/headers | grep x-litellm-model
x-litellm-model-id: 8b2d27fc9982e18f87ed59ca8c8b0d02c52546f26ece314f17d14433ed6498c2
x-litellm-model-name: openai/gpt-4.1-mini
x-litellm-model-api-base: https://api.openai.com
x-litellm-model-group: gpt-4.1-mini

Fallback Options

Див. Fallbacks + Retries + Timeouts + Cooldowns.

Для fallback routing можна задати кілька корисних опцій:

  • num_retries: скільки раз повторити запит в основній model group перед переходом до fallback-моделі
    • якщо в model group є кілька deployments, retry може піти до іншого deployment без fallback
  • timeout: скільки часу чекати відповіді перед тим, як перенаправити запити до fallback model
    • після кожного timout починається наступний num_retries, якщо там задано більше 1, і тільки потім – до fallback mode
  • allowed_fails: скільки failed requests до моделі допустимо перед тим, як модель перейти в cooldown
    • при значенні 3 cooldown спрацює на четвертій невдалій спробі
  • cooldown_time: скільки часу секундах  модель буде “відключена” від загального роутингу

Тут знов-таки документація трохи… крива, бо, наприклад, описано “allowed_fails: 3 # cooldown model if it fails > 1 call in a minute“, ну і замість “in a minute“, мабуть, малось на увазі 30 секунд в прикладі з cooldown_time.

Fallbacks та Prometheus metrics

Звісно, спрацювання – і помилки – фолбеків бажано моніторити.

У LiteLLM з коробки є метрики, див. Fallback (Failover) Metrics:

LiteLLM: OpenRouter та налаштування Fallbacks

Тут:

  • litellm_deployment_cooled_down: скільки раз deployment (модель) переводилась в стан cooldown
  • litellm_deployment_successful_fallbacks: кількість успішних спрацювань fallbacks
  • litellm_deployment_failed_fallbacks: кількість помилок при fallbacks

І приклад алерту:

- alert: LiteLLM Deployment Failed Fallback
  expr: |
    sum by (requested_model, fallback_model, api_key_alias, team_alias, exception_class, exception_status) (
      increase(litellm_deployment_failed_fallbacks_total[5m])
    ) > 0
  for: 30s
  labels:
    component: devops
    environment: ops
    severity: critical
    ilert_routingkey: devops-ops-critical
  annotations:
    summary: LiteLLM Deployment Failed Fallback
    description: |-
      LiteLLM failed to handle a request using a fallback model during the last 5 minutes.
      *Failed fallbacks*: `{{ "{{" }} $value }}`
      *Requested model*: `{{ "{{" }} $labels.requested_model }}`
      *Fallback model*: `{{ "{{" }} $labels.fallback_model }}`
      *API key alias*: `{{ "{{" }} $labels.api_key_alias }}`
      *Team alias*: `{{ "{{" }} $labels.team_alias }}`
      *Exception class*: `{{ "{{" }} $labels.exception_class }}`
      *Exception status*: `{{ "{{" }} $labels.exception_status }}`
      <https://{{ $.Values.monitoring.root_url }}/d/adtt9jj/adrmshg/litellm-system-overview |:grafana: LiteLLM System overview>

Advanced routing

У нас є сервіси, які хочуть продовжувати ходити на OpenAI – але ми не хочемо нічого міняти в їх коді, тобто model_name має залишитись, як є.

Тут є кілька варіантів, див. документацію Tag Based Routing:

  • використати Tags в API Keys
  • використати Tags з заголовків (але з нюансами)

Routing by API Key Tags

Створюємо новий ключ, правда при створенні ключа йому не мона відразу вказати Tags, бо “This feature is only available for LiteLLM Enterprise“.

Але можна створити ключ – а потім в його Settings вже задати потрібний Tag:

LiteLLM: OpenRouter та налаштування Fallbacks

І аналогічно – ключ з тегом “direct-openrouter“.

До router_settings додаємо enable_tag_filtering=true, а в model_list описуємо model group – два деплоймента з однаковим model_name, але різними умовами в tags:

router_settings:
  enable_tag_filtering: True

model_list:

  - model_name: or-gpt-4.1-mini
    litellm_params:
      model: openrouter/openai/gpt-4.1-mini
      api_base: https://openrouter.ai/api/v1
      api_key: os.environ/OPENROUTER_API_KEY
      tags: ["direct-openrouter"]

  - model_name: or-gpt-4.1-mini
    litellm_params:
      model: openai/gpt-4.1-mini
      api_key: os.environ/OPENAI_API_KEY
      tags: ["direct-openai"]

Робимо запит з ключем “direct-openai“:

$ curl -sS -D /tmp/openai 'https://aigw.test.example.co/chat/completions' -H 'Content-Type: application/json' -H "Authorization: Bearer sk-***" --data ' { "model": "or-gpt-4.1-mini", "messages": [ { "role": "user", "content": "what llm are you" } ] } ' | jq
{
  "id": "chatcmpl-E7f89zMEtza5liQnm8uBVQaaBcyVv",
  "created": 1785495109,
  "model": "gpt-4.1-mini-2025-04-14",
  ...

Маємо відповідь від OpenAI:

$ cat /tmp/openai | grep x-litellm-model
x-litellm-model-id: b1cd50178d84ad641058301b47c9f171d86337bcc7f0c08dc425f9f44ec2dffd
x-litellm-model-name: openai/gpt-4.1-mini
x-litellm-model-api-base: https://api.openai.com

І з ключем “direct-openrouter“:

$ curl -sS -D /tmp/openrouter 'https://aigw.test.example.co/chat/completions' -H 'Content-Type: application/json' -H "Authorization: Bearer sk-***" --data ' { "model": "or-gpt-4.1-mini", "messages": [ { "role": "user", "content": "what llm are you" } ] } ' | jq
{
  "id": "gen-1785495093-Toi3dLdjHJ692Ue8bFgl",
  "created": 1785495093,
  "model": "or-gpt-4.1-mini",
  ...

Маємо відповідь від OpenRouter:

$ cat /tmp/openrouter | grep x-litellm-model
x-litellm-model-name: openrouter/openai/gpt-4.1-mini
x-litellm-model-api-base: https://openrouter.ai/api/v1

Routing by the User Agent header

Інший варіант – не додавати теги вручну, а просто використати User Agent з headers.

В документації Regex-based tag routing (tag_regex) tag_regex описаний як “Use tag_regex on a deployment to match incoming requests by their headers” – тобто наче по будь-якому заголовку, але по факту враховується тільки User-Agent (якщо я правильно прочитав код):

...
        # Build header strings for regex matching from what the proxy already stores.
        # Currently we match against User-Agent; format matches "^User-Agent: claude-code/..."
        user_agent = metadata.get("user_agent", "")
        header_strings: list[str] = [f"User-Agent: {user_agent}"] if user_agent else []
...

Втім, в моєму випадку цього достатньо – бо один сервіс у нас це TypeScript з user-agent = "OpenAI/JS 6.26.0", а інший – Python з user-agent = "OpenAI/Python 2.45.0".

Міняємо умови в моделях, описуємо regex:

- model_name: or-gpt-4.1-mini
  litellm_params:
    model: openrouter/openai/gpt-4.1-mini
    api_base: https://openrouter.ai/api/v1
    api_key: os.environ/OPENROUTER_API_KEY
    tag_regex:
      - '^User-Agent: OpenAI/JS '

- model_name: or-gpt-4.1-mini
  litellm_params:
    model: openai/gpt-4.1-mini
    api_key: os.environ/OPENAI_API_KEY
    tag_regex:
      - '^User-Agent: OpenAI/Python '

Робимо запит з “основним” тестовим ключем (де нема тегів), але явно передаємо -H 'User-Agent: OpenAI/JS 6.26.0':

$ curl -sS -D /tmp/js-headers 'https://aigw.test.example.co/chat/completions' -H 'Content-Type: application/json' -H "Authorization: Bearer $LITELLM_TESTING_KEY" -H 'User-Agent: OpenAI/JS 6.26.0'   --data '{    "model": "or-gpt-4.1-mini",
    "messages": [{
      "role": "user",
      "content": "JS routing test unique-001"
    }]                                           
  }' | jq
{
  "id": "gen-1785495531-69Gu3ziqrjBmlNyEx3Jo",
  "created": 1785495531,
  "model": "or-gpt-4.1-mini",
  ...

Відповідь отримали від OpenRouter:

$ cat /tmp/js-headers
...
x-litellm-model-name: openrouter/openai/gpt-4.1-mini
x-litellm-model-api-base: https://openrouter.ai/api/v1
...

І аналогічний запит, але з -H 'User-Agent: OpenAI/Python 2.45.0' :

$ curl -sS -D /tmp/python-headers   'https://aigw.test.example.co/chat/completions'   -H 'Content-Type: application/json'   -H "Authorization: Bearer $LITELLM_TESTING_KEY"   -H 'User-Agent: OpenAI/Python 2.45.0'   --data '{
    "model": "or-gpt-4.1-mini",
    "messages": [{
      "role": "user",
      "content": "Python routing test unique-001"
    }]
  }' | jq
{
  "id": "chatcmpl-E7fFeVdB0ZWynqc5JxO3d4lUA4Gqf",
  "created": 1785495574,
  "model": "gpt-4.1-mini-2025-04-14",
  ...

І отримали відповідь від OpenAI:

$ cat /tmp/python-headers
x-litellm-model-name: openai/gpt-4.1-mini
x-litellm-model-api-base: https://api.openai.com

Готово.

Loading