惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

L
LangChain Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
雷峰网
雷峰网
量子位
V
V2EX
S
SegmentFault 最新的问题
月光博客
月光博客
博客园 - 【当耐特】
Hugging Face - Blog
Hugging Face - Blog
V
Visual Studio Blog
大猫的无限游戏
大猫的无限游戏
T
Tailwind CSS Blog
博客园_首页
博客园 - Franky
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
美团技术团队
Y
Y Combinator Blog
The Cloudflare Blog
C
Check Point Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
腾讯CDC
B
Blog
Stack Overflow Blog
Stack Overflow Blog
P
Proofpoint News Feed

RTFM: Linux, DevOps та системне адміністрування

LiteLLM: дебаг AI Cost Monitoring з VictoriaMetrics LiteLLM: Custom Callback та LLM Evaluations з Judge LLM LiteLLM: Custom Callback для traffic mirroring та OTel tracing до VictoriaTraces LiteLLM: Traffic Mirroring та Batch Completions і трафік до двох провайдерів llama.cpp: метрики та моніторинг з VictoriaMetrics Ubuntu: установка NGINX та TLS-сертифікат з Let’s Encrypt та AWS Route53 LiteLLM: метрики, traces та дебаг exception_class=”ValueError” NixOS: знайомство, установка пакетів та конфігурація системи MongoDB: запуск в Kubernetes з MongoDB Operator та MongoDBCommunity CRD Valkey: запуск в Kubernetes – Helm, monitoring, ACL LiteLLM: моніторинг з VictoriaMetrics – алерти та Grafana LiteLLM: метрики, traces та інтеграція з VictoriaMetrics Stack LiteLLM: AI Gateway в Kubernetes та метрики до VictoriaMetrics Claude Code: моніторинг з OpenTelemetry та VictoriaMetrics LiteLLM: AI Gateway для LLM – overview можливостей MikroTik: Users Management, права доступа та SSH VictoriaTraces: Recording Rules, метрики та алерти з trace spans Arch Linux: DNS-детектив – VPN, systemd-resolved та Unbound VictoriaTraces: Tracing, Observability та OpenTelemetry OpenTelemetry: OTel Collectors в Kubernetes та інтеграція з VictoriaMetrics stack Arch Linux: WireGuard Peer для підключення до MikroTik FreeBSD: Jails networking та менеджмент контейнерів з Bastille Hermes Agent: запуск AI Agent у FreeBSD Jail з Bastille Claude Code: створення Kubernetes debugging AI Agent для VictoriaMetrics Okta: інтеграція з Google Workspaces, частина 1 – Provisioning
LiteLLM: OpenRouter та налаштування Fallbacks
setevoy · 2026-07-31 · via RTFM: Linux, DevOps та системне адміністрування

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

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

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

Зміст

Взагалі все, що треба – це описати новий 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