LiteLLM: моніторинг з VictoriaMetrics – алерти та Grafana
0 (0)

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

Це друга частина матеріалу про моніторинг LiteLLM – у попередній ми розібрали загальну інтеграцію з VictoriaStack, а також подивилися, які метрики й трейси отримуємо від LiteLLM (див. LiteLLM: метрики, traces та інтеграція з VictoriaMetrics Stack).

Тепер перейдемо до практичної частини – що відстежувати та як це можна організувати.

Втім, хоча LiteLLM у нас уже працює в production, система все ще перебуває в процесі “допилювання” та “спостереження за поведінкою”, а тому описані в цьому пості алерти й дашборди не є фінальним варіантом – це, скоріше, демонстрація можливих підходів до контролю за LiteLLM, а також приклади запитів до VictoriaMetrics і VictoriaTraces.

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

Всі пости цієї серії:

І далі буде ще – слідкуйте за апдейтами в Telegram @rtfmcoua або LinkedIn group RTFM! DevOps[at]UA.

Alerts

У нас кілька основних компонентів які треба моніторити і по яким треба алертити:

  • сама система: загальні алерти по статусу Pods, failed requests, latency, in-flight requests
    • Redis: статус самого Redis та його Pods, помилки
      • для real production можна додати Redis Prometheus експортер
    • PostgreSQL: взагалі, звісно, за сервером баз даних теж треба слідкувати – але в моєму випадку це вже існуючий сервер і він вже давно “покритий моніторингом”
  • 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: тільки час відповіді від провайдера/LLM
    • litellm_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, приклад алерту:

LiteLLM: моніторинг з VictoriaMetrics, алерти та Grafana

І є окремий алерт для нашого 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 API
  • cortex: конкретна фіча/сервіс бекенду
  • 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]))

LiteLLM: моніторинг з VictoriaMetrics, алерти та Grafana

Панель “Model State”

Просто загальна картина ситуації з помилками провайдерів та моделей:

max(litellm_deployment_state) by (litellm_model_name)

LiteLLM: моніторинг з VictoriaMetrics, алерти та Grafana

Графіки по 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:

LiteLLM: моніторинг з VictoriaMetrics, алерти та Grafana

І друга панель – по TTFT/TPOT, тільки streaming у нас зараз нема – тому маємо картину тільки для TPOT:

histogram_quantile(
  0.50,
  sum by (le) (
    rate(litellm_llm_api_time_to_first_token_metric_bucket[$__rate_interval])
  )
)

LiteLLM: моніторинг з VictoriaMetrics, алерти та Grafana

Панель “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

LiteLLM: моніторинг з VictoriaMetrics, алерти та Grafana

Панель “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

LiteLLM: моніторинг з VictoriaMetrics, алерти та Grafana

Панель “Provider Failed Requests Rate”

Окремо помилки від провайдера (але пам’ятаємо, що тут Openai.RateLimitError спрацьовує і на “закінчились гроші”):

sum(rate(litellm_deployment_failure_responses_total[5m])) by (exception_class)

LiteLLM: моніторинг з VictoriaMetrics, алерти та Grafana

Resulted dashboard

В результаті вся дашборда зараз виглядає так:

LiteLLM: моніторинг з VictoriaMetrics, алерти та Grafana

“LiteLLM Services Overview” dashboard

Ця борда багато в чому схожа на System dashboard – але більш заточена під менеджерів та девелоперів, яким цікаве використання LLM та гроші.

Dashboard variables

Тут додав фільтри, аби була можливість подивитись дані по конкретним Teams або API Keys:

LiteLLM: моніторинг з VictoriaMetrics, алерти та Grafana

Запит для отримання списку API Keys:

sum(litellm_spend_metric_created{api_key_alias=~".*$environment"}) by (api_key_alias)

І регуляркою вирізаємо саме ім’я ключа:

LiteLLM: моніторинг з VictoriaMetrics, алерти та Grafana

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:

LiteLLM: моніторинг з VictoriaMetrics, алерти та Grafana

І аналогічний запит для візуалізації Pie chart – але тут % витрат кожним ключем від загальних витрат:

LiteLLM: моніторинг з VictoriaMetrics, алерти та Grafana

Панель “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))

LiteLLM: моніторинг з VictoriaMetrics, алерти та Grafana

Панель “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

LiteLLM: моніторинг з VictoriaMetrics, алерти та Grafana

Та 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: моніторинг з VictoriaMetrics, алерти та Grafana

Власне, на цьому поки і все.

Потроху переключаємо сервіси на LiteLLM, ще багато чого цікавого і корисного будемо робити. В найближчих планах – протестувати parallel requests одночасно до кількох провайдерів (див. Batch Completions – pass multiple models) – бо вже хочемо мати self-hosted LLM, і треба буде робити порівняння моделей OpenAI/Anthropic та наших власних моделей.

Але про це вже to be continued в наступних постах.

Loading