Це друга частина матеріалу про моніторинг LiteLLM – у попередній ми розібрали загальну інтеграцію з VictoriaStack, а також подивилися, які метрики й трейси отримуємо від LiteLLM (див. LiteLLM: метрики, traces та інтеграція з VictoriaMetrics Stack).
Тепер перейдемо до практичної частини – що відстежувати та як це можна організувати.
Втім, хоча LiteLLM у нас уже працює в production, система все ще перебуває в процесі “допилювання” та “спостереження за поведінкою”, а тому описані в цьому пості алерти й дашборди не є фінальним варіантом – це, скоріше, демонстрація можливих підходів до контролю за LiteLLM, а також приклади запитів до VictoriaMetrics і VictoriaTraces.
Сьогодні зосередимося на двох основних компонентах моніторингу – алертах та Grafana dashboards, бо метрик у нас багато, і спочатку треба визначити базовий набір даних, з якого варто починати налаштування контролю роботи системи.
Всі пости цієї серії:
- LiteLLM: AI Gateway для LLM – overview можливостей
- LiteLLM: AI Gateway в Kubernetes та метрики до VictoriaMetrics
- LiteLLM: метрики, traces та інтеграція з VictoriaMetrics Stack
- (current) LiteLLM: моніторинг з VictoriaMetrics, алерти та Grafana
І далі буде ще – слідкуйте за апдейтами в Telegram @rtfmcoua або LinkedIn group RTFM! DevOps[at]UA.
Зміст
Alerts
У нас кілька основних компонентів які треба моніторити і по яким треба алертити:
- сама система: загальні алерти по статусу Pods, failed requests, latency, in-flight requests
- Redis: статус самого Redis та його Pods, помилки
- для real production можна додати Redis Prometheus експортер
- PostgreSQL: взагалі, звісно, за сервером баз даних теж треба слідкувати – але в моєму випадку це вже існуючий сервер і він вже давно “покритий моніторингом”
- Redis: статус самого Redis та його Pods, помилки
- providers/LLM: статус моделей, failed responses, latency
- costs, tokens, budgets та limits: використання системи by Teams, API Keys, і взагалі сервісами та юзерами
System status: статус LiteLLM
Див. також Monitor System Health.
Що нам важливо тут:
- Pods number: перевіряємо Deployment desired Pods vs current running Pods
- Pods resources: CPU/RAM used – окремий алерт не робив, вистачить загальних
- Failed requests: тут саме про фейли від клієнтів до LiteLLM
- алертимо по метриці
litellm_proxy_failed_requests_metric_total
- алертимо по метриці
- Latency: потім зробимо алерти, поки просто в дашборді Grafana:
litellm_request_total_latency_metric: загальний час на обробку запитів – з моменту отримання реквеста від клієнта + LiteLLM overhead + відправка до LLM provider + очікування і повернення відповіді клієнтуlitellm_llm_api_latency_metric: тільки час відповіді від провайдера/LLMlitellm_overhead_latency_metric: скільки часу із загальногоlitellm_request_total_latency_metricвитратив сам LiteLLM – поки спостерігаємо, потім можна додати алертlitellm_self_latency: auth, routing, logging callbacks (prometheus,otel), обчислювання spend tracking, Redis і PostgreSQL операції – все що LiteLLM робить до і після запиту до OpenAI/Anthropic
- LiteLLM HTTP requests: кількість активних запитів в обробці на uvicorn workers – метрика
litellm_in_flight_requests
Алерт “Kubernetes LiteLLM Pods/Deployment status”
Взагалі це стандартний алерт, який є в більшості Helm-чартів з коробки – але я створив окремий саме на LiteLLM:
---
apiVersion: operator.victoriametrics.com/v1beta1
kind: VMRule
metadata:
name: alerts-litellm-system
spec:
groups:
- name: LiteLLM.System.Error.rules
rules:
# Deployment not matching replicas
- alert: LiteLLM System Pods Not Ready
expr: kube_deployment_status_replicas_ready{namespace="ops-litellm-ns"} < kube_deployment_spec_replicas{namespace="ops-litellm-ns"}
for: 3m
labels:
component: devops
environment: ops
severity: critical
ilert_routingkey: devops-ops-critical
annotations:
summary: LiteLLM System Pods Not Ready
description: |-
LiteLLM System pods are not ready for more than `{{ "{{" }} $for }}`
*Namespace*: `{{ "{{" }} $labels.namespace }}`
*Deployment*: `{{ "{{" }} $labels.deployment }}`
*Ready replicas*: `{{ "{{" }} $labels.ready_replicas }}`
*Replicas*: `{{ "{{" }} $labels.replicas }}`
<https://{{ $.Values.monitoring.root_url }}/d/adtt9jj/adrmshg/litellm-system-overview |:grafana: LiteLLM System overview>
Алерти Redis
Окремо кілька алертів конкретно по Redis.
Код з метриками в самому LiteLLM – prometheus_services.py (бо іноді довелось дивитись там, а не в документації – хоча загалом документація чудова).
Алерт “LiteLLM Redis Pods Not Ready”
Алерт “Redis Pods Not Ready” – аналогічний до попереднього, просто перевіряємо кількість подів в Ready vs скільки має бути в StatefulSet:
- alert: LiteLLM Redis Pods Not Ready
expr: kube_statefulset_status_replicas_ready{namespace="ops-litellm-ns", statefulset="litellm-redis-master"} < kube_statefulset_replicas{namespace="ops-litellm-ns", statefulset="litellm-redis-master"}
for: 1s
labels:
component: devops
environment: ops
severity: critical
ilert_routingkey: devops-ops-critical
annotations:
summary: LiteLLM Redis Pods Not Ready
description: |-
LiteLLM Redis Pods are not ready for more than `{{ "{{" }} $for }}`
*StatefulSet*: `{{ "{{" }} $labels.statefulset }}`
*Ready replicas*: `{{ "{{" }} $value }}`
<https://{{ $.Values.monitoring.root_url }}/d/adtt9jj/adrmshg/litellm-system-overview |:grafana: LiteLLM System overview>
Алерт “LiteLLM Redis Failed Requests”
Алерт по litellm_redis_failed_requests_total – помилки виконання запитів від LiteLLM до Redis: інкрементиться на кожну помилку в try/except в redis_cache.py, наприклад при помилках ping інстансу Redis або помилки при роботі з кешем (читання/запис).
В документації вона описана як “litellm_redis_fails“, але в 1.91.0 вона створюється саме як litellm_redis_failed_requests_total, метрики “litellm_redis_fails” в коді вже нема взагалі.
В лейблах мають бути ["error_class", "function_name"] – подивимось, що там буде, бо поки помилок не було взагалі, тому алерт максимально простий:
- alert: LiteLLM Redis Failed Requests
expr: rate(litellm_redis_failed_requests_total[5m]) > 0
for: 1s
labels:
component: devops
environment: ops
severity: critical
ilert_routingkey: devops-ops-critical
annotations:
summary: LiteLLM Redis Failed Requests
description: |-
LiteLLM Redis failed requests have been above 0 for more than `{{ "{{" }} $for }}`
*Failed requests*: `{{ "{{" }} $value }}`
Для PostgreSQL є метрики litellm_postgres_total_requests_total та litellm_postgres_latency_sum, і має бути litellm_postgres_failed_requests_total – але теж поки помилок не було, тому метрика порожня.
Втім, можна додати такий самий простий алерт як для Redis саме по litellm_postgres_failed_requests_total.
Алерт “LiteLLM Proxy Failed Requests”
Окремий алерт на failed responses від LiteLLM клієнтам – якщо вже після всіх спроб retry request або fallback ми клієнту все одно повернули помилку:
- alert: LiteLLM Proxy Failed Requests
expr: |
sum (rate(litellm_proxy_failed_requests_metric_total[5m])) by (api_key_alias, exception_class, exception_status, requested_model) > 0
for: 1s
labels:
component: devops
environment: ops
severity: warning
ilert_routingkey: devops-ops-warning
annotations:
summary: LiteLLM Proxy Failed Requests
description: |-
LiteLLM proxy failed requests have been above 0 for more than `{{ "{{" }} $for }}`
*API key alias*: `{{ "{{" }} $labels.api_key_alias }}`
*Exception class*: `{{ "{{" }} $labels.exception_class }}`
*Exception status*: `{{ "{{" }} $labels.exception_status }}`
*Requested model*: `{{ "{{" }} $labels.requested_model }}`
<https://{{ $.Values.monitoring.root_url }}/d/adrmshg/litellm-system-overview |:grafana: LiteLLM System overview>
Тут корисні лейбли exception_class та exception_status, приклад алерту:
І є окремий алерт для нашого Backend API з label_replace, де створюється додаткова label з іменем environment – аби коректно налаштувати роутинг алертів в різні канали Slack (див. ilert: альтернатива Opsgenie – перше знайомство, Alertmanager, Slack):
...
# create a metric with the environment label
- record: litellm:proxy:failed_requests:environment
expr: |
label_replace(
litellm_proxy_failed_requests_metric_total,
"environment", "$1",
"api_key_alias", ".*-(ops|dev|staging|prod)$"
)
# BACKEND
{{- range .Values.alerts.litellm.backend }}
{{- $env := . }}
{{- range .severities }}
- alert: LiteLLM Kraken Proxy Failed Requests
expr: |
sum (rate(litellm:proxy:failed_requests:environment{environment="{{ $env.env }}"}[5m])) by (api_key_alias, exception_class, exception_status, requested_model) > 0
for: 1s
labels:
severity: {{ . }}
component: backend
environment: {{ $env.env }}
ilert_routingkey: backend-{{ $env.env }}-{{ . }}
annotations:
summary: "LiteLLM Kraken Proxy Failed Requests"
description: |-
LiteLLM Kraken proxy failed requests have been above 0 for more than `{{ "{{" }} $for }}`
*API key alias*: `{{ "{{" }} $labels.api_key_alias }}`
*Exception class*: `{{ "{{" }} $labels.exception_class }}`
*Exception status*: `{{ "{{" }} $labels.exception_status }}`
*Requested model*: `{{ "{{" }} $labels.requested_model }}`
<https://{{ $.Values.monitoring.root_url }}/d/adtt9jj/litellm-services-overview?orgId=1&from=now-2d&to=now&timezone=browser&var-datasource_vm=VictoriaMetrics|:grafana: Litellm Overview>
{{- end }}
{{- end }}
Тут зі значення api_key_alias береться остання частина і задається в лейблу environment (хоча можна просто передавати environment в metadata, див. Custom Metadata Labels, але це треба зміни в коді).
Ну а для імен ключів у нас є такий собі naming convention: <type>-<component>-<feature>-<env>, наприклад – svc-kraken-cortex-dev, де:
svc: тип API-ключа – Service Account (для звичайних юзерів – префіксusr)kraken: наш Backend APIcortex: конкретна фіча/сервіс бекендуdev: оточення
Поки подивлюсь що буде з цією метрикою litellm:proxy:failed_requests:environment і дефолтною litellm_proxy_failed_requests_metric_total – потім вирішу, яку залишити для використання. Чи, може, так і буде – такий собі “системний” алерт з litellm_proxy_failed_requests_metric_total, та per service – з окремою litellm:proxy:failed_requests:environment.
Тут ще є цікавий момент з помилками RateLimitError – див. нижче в частині “OpenAI та помилка RateLimitError“.
Алерт “LiteLLM In-Flight Requests”
Корисна для моніторингу навантаження системи метрика litellm_in_flight_requests: описана в документації в частині Pod Health Metrics, відображає скільки HTTP-запитів в обробці на LiteLLM прямо зараз.
Навіть є такий приклад того, як її використовувати:
– high in_flight_requests + high ALB TargetResponseTime → pod overloaded, scale out
– low in_flight_requests + high ALB TargetResponseTime → delay is pre-ASGI (event loop blocking)
Я поки зробив просто алерт на кількість запитів – далі подивимось, що тут буде в значеннях і, може, трохи перероблю цей алерт:
# In Flight Requests HTTP requests currently in-flight on uvicorn workers
- alert: LiteLLM In Flight Requests
expr: |
avg(litellm_in_flight_requests) > 5
for: 1s
...
Provider/LLM alerts
Окремою групою алерти по статусам безпосередньо провайдерів: тут шлемо нотифікації, якщо клієнти LiteLLM отримують якісь помилки від OpenAI/Anthropic.
Алерт “LiteLLM Deployment/Model Not Ready”
Метрика litellm_deployment_state повертає значення 0 = healthy, 1 = partial outage, 2 = complete outage, і інкрементиться, коли на запит клієнта провайдер повернув код != 200, тому в цілому є сенс алертити:
# Deployment/Model not ready
- alert: LiteLLM Deployment/Model Not Ready
expr: max(litellm_deployment_state) by (api_base, litellm_model_name) > 0
for: 1s
labels:
component: devops
environment: ops
severity: critical
ilert_routingkey: devops-ops-critical
annotations:
summary: LiteLLM Deployment/Model Not Ready
description: |-
LiteLLM deployment/model is not ready for more than `{{ "{{" }} $for }}`
*Model State*: `{{ "{{" }} if eq $value 1.0 {{ "}}" }}1 - Partial outage{{ "{{" }} else if eq $value 2.0 {{ "}}" }}2 - Complete outage{{ "{{" }} else {{ "}}" }}Unknown{{ "{{" }} end {{ "}}" }}`
*Model name*: `{{ "{{" }} $labels.litellm_model_name }}`
*API base*: `{{ "{{" }} $labels.api_base }}`
<https://{{ $.Values.monitoring.root_url }}/d/adtt9jj/adrmshg/litellm-system-overview |:grafana: LiteLLM System overview>
Хоча більш корисним буде наступний алерт, а метрику litellm_deployment_state просто використовувати в Grafana.
Алерт “LiteLLM Deployment/Model Failure Responses”
Є окрема метрика litellm_deployment_failure_responses_total – помилки від провайдера.
Вона схожа на litellm_proxy_failed_requests_metric_total і навіть має ті самі лейбли exception_class та exception_status – але litellm_deployment_failure_responses_total інкрементиться на кожну помилку від провайдера, а litellm_proxy_failed_requests_metric_total – після всіх retry/fallback самою LiteLLM.
Поки зробив обидва алерти – подивимось, від якого буде більше толку.
Сам алерт може виглядати так:
- alert: LiteLLM Deployment/Model Failure Responses
expr: |
sum(increase(litellm_deployment_failure_responses_total[5m])) by (api_key_alias, exception_class, exception_status, requested_model, team_alias, user_agent) > 0
for: 1s
labels:
component: devops
environment: ops
severity: critical
ilert_routingkey: devops-ops-warning
annotations:
summary: LiteLLM Deployment/Model Failure Responses
description: |-
LiteLLM deployment/model failure responses have been above 0 for more than `{{ "{{" }} $for }}`
*API key alias*: `{{ "{{" }} $labels.api_key_alias }}`
*Exception class*: `{{ "{{" }} $labels.exception_class }}`
*Exception status*: `{{ "{{" }} $labels.exception_status }}`
*Team alias*: `{{ "{{" }} $labels.team_alias }}`
*User agent*: `{{ "{{" }} $labels.user_agent }}`
Budgets
Окремий VMRule на бюджети, які задаємо для Teams та API Keys.
Алерт “LiteLLM Budget Low”
Шлемо алерт, якщо у Team залишилось менше 10% його бюджету:
- alert: LiteLLM Team Costs Budget Low
expr: |
(
sum by (team, team_alias) (litellm_remaining_team_budget_metric)
/
sum by (team, team_alias) (litellm_team_max_budget_metric)
) < 0.1
for: 1s
labels:
component: devops
environment: ops
severity: warning
ilert_routingkey: devops-ops-warning
annotations:
summary: LiteLLM Team Budget Low
description: |-
LiteLLM team budget is less than 10% remaining for more than `{{ "{{" }} $for }}`
*Team*: `{{ "{{" }} $labels.team_alias }}`
*Budget left percentage*: `{{ "{{" }} $value | humanizePercentage }}`%
<https://{{ $.Values.monitoring.root_url }}/d/adtt9jj/adrmshg/litellm-system-overview |:grafana: LiteLLM System overview>
І аналогічний – для API Keys, з метрикою litellm_remaining_api_key_budget_metric:
- alert: LiteLLM API Key Budget Low
expr: |
(
sum by (api_key_alias) (litellm_remaining_api_key_budget_metric)
/
sum by (api_key_alias) (litellm_api_key_max_budget_metric)
) < 0.1
for: 1s
labels:
component: devops
environment: ops
severity: warning
ilert_routingkey: devops-ops-warning
annotations:
summary: LiteLLM API Key Budget Low
description: |-
LiteLLM API Key budget is less than 10% remaining for more than `{{ "{{" }} $for }}`
*API Key*: `{{ "{{" }} $labels.api_key_alias }}`
*Budget left percentage*: `{{ "{{" }} $value | humanizePercentage }}`%
<https://{{ $.Values.monitoring.root_url }}/d/adtt9jj/adrmshg/litellm-system-overview |:grafana: LiteLLM System overview>
Limits
Ще одна група – по Requests та Tokens per minute.
Тут у нас можуть бути ліміти як від провайдера – так і ті, що ми задаємо самі для API Key, і, може, є сенс їх розділити – але поки зробив в одному VMRule, тільки в різних groups.
Особливість OpenAI та помилки “RateLimitError”
Виявився цікавий нюанс: OpenAI повертає 429 “RateLimitError” в двох випадках:
- коли дійсно вперлись в ліміт TPM/RPM
- і коли на акаунті закінчились гроші
Тому алерти по litellm_proxy_failed_requests_metric_total або litellm_deployment_failure_responses_total будуть спрацьовувати в обох випадках – але на різні речі, і я кілька годин провів, намагаючись зрозуміти чому у нас значення в litellm_remaining_requests_metric не падає – але при цьому ми ловили RateLimitError.
А документація самого OpenAI Example rate limit error ще більш заплутує, бо показує приклад про RateLimitError з 'code': 'insufficient_quota' – при цьому в тексті пояснення пишуть “when API requests are sent too quickly“.
Може пізніше LiteLLM якось це змінить, бо з трейсів різниця видна, і можна створювати окремі метрики (див. приклад нижче).
Алерт “LiteLLM OpenAI Quota Exhausted”
Тому замість використання litellm_proxy_failed_requests_metric_total і litellm_deployment_failure_responses_total (алерт з ними поки залишається) зробив інакше: додав окремий RecordingRule, який в traces перевіряє значення поля event:event_attr:exception.message і інкрементить метрику vmtraces:litellm:openai:insufficient_quota:rate тільки тоді, коли в тексті є code: "insufficient_quota":
- name: LiteLLM.VictoriaTraces.OpenAI.rules
type: vlogs
interval: 1m
rules:
# OpenAI returns HTTP 429 both for actual rate limiting and exhausted quota.
# Match the structured error code in the LiteLLM exception to distinguish them.
- record: vmtraces:litellm:openai:insufficient_quota:rate
expr: |
{resource_attr:service.name="litellm"} "event:event_attr:exception.message:0":~"\"code\":\\s*\"insufficient_quota\"\\s*\\}\\s*\\}"
| stats by ("resource_attr:service.name") rate() errors_per_second
А далі вже з цією метрикою – окремий алерт конкретно на “Not enough gold, my Lord!” (c):
- name: LiteLLM.Provider.Quota.rules
rules:
# This is separate from rate-limit alerts because OpenAI uses HTTP 429 for
# both rate limiting and an exhausted account/project quota.
- alert: LiteLLM OpenAI Quota Exhausted
expr: vmtraces:litellm:openai:insufficient_quota:rate{stats_result="errors_per_second"} > 0
for: 1s
labels:
component: devops
environment: ops
severity: critical
ilert_routingkey: devops-ops-critical
annotations:
summary: LiteLLM OpenAI Quota Exhausted
description: |-
OpenAI rejected a LiteLLM request with `insufficient_quota`. The account or project has run out of credits; please add funds.
*Service name*: `{{ "{{" }} index $labels "resource_attr:service.name" }}`
<https://platform.openai.com/settings/organization/billing/overview |:openai: OpenAI billing overview>
Аналогічно можна зробити метрику і алерт саме на реальні RPM/TPM ліміти провайдера, щось типу такого:
{resource_attr:service.name="litellm"}
"event:event_attr:exception.message:0":~"(rate_limit_exceeded|Rate limit reached)"
-"insufficient_quota"
Але його треба перевірити, коли отримаємо помилки саме по лімітам RPM/TPM – поки що не було.
Алерти “Provider RPM та TPM Rate Limits”
Додав такий алерт – слати повідомлення, якщо скоро впремось RPM/TPM ліміт від провайдера:
- alert: LiteLLM Provider RPM Rate Limit Low
# less then 10% of the limit, limit taken by the max_over_time which is reseted every minute
expr: |
(
sum by (api_key_alias, api_provider, model_group, litellm_model_name) (litellm_remaining_requests_metric)
/
sum by (api_key_alias, api_provider, model_group, litellm_model_name) (max_over_time(litellm_remaining_requests_metric[1h]))
) < 0.1
for: 1s
labels:
component: devops
environment: ops
severity: warning
ilert_routingkey: devops-ops-warning
annotations:
summary: LiteLLM Provider RPM Rate Limit Low
description: |-
LLM Provider rate limit is less than 10% of the limit for more than `{{ "{{" }} $for }}`
*API key alias*: `{{ "{{" }} $labels.api_key_alias }}`
*API provider*: `{{ "{{" }} $labels.api_provider }}`
*Model group*: `{{ "{{" }} $labels.model_group }}`
*Model name*: `{{ "{{" }} $labels.litellm_model_name }}`
*Rate limit left percentage*: `{{ "{{" }} $value | humanizePercentage }}`%
<https://{{ $.Values.monitoring.root_url }}/d/adtt9jj/adrmshg/litellm-system-overview |:grafana: LiteLLM System overview>
Тут ідея в тому, що значення в litellm_remaining_requests_metric має скидатись кожну хвилину (бо ліміт жеж per minute), і від неї рахуємо “відсоток поточної кількості запитів від максимально доступної”, тобто:
- в
max_over_time(litellm_remaining_requests_metric[1h])беремо максимальне значення – тут має бути загальний ліміт від провайдера - а в
litellm_remaining_requests_metricмаємо поточний залишок ліміту
Але насправді, не впевнений, що це спрацює – бо evaluation interval алертів раз на хвилину і скоріш за все “вікно” для алерту буде пропускатись.
Можна зробити менший interval для LiteLLM VMPodScrape:
...
podMetricsEndpoints:
- port: http
path: /metrics/
interval: 15s
Але маємо на увазі, що зменшення interval в Scrape Job збільшить кількість samples в базі даних VictoriaMetrics (див. VictoriaMetrics: Churn Rate, High cardinality, метрики та IndexDB – там розбирав Metric vs Time Series vs Samples).
Якщо зменшуємо interval для збору метрик – то зменшуємо і evaluation interval конкретно для цієї для VMRules group:
...
- name: LiteLLM.Provider.Limits.rules
interval: 10s
rules:
# by (api_key_alias, api_provider, model_group, litellm_model_name)
# RPM Rate limit low
- alert: LiteLLM Provider RPM Rate Limit Low
# less then 10% of the limit, limit taken by the max_over_time which is reseted every minute
expr: |
(
sum by (api_key_alias, api_provider, model_group, litellm_model_name) (litellm_remaining_requests_metric)
/
sum by (api_key_alias, api_provider, model_group, litellm_model_name) (max_over_time(litellm_remaining_requests_metric[1h]))
) < 0.1
...
Подивимось, може пізніше перероблю чи приберу взагалі, хоча в цілому алерт виглядає корисним.
Аналогічно – алерт по TPM limit, з метрикою litellm_remaining_tokens_metric:
- alert: LiteLLM Provider TPM Rate Limit Low
# less then 10% of the limit, limit taken by the max_over_time which is reseted every minute
expr: |
(
sum by (api_key_alias, api_provider, model_group, litellm_model_name) (litellm_remaining_tokens_metric)
/
sum by (api_key_alias, api_provider, model_group, litellm_model_name) (max_over_time(litellm_remaining_tokens_metric[1h]))
) < 0.1
for: 1s
...
Алерти “LiteLLM API Key RPM та TPM Rate Limit”
Аналогічні алерти – але вже по лімітам, які ми задаємо самі для ключів в самій LiteLLM, ну і з тою самою проблемою з evaluation interval.
Алерт на RPM:
- alert: LiteLLM API Key RPM Rate Limit Low
expr: |
(
sum by (api_key_alias, model) (litellm_remaining_api_key_requests_for_model)
/
sum by (api_key_alias, model) (max_over_time(litellm_remaining_api_key_requests_for_model[1h]))
) < 0.1
for: 1s
labels:
component: devops
environment: ops
severity: warning
ilert_routingkey: devops-ops-warning
annotations:
summary: LiteLLM API Key RPM Rate Limit Low
description: |-
LiteLLM API key rate limit is less than 10% of the limit for more than `{{ "{{" }} $for }}`
*API key alias*: `{{ "{{" }} $labels.api_key_alias }}`
*Model*: `{{ "{{" }} $labels.model }}`
*Rate limit left percentage*: `{{ "{{" }} $value | humanizePercentage }}`%
І на TPM:
- alert: LiteLLM API Key TPM Rate Limit Low
expr: |
(
sum by (api_key_alias, model) (litellm_remaining_api_key_tokens_for_model)
/
sum by (api_key_alias, model) (max_over_time(litellm_remaining_api_key_tokens_for_model[1h]))
) < 0.1
for: 1s
...
Metrics and Alerts from VictoriaLogs
Пара прикладів того, як можемо з Recording Rules робити метрики та алерти. Про Recording Rules детальніше писав в пості VictoriaLogs: створення Recording Rules з VMAlert.
Зараз в них великого сенсу нема, описані нижче робив ці на самому початку, коли тільки переключали наш Backend API – аби на додачу до метрик самої LiteLLM мати алерт на помилки в самому Backend API, але як приклад нехай буде.
Тут з логів самого Backend API грепаються помилки “LLM request failed” та “Invalid model name passed” і генеруються метрики vmlogs:litellm:logs:kraken:llm_request_failed:rate та vmlogs:litellm:logs:kraken:invalid_model_name_passed:rate:
- name: LiteLLM.VictoriaLogs.Logs.rules
type: vlogs
interval: 1m
rules:
- record: vmlogs:litellm:logs:kraken:llm_request_failed:rate
expr: |
component:="backend" | "LLM request failed"
| extract_regexp "Error code: (?P<error_code>\\d+).*"
| extract_regexp "'message': '(?P<rate_error_msg>[^']+)'"
| extract_regexp `'message': \\"(?P<auth_error_msg>[^":,]+)`
| stats by (container, namespace, error_code, rate_error_msg, auth_error_msg) rate()
- record: vmlogs:litellm:logs:kraken:invalid_model_name_passed:rate
expr: |
component:="backend" | "Invalid model name passed"
| extract_regexp "Error code: (?P<error_code>\\d+).*"
| extract_regexp "'message': '(?P<error_msg>[^']+)'"
| stats by (container, namespace, error_code, error_msg) rate()
А потім з цими метриками маємо два алерти:
{{- range .Values.alerts.litellm.backend }}
{{- $ns := . }}
{{- range .severities }}
- alert: LiteLLM Kraken LLM Error Rate High
expr: avg(vmlogs:litellm:logs:kraken:llm_request_failed:rate{namespace="{{ $ns.namespace }}"}) by (container, namespace, error_code, rate_error_msg, auth_error_msg) > 0
for: 1s
labels:
severity: {{ . }}
component: backend
environment: {{ $ns.env }}
ilert_routingkey: backend-{{ $ns.env }}-{{ . }}
annotations:
summary: "LiteLLM Kraken LLM Error Rate High"
description: |-
LiteLLM Kraken LLM error rate has been above 0 for more than `{{ "{{" }} $for }}`
*Error code*: `{{ "{{" }} $labels.error_code }}`
*Error message*: {{ "{{" }} $labels.rate_error_msg }}
*Auth error message*: `{{ "{{" }} $labels.auth_error_msg }}`
*Container*: `{{ "{{" }} $labels.container }}`
*Namespace*: `{{ "{{" }} $labels.namespace }}`
<https://{{ $.Values.monitoring.root_url }}/d/adtt9jj/litellm-services-overview?orgId=1&from=now-2d&to=now&timezone=browser&var-datasource_vm=VictoriaMetrics|:grafana: Litellm Overview>
{{- end }}
{{- end }}
...
- alert: LiteLLM Kraken Invalid Model Name Passed Error Rate High
expr: avg(vmlogs:litellm:logs:kraken:invalid_model_name_passed:rate{namespace="{{ $ns.namespace }}"}) by (container, namespace, error_code, error_msg) > 0
for: 1s
...
Grafana dashboards
Цей пост і так вже вийшов довгий – але виносити Grafana dashboards окремим не бачу сенсу, тому нехай буде тут – але якось коротко.
В репозиторії LiteLLM є власні дашборди – але вони прям зовсім… ніякі.
Тому, як зазвичай і роблю – створив власні такі собі “overview dashboards”, дві штуки – одна більше “системна” по стану самого сервісу, друга – більше product oriented з інформацією по Costs різними сервісами і клієнтами.
Хоча в якійсь нещодавньому апгрейді Grafana завезла підтримку Tabs, коли на одній борді можна зробити два окремих набори візуалізацій – але я по-старінкє зробив окремі борди.
100% буду їх перероблювати по ходу використання – але поки так, мінімально корисна інформація.
“LiteLLM System Overview” dashboard
Тут просто загальні дані по використанню та стану самої системи – це більше для мене, суто “технічна”, тому навіть не додавав якихось фільтрів.
Панель “Total Spent”
Stats панель з інформацією по витратам за обраний в Grafana time range період часу:
sum(increase(litellm_spend_metric_total[$__range]))
Панель “Model State”
Просто загальна картина ситуації з помилками провайдерів та моделей:
max(litellm_deployment_state) by (litellm_model_name)
Графіки по Latency
Тут у нас є лейтенсі загальна – і всякі TTFT та TPOT.
Загальні:
litellm_request_total_latency_metric_bucket: скільки реально чекав клієнт – повний час на обробку запиту від отримання від клієнта + LiteLLM overhead + відправка провайдеру + очікування відповіді + повернення відповіді клієнтуlitellm_llm_api_latency_metric_bucket: скільки LiteLLM чекав на відповідь від провайдераlitellm_overhead_latency_metric_bucket: скільки додав сам LiteLLM доlitellm_llm_api_latency_metric_bucket
Що ми можемо побачити з них:
- росте Total та Provider, але Overhead стабільний: можлива проблема у провайдера (або нетворк)
- росте Total та Overhead, але Provider стабільний: проблема на самому LiteLLM, треба глянути ресурси Pods
Плюс маємо метрики по Time To First Token (TTFT) та Time Per Output Token (TPOT):
litellm_llm_api_time_to_first_token_metric_bucket(TTFT): час очікування до отримання першого токена від провайдера (тільки для streaming requests)litellm_deployment_latency_per_output_token(TPOT): latency per output token по конкретній моделі – скільки в середньому займає генерація кожного токена у відповіді
Але з TPOT (litellm_deployment_latency_per_output_token) є питання в тому, як воно рахується, бо метрика заповнюється як “latency_per_token = _latency_seconds / output_tokens” (див. prometheus.py).
А тому:
- в non-streaming: модель згенерувала відповідь на 1000 токенів за 10 000 мс – маємо ~10 мс на генерацію одного токена.
- при streaming: TTFT фіксується в момент першого токена стріму, і він не залежить від того, скільки токенів вийде у відповіді (
output_tokens) – цих токенів на момент TTFT ще навіть не існує, а оскільки формула ділить саме TTFT на кількість токенів відповіді – то для streaming так рахувати TPOT немає сенсу
Тому поки зробив дві візуалізації – перша по “загальним” latency:
histogram_quantile(
0.95,
sum(
rate(litellm_request_total_latency_metric_bucket[$__rate_interval])
) by (le)
)
І аналогічно запити для litellm_llm_api_latency_metric_bucket і litellm_overhead_latency_metric_bucket:
І друга панель – по TTFT/TPOT, тільки streaming у нас зараз нема – тому маємо картину тільки для TPOT:
histogram_quantile(
0.50,
sum by (le) (
rate(litellm_llm_api_time_to_first_token_metric_bucket[$__rate_interval])
)
)
Панель “Success Responses”
Тут просто рахуємо % успішних відповідей від загальної кількості:
sum by (api_provider) (rate(litellm_deployment_success_responses_total[5m])) / sum by (api_provider) (rate(litellm_deployment_total_requests_total[5m])) * 100
Панель “Gateway Failed Requests”
Графік помилок від LiteLLM клієнтам:
sum(rate(litellm_proxy_failed_requests_metric_total[5m])) by (exception_class) / sum(rate(litellm_proxy_total_requests_metric_total[5m])) * 100
Панель “Provider Failed Requests Rate”
Окремо помилки від провайдера (але пам’ятаємо, що тут Openai.RateLimitError спрацьовує і на “закінчились гроші”):
sum(rate(litellm_deployment_failure_responses_total[5m])) by (exception_class)
Resulted dashboard
В результаті вся дашборда зараз виглядає так:
“LiteLLM Services Overview” dashboard
Ця борда багато в чому схожа на System dashboard – але більш заточена під менеджерів та девелоперів, яким цікаве використання LLM та гроші.
Dashboard variables
Тут додав фільтри, аби була можливість подивитись дані по конкретним Teams або API Keys:
Запит для отримання списку API Keys:
sum(litellm_spend_metric_created{api_key_alias=~".*$environment"}) by (api_key_alias)
І регуляркою вирізаємо саме ім’я ключа:
Total Spend by Feature
Тут “Feature” – це більше для менеджерів, бо фактично це “spent by API Key”:
sort_desc(sum(increase(litellm_spend_metric_total[$__range])) by (api_key_alias))
Тут з MetricsQL та функцією sort_desc() сортуємо результат, що перший йшов той ключ, у якого найбільше витрат.
Тип візуалізації – Bar gauge:
І аналогічний запит для візуалізації Pie chart – але тут % витрат кожним ключем від загальних витрат:
Панель “Spent Rate by User”
У нас через metadata в реквестах додається end_user (див. Customers / End-Users) – поки проект невеликий, то можна собі дозволити.
І є окремий графік використання кожним юзером: можемо подивитись який юзер “скільки нам коштує” по кожній фічє/API Key:
sum(increase(litellm_spend_metric_total{api_key_alias=~"$api_key", team_alias=~"$team"}[$__interval])) by (end_user)
and on(end_user)
topk(5, sum(increase(litellm_spend_metric_total{api_key_alias=~"$api_key", team_alias=~"$team"}[$__range])) by (end_user))
Панель “Remaining Budget by Team та API Key”
Рахуємо % використаних бюджетів by (team_alias):
sum by (team_alias) (litellm_remaining_team_budget_metric{team_alias=~"$team"})
/
sum by (team_alias) (litellm_team_max_budget_metric{team_alias=~"$team"})
* 100
Та by (api_key_alias):
sum by (api_key_alias) (litellm_remaining_api_key_budget_metric) / sum by (api_key_alias) (litellm_api_key_max_budget_metric) * 100
Resulted dashboard
І вся борда разом виглядає так:
Власне, на цьому поки і все.
Потроху переключаємо сервіси на LiteLLM, ще багато чого цікавого і корисного будемо робити. В найближчих планах – протестувати parallel requests одночасно до кількох провайдерів (див. Batch Completions – pass multiple models) – бо вже хочемо мати self-hosted LLM, і треба буде робити порівняння моделей OpenAI/Anthropic та наших власних моделей.
Але про це вже to be continued в наступних постах.
![]()















