В попередніх постах по OpenTelemetry і VictoriaTraces (див. OpenTelemetry: OTel Collectors в Kubernetes та інтеграція з VictoriaMetrics stack та VictoriaTraces: Tracing, Observability та OpenTelemetry) розбирали загальні концепції того, що таке observability та як працювати з трейсами.
Але взагалі на проекті ця тема з’явилась коли ми зрозуміли, що використання LLM стає важливою частиною нашого продукту – але на відміну від інших компонентів в мене нема ніякого моніторингу того, як взагалі LLM використовується, скільки токенів витрачає кожен із сервісів, скільки помилок ми отримуємо.
Тому почали все це діло “обмазувати” трейсингом для отримання даних від наших сервісів. На додачу у нас є окремий самописний OpenAI Exporter, який з OpenAI API збирає дані по токенам і витраченим грошам.
Втім, коли все “обмазали” і почали отримувати дані з різних систем в єдиний бекенд – VictoriaMetrics/VictoriaTraces – та робити дашборди в Grafana і метрики з VMAlert, то виявилась одна неприємна річ: різні компоненти використовують різні бібліотеки для роботи з LLM, різні бібліотеки для створення spans, по різному створюють атрибути – а тому доводиться робити різні дашборди, різні алерти.
Варіанти рішення тут, звісно, є: або переписувати код всіх систем з використанням однакових бібліотек – або створювати кастомні атрибути для спанів, щоб вони були однакові в усіх сервісах.
Але це, по-перше – багато змін в коді, по-друге – не хочеться обмежувати девелоперів правилами типу “використовуй тільки цю бібліотеку” або “завжди додавай такі атрибути до спанів”.
Тому вирішив подивитись на інший підхід: нехай всі роблять те, що хочуть – але всі запити від всіх систем відправляти через єдиний шлюз, AI Gateway – а він вже сам буде створювати і трейси, і метрики – і тоді у всіх буде загальний контекст у вигляді загальних атрибутів/лейблів/метрик.
Крім того, загальний гейтвей вирішить ще пачку задач – і централізований менеджмент доступів, і бюджети з лімітами, і failover між OpenAI/Anthropic, якщо одна система впала.
Сьогодні подивимось на те, що таке LiteLLM взагалі, поганяємо локально в Docker, а потім, якщо сподобається (а so far – подобається, хоча деякі питання виникли) – то запустимо в Kubernetes та інтегруємо з нашим існуючим стеком моніторингу – VictoriaMetrics, VictoriaLogs, VictoriaTraces, Grafana.
Зміст
LiteLLM – основні можливості
Сторінка проекту – litellm.ai, вся документація – docs.litellm.ai, GitHub проекту – BerriAI/litellm.
Отже, що таке таке LiteLLM: це система для створення єдиного шлюзу, яка через себе проксює всі запити до LLM і різних провайдерів – Backend API може слати запити до AWS Bedrock для RAG, клієнтські AI Agents можуть слати запити до OpenAI або Anthropic, і навіть Claude Code девелоперів можна зароутити через цей шлюз та отримати картину того, хто і скільки токенів використовує (правда, у випадку з Claude Code питання використання API, бо LiteLLM наче не вміє працювати через subscription – тільки API).
При цьому ми спокійно залишаємо вже існуючі метрики та трейси, які вже створюються із сервісів – бо вони вже звичні девелоперам і трохи інтегровані в наш моніторинг. А на додачу до них – отримаємо нові, з загальним контекстом для всього нашого проекту та AI/LLM в ньому.
Є також LiteLLM Python SDK – можна мати всі можливості LiteLLM прямо з коду без необхідності піднімати окремий proxy service.
З цікавих можливостей LiteLLM:
- Admin Web UI: єдиний веб-інтерфейс для моніторингу і налаштувань
- Alerting & Monitoring: з коробки маємо логи, метрики, алерти, інтеграцію з Prometheus/VictoriaMetrics та системами типу Phoenix/Langfuse
- Cost tracking: з коробки автоматично моніторить витрати на роботу з моделями, повертає метрики та трейси з вартістю, можна налаштовувати бюджети на різні ключі, команди, юзерів
- Centralized authentication: єдина система для управління доступами – групи, юзери, ключі, окремі бюджети і ліміти, та навіть обмеження доступу до LiteLLM по IP
- Budgets, Rate Limits: багато налаштувань для контролю використання провайдерів
- Skills Registry: тримаємо всі скіли в одному місці – але це начебто тільки для Claude Code
- MCP Gateway: можна мати всі налаштовані MCP servers на LiteLLM – і клієнти типу VSCode, Cursor, Claude Code просто звертаються до нього
- Agent Gateway: можна мати проксі для agent-to-agent комунікації і моніторити всю цю активність
- LLM Response caching: LiteLLM може тримати кеш відповідей від LLM – на той самий запит від клієнта повертати закешовану відповідь, а не робити новий запит до LLM
- див. також Prompt Caching
- Memory: зберігання налаштувань і контексту між сесіями
- Vector Store: LiteLLM може грати роль проксі до різних Vector Stores і записувати додаткові дані для моніторингу
- Guardrails: захист конфіденційних даних – prompt injection, маскування даних юзерів
- Policies: є набір готових політик, можна створювати власні
- Load Balancing: автоматичне балансування між різними провайдерами та/або моделями в залежність від навантаження чи пріорітетів
- Model Health Status: перевірка статусу LLM і виключення з роутінгу тих провайдерів, які недоступні
- Fallbacks: автоматичний роутинг запитів, якщо модель чи провайдер недоступні
- Traffic Mirroring: цікава можливість – відправляти запити одночасно до двох різних моделей, аби порівнювати результати їхньої роботи
Для чого це нам?
Кожного разу, коли хочеться запустити щось новеньке – треба спитати себе “А яку, власне, проблему ми вирішуємо”?
Конкретно в нашому випадку це:
- менеджмент доступів: замість 100500 API ключів в OpenAI/Anthropic – мати налаштовані групи в LiteLLM, кожен з власними бюджетами і лімітами
- моніторинг: мати загальні метрики, логи, трейси з загальними лейблами/атрибутами
- failover: мати можливість автоматично переключитись на іншого провайдера, якщо на поточному вперлись в ліміти (або якщо Claude знову впав)
Запуск LiteLLM з Docker
Для повноцінної роботи Лайт потрібна база даних – в ній будуть зберігатись всі юзери та групи, налаштування моделей, бюджетів, витрати на LLM, див. What is stored in the DB.
Тому з Docker створимо два контейнери – сам Gateway та PostgreSQL для нього.
Документація – Getting Started Tutorial.
Для Production setup ще варто додавати Redis – але про це будемо говорити в наступній частині. Див. Deployment Options та High Availability Setup (Resolve DB Deadlocks).
Для запуску в Kubernetes є Helm chart (beta) і неофіційний litellm-operator.
Config.yaml – налаштування LiteLLM
Перед запуском LiteLLM нам потрібен конфіг того, як він буде працювати – див. документацію Config.yaml, а всі параметри в All settings.
Створюємо файл litellm_config.yaml з мінімальними налаштуваннями:
model_list:
- model_name: gpt-4o-mini
litellm_params:
model: openai/gpt-4o-mini
api_key: os.environ/OPENAI_API_KEY
general_settings:
master_key: os.environ/LITELLM_MASTER_KEY
litellm_settings:
callbacks:
- prometheus
Тут:
model_list: список моделей, які будуть доступні клієнтам – див. LLM configs model_listmodel_name: ім’я, яке отримуємо в запиті від клієнта (як ми його будемо вказувати в коді, наприкладclient.chat.completions.create(model="gpt-4o-mini")- кожна модель в цьому списку – це окремий deployment в термінології LiteLLM – див. Quick Start та Proxy – Load Balancing
litellm_params: параметри провайдера для цієї моделі
general_settings: див. General Settings general_settingsmaster_key: головний ключ для аутентифікації і в Web UI для адміна- тут же можна передати і параметри для бази даних, але зараз зробимо через змінні оточення
litellm_settings: налаштування самого LiteLLM – моніторинг, логгінг, кешування, див. litellm_settings – Referencecallbacks: відкриваємо метрики (ендпоінт/metrics/), пізніше сюди додамо відправку трейсів, див. Prometheus metrics
Створюємо docker-compose.yml – описуємо запуск самого LiteLLM та PostgreSQL для нього:
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_USER: litellm
POSTGRES_PASSWORD: litellm
POSTGRES_DB: litellm
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U litellm"]
interval: 5s
timeout: 5s
retries: 5
litellm:
image: ghcr.io/berriai/litellm:main-latest
ports:
- "4000:4000"
volumes:
- ./litellm_config.yaml:/app/config.yaml
env_file:
- .env
environment:
DATABASE_URL: postgresql://litellm:litellm@postgres:5432/litellm
LITELLM_LOG: DEBUG
command: ["--config", "/app/config.yaml", "--port", "4000"]
depends_on:
postgres:
condition: service_healthy
volumes:
postgres_data:
Тут PostgreSQL з health check, який використовується інстансом LiteLLM, а через змінну оточення DATABASE_URL для LiteLLM передаємо connection string для підключення до бази даних.
Генеруємо ключ для $LITELLM_MASTER_KEY – в OpenAI форматі, з префіксом sk- (“secret key”):
$ echo "sk-$(openssl rand -hex 16)" sk-db69761228204509fa80f934bff6e0f5
Задаємо в змінні оточення:
$ export LITELLM_MASTER_KEY=sk-db69761228204509fa80f934bff6e0f5
Створюємо файл .env з API ключем для OpenAI та самого LiteLLM:
$ echo "OPENAI_API_KEY=$OPENAI_API_KEY" > .env $ echo "LITELLM_MASTER_KEY=$LITELLM_MASTER_KEY" >> .env
Запускаємо – перший запуск буде кілька хвилин, поки накатяться всі міграції в базу.
Чекаємо на повідомлення “Application startup complete” та “Uvicorn running on“:
$ docker compose up ... litellm-1 | INFO: Waiting for application startup. litellm-1 | litellm-1 | ██╗ ██╗████████╗███████╗██╗ ██╗ ███╗ ███╗ litellm-1 | ██║ ██║╚══██╔══╝██╔════╝██║ ██║ ████╗ ████║ litellm-1 | ██║ ██║ ██║ █████╗ ██║ ██║ ██╔████╔██║ litellm-1 | ██║ ██║ ██║ ██╔══╝ ██║ ██║ ██║╚██╔╝██║ litellm-1 | ███████╗██║ ██║ ███████╗███████╗███████╗██║ ╚═╝ ██║ litellm-1 | ╚══════╝╚═╝ ╚═╝ ╚══════╝╚══════╝╚══════╝╚═╝ ╚═╝ litellm-1 | litellm-1 | query-engine ac9d7041ed77bcc8a8dbd2ab6616b39013829574 litellm-1 | INFO: Application startup complete. litellm-1 | INFO: Uvicorn running on http://0.0.0.0:4000 (Press CTRL+C to quit)
Заходимо на http://0.0.0.0:4000 – тут посилання на Admin UI та документація по API самого LiteLLM (Swagger Docs можна відключити з NO_DOCS=true, див. environment variables – Reference, але вцілому її цікаво глянути – бо можливостей в API дуже багато):
Логінимось в адмінку – дефолтний логін “admin“, пароль – $LITELLM_MASTER_KEY, який створювали вище:
І попадаємо в дуже приємний інтерфейс:
Вже маємо метрики, але ендпоінт саме /metrics/ – зі слешем в кінці (хоча в документації вказаний як /metrics):
$ curl -s http://localhost:4000/metrics/ ... # HELP litellm_in_flight_requests Number of HTTP requests currently in-flight on this uvicorn worker # TYPE litellm_in_flight_requests gauge litellm_in_flight_requests 1.0
По різним налаштуванням пройдемось далі – зараз давайте створимо “клієнта” – простенький скрипт, який звертається до OpenAI через LiteLLM.
Demo Python App – AI Client
Пишемо скрипт, який використовує OpenAI і передає один промпт:
#!/usr/bin/env python
import os
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:4000",
api_key=os.getenv("LITELLM_MASTER_KEY"),
)
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "Say hello in one sentence"}],
)
print(response.choices[0].message.content)
print(f"Tokens: {response.usage}")
Тут:
base_urlдля OpenAI: замість дефолтного OpenAI ендпоінта api.openai.com перевизначаємо ендпоінт нашого інстансу LiteLLMmodel: ім’я моделі, як ми його задавали в параметрах LiteLLM –model_list.model_name
Встановлюємо залежності:
$ python3 -m venv .venv $ source .venv/bin/activate $ pip install openai
Запускаємо скрипт:
$ ./demo-llm.py Hello! How can I assist you today? Tokens: CompletionUsage(completion_tokens=9, prompt_tokens=12, total_tokens=21, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0))
Переходимо в адмінку > Usage – і вже маємо дані по запитам:
Трейси дивимось в Logs:
Та нові метрики:
$ curl -s http://localhost:4000/metrics/ | grep "# HELP lite" # HELP litellm_in_flight_requests Number of HTTP requests currently in-flight on this uvicorn worker # HELP litellm_proxy_failed_requests_metric_total Total number of failed responses from proxy - the client did not get a success response from litellm proxy # HELP litellm_proxy_total_requests_metric_total Total number of requests made to the proxy server - track number of client side requests # HELP litellm_proxy_total_requests_metric_created Total number of requests made to the proxy server - track number of client side requests ... # HELP litellm_total_users Total number of users in LiteLLM # HELP litellm_teams_count Total number of teams in LiteLLM
Тепер, як маємо сам LiteLLM та клієнта – можна подивитись що ж можемо з LiteLLM цікавого робити, і перше, що цікавить особисто мене – це моніторинг.
Monitoring, OpenTelemetry та Traces
Метрики будемо збирати з VMAgent або OTel Collector, логи – просто аутпут, який, якщо в Kubernetes, то збираємо з Promtail, vlagent, OTel filelog, whatever.
Цікаві метрики розберемо далі, але сьогодні їх збирати не будемо – бо зараз все локально в Docker, документація по всім доступним метрикам – Prometheus metrics.
А от глянути як можна писати трейси до VictoriaTraces можемо.
Документація – OpenTelemetry та OpenTelemetry – Tracing LLMs with any observability tool.
OpenTelemetry та VictoriaTraces
LiteLLM може писати дані з OpenTelemetry Protocol. Для VictoriaTraces використовуємо ендпоінт /insert/opentelemetry/v1/traces, див. Data ingestion.
Відкриваємо локальний порт до інстансу VictoriaTraces в Kubernetes, аби отримати доступ із Docker container з LiteLLM – додаємо --address=0.0.0.0:
$ kk port-forward svc/atlas-victoriametrics-vt-single-server 10428 --address=0.0.0.0
В docker-compose.yaml додаємо параметр extra_hosts:
litellm:
image: ghcr.io/berriai/litellm:main-latest
...
extra_hosts:
- "host.docker.internal:host-gateway"
В .env для LiteLLM додаємо змінні для відправки трейсів:
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://host.docker.internal:10428/insert/opentelemetry/v1/traces OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
Можна задати через власні змінні LiteLLM – OTEL_EXPORTER та OTEL_ENDPOINT:
OTEL_ENDPOINT=http://host.docker.internal:10428/insert/opentelemetry/v1/traces OTEL_EXPORTER=otlp_http
Трохи mess в документації (хоча взагалі документація дуже класна) – але обидва варіанти працюють. Див. також Exporter & resource.
В конфіг litellm_config.yaml до callbacks додаємо “otel” – включаємо відправку трейсів:
... litellm_settings: callbacks: - prometheus - otel
Замість або на додачу до “otel” можна вказати “langfuse” або “arize” для Phoenix (див. Arize Phoenix: сервіс моніторингу LLM – запуск в Kubernetes) – тоді трейси будуть відправлятись в кілька сервісів – тестив, працює, зручно, прикольно.
Перезапускаємо контейнери, в логах маємо побачити, що експортери активні:
... 11:31:57 - LiteLLM Proxy:DEBUG: callback_utils.py:34 - initializing callbacks=['prometheus', 'otel'] on proxy ... litellm-1 | self.OTEL_EXPORTER: otlp_http litellm-1 | self.OTEL_ENDPOINT: http://host.docker.internal:10428/insert/opentelemetry/v1/traces litellm-1 | self.OTEL_HEADERS: None ...
Перевіряємо, що змінні оточення з .env застосувались – при першому запуску трохи довелось подебажити:
$ docker compose exec litellm env | grep OTEL OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://host.docker.internal:10428/insert/opentelemetry/v1/traces OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
Ще раз запускаємо наш скрипт клієнта:
$ ./demo-llm.py Hello! How can I assist you today? Tokens: CompletionUsage(completion_tokens=9, prompt_tokens=12, total_tokens=21, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0))
І в VictoriaTraces шукаємо спани по {"resource_attr:service.name"="litellm"}:
Або відразу в Grafana з групуванням:
LiteLLM Span Attributes
Атрибутів прям дуже багато – просто відразу маємо все, без всяких інструментаріїв в коді.
“Корневий” спан у нас буде “Received Proxy Server Request“, див. Span Hierarchy та Span name reference.
Див. також документацію самого OpenTelemetry Semantic conventions for generative client AI spans – імена спанів та атрибутів.
Всі атрибути разом простіше подивитись в самій VictoriaTraces:
Також можна включити спани LiteLLM як дочірні до спанів клієнта, якщо там налаштований трейсінг – див. Context propagation (W3C traceparent).
Всі атрибути див. в Attributes Reference, тут коротко з основних, які можуть бути цікавими далі для моніторингу.
Costs – можна будувати графіки витрат по моделях, юзерах, командах:
gen_ai.cost.total_cost: скільки грошей витратили на обробку запитуgen_ai.cost.input_cost/gen_ai.cost.output_cost: розбивка вартості по вхідним/вихідним токенам
Tokens Usage:
gen_ai.usage.input_tokens/output_tokens/total_tokens
Execution time – час обробки запиту:
duration: загальний час обробкиhidden_params=>litellm_overhead_time_ms: скільки часу зайняла робота самого проксі
Model і провайдер:
gen_ai.request.model: яка модель використовувалась в запитіgen_ai.response.model: яка модель реально відповіла (може відрізнятись, наприклад – якщо спрацював fallback)gen_ai.system: провайдер
Дані по юзеру:
metadata.user_api_key_hash: який ключ використовувавсяmetadata.user_api_key_user_id/team_id: хто зробив запит – конкретний юзер чи групаmetadata.requester_ip_address: адреса клієнта
Rate limits від провайдера (в hidden_params), див. Rate Limit Headers:
x_ratelimit_remaining_requests: скільки запитів ще залишилось до того, як провайдер почне повертати помилку 429 – Too Many Requestsx_ratelimit_remaining_tokens: те саме, але по токенам
Content (може містити конфіденційні дані, можна відключити, див. Redacting Messages, Response Content та Capturing Message Content):
gen_ai.input.messages: сам промптgen_ai.output.messages: відповідь моделі
Status запиту:
status_code: 1 – OK (2 = ERROR)gen_ai.response.finish_reasons: чому зупинилась генерація
Access Management
Основна концепція – можемо мати різні Organizations (але це Enterprise feature), кожна Organization може містити кілька Teams, в кожній Team – тримаємо Users, а кожен User може створювати власні API Keys – див. User Management Hierarchy.
Users логіняться у Web UI або API, а API Keys використовуємо для сервісів.
Аутентифікація та доступ
Основний метод – API Keys для сервісів або юзерів, які працюють з LiteLLM через API – там звичайні паролі для юзерів, які користуються Web UI.
Є підтримка аутентифікації з JWT – але це Enterprise фіча.
З коробки маємо SSO – “SSO is now Free for up to 5 users“, більше юзерів тільки в Enterprise, див. SSO for Admin UI.
Є навіть підтримка Users Provisioning з SCIM (див Okta: інтеграція з Google Workspaces, частина 1 – Provisioning) – але і тут Premium.
Втім, можемо автоматизувати це з litellm-operator – може якось спробую, але не впевнений, бо оператор не офіційний.
І дуже прикольна штука – обмеження по IP, див. IP Address Filtering – але знов-таки Enterprise фіча 🙁
В результаті з Free-опцій маємо тільки власне, Teams, юзерів та API Keys.
Teams та Users
Кожній Team можна налаштувати параметри того, до яких моделей із model_list юзери і ключі цієї групи будуть мати доступ, максимальний бюджет Costs, яка група може витратити на день/тиждень/місяць, можемо задати ліміти на Tokens per minute Limit (TPM) та Requests per minute Limit (RPM) – див. Budgets, Rate Limits та Setting Team Budgets.
Крім того, є Access Groups – групуємо списки моделей, MCP або агентів в єдиний список, який потім можна підключати до Teams або Users.
Ну і в моніторингу, як бачили вище, маємо атрибути з іменами груп та юзерів – тому потім можемо будувати графіки та алерти по ним.
Окрім звичайних юзерів можемо мати Service Accounts.
Бюджети та ліміти
Бюджети та ліміти на Tokens/Requests Per Minute можна задати на рівні всього Gateway, на рівні Team, для кожного юзера в цій Team, або на окремих юзерів поза Team чи на конкретні API Keys.
Але тут є одна трохи дивна, як на мене, штука:
- Team Limits застосовуються тільки для тих API Keys, які явно були створені юзером або адміном для цієї групи (мають
team_id) - при цьому User з Global Proxy Role == Internal User (Create/Delete/View) може створювати власні ключі (не прив’язані до Team), не задавати їм ніяких лімітів – і спокійно спамити LLM запитами
- єдине обмеження, яке ми можемо задати в Web UI при створенні нового юзера – це які моделі йому будуть доступні (хоча в API docs для
/user/newє параметриmax_budget,rpm_limitтаtpm_limit– нижче буде приклад)
Тобто з одного боку – начебто є User, який є членом Team, і в Team ми задаємо, наприклад, Team Member RPM Limit – але при цьому цей юзер може створювати ключі, на які цей ліміт ніяк не впливає.
А єдине обмеження, яке ми можемо задати при створенні юзера в Web UI – це те, які моделі йому будуть доступні – хоча через конфіг-файл ще можна задати upperbound_key_generate_params, див. All Settings for Self Serve.
Виглядає так, наче UI просто ще не має всіх опцій, які доступні в API.
Взагалі, мабуть, треба буде писати окремий пост на тему доступів і лімітів, бо тут “нє всьо так однозначно”.
RBAC та System Roles
Простенький (принаймні на зараз, у версії v1.82.6), з кількома дефолтними ролями – але є RBAC.
Ролі поділені на три основних групи:
- глобальні всього LiteLLM –
adminтаadmin_read_only - user roles –
userтаuser_read_only - Organization та Team Roles – org/team admin
Див. User Roles та Available Roles.
Ну і давайте глянемо як це все виглядає на практиці: створимо Team з бюджетом та лімітами на Requests per minute, потім в цю групу додамо юзера, юзеру створимо API Key – і використаємо його в нашому Demo App.
Створення Team
Переходимо в Teams > Create Team:
Створюємо групу:
Тут задаємо доступ до всіх моделей, вказуємо загальний бюджет групи в 100 долларів на день (Reset Budget: daily), і для перевірки задамо жорсткий ліміт в 1 запит на хвилину.
Бюджети і ліміти в Team задаються на двох “рівнях” – самої групи і всіх юзерів в ній, та окремо на кожного юзера (точніше – його ключів, як створені в цій групі – див. далі), тобто:
- Max Budget (USD): це бюджет всіх разом, а Team Member Budget (USD) – на кожного юзера в групі
- Requests per minute Limit (RPM): на всю Team, а Team Member RPM Limit – кожного юзера в групі
Бюджети створюються як окремі об’єкти, доступні в Budgets:
І тут ловив ще одну чи то багу, чи то фічу, що після зміни значень в Team Budget значення бюджету для юзера не змінились, поки не зробив це руками саме в Budgets.
Нижче в параметрах нової Team у Router Settings можна налаштувати власні параметри для Load Balancing та Fallbacks:
Створення User у Web UI
Юзери в UI створюються через Invite, який відправляється на пошту – тому треба мати SMTP, але після створення Invite у нас буде показаний лінк, за яким можемо зареєструватись.
Клікаємо Invite User:
Задаємо роль з правами на створення ключів, вибираємо створену вище групу, в Personal Key Creation можна обмежити доступ до моделей – і це, власне, єдине обмеження, яке ми тут можемо встановити для юзера:
Ба більше: під час створення юзера в Team – йому не можна відразу задати Team Role, і він буде створений з дефолтною роллю User – але це можна змінити потім.
Клікаємо на Invite User – отримуємо посилання, яке було відправлено на пошту:
Відкриваємо його в Incognito, задаємо пароль нового юзера, і попадаємо в Web UI – але тут вже, звісно, набагато менше доступів:
Team Permissions
Вже після інвайту можемо змінити роль юзера в цій групі – бо без Admin ролі він не зможе створювати ключі в групі, і навіть встановити йому власні Member Limits/Budget:
Інший варіант дозволити створення ключів для групи – задати через Member Permissions:
Створення User API Key для Team у Web UI
Тепер під цим юзером створюємо ключ – вказуємо групу, але не задаємо RPM:
Зберігаємо ключ:
Задаємо нову змінну:
$ export LITELLM_USER_KEY=sk-1WezQOWNC55fyt_z6y7V7w
В коді Demo App міняємо назву змінної з LITELLM_MASTER_KEY на LITELLM_USER_KEY, і можна додати max_retries – аби ловити Exception відразу, як LiteLLM поверне клієнту 429:
...
client = OpenAI(
base_url="http://localhost:4000",
api_key=os.getenv("LITELLM_USER_KEY"),
max_retries=0,
)
...
Запускаємо скрипт два рази підряд – перший спрацював, а на другий раз ловимо 429 Rate limit exceeded – бо задавали RPM Limit в Team:
$ ./demo-llm.py
Hello! How can I assist you today?
...
$ ./demo-llm.py
...
openai.RateLimitError: Error code: 429 - {'error': {'message': 'Rate limit exceeded for team: aa65abde-4a51-49ee-9271-d16b09fd2058. Limit type: requests. Current limit: 1, Remaining: 0. Limit resets at: 2026-06-04 09:13:21 UTC', 'type': 'None', 'param': 'None', 'code': '429'}}
Створення User API Key у Web UI без Team і без Limits
Тепер під тим жеж юзером створюємо ще один ключ – але вже без Team, і теж не задаємо ніяких лімітів:
Оновлюємо змінну:
$ export LITELLM_USER_KEY=sk-K7Dvc7gSFye6RKsFBB2PyQ
Запускаємо скрипт – і спокійно спамимо LiteLLM запитами:
$ ./demo-llm.py Hello! How can I assist you today? ... $ ./demo-llm.py Hello! How can I assist you today? ...
Тобто, якщо ми даємо юзерам можливість створювати ключі – то вони спокійно можуть робити ключі без всяких обмежень (окрім тих, що ми задамо глобально у upperbound_key_generate_params).
Але при створенні юзера чи ключа через API ми відразу можемо задавати всі потрібні ліміти.
Створення User та API Key з LiteLLM API з Rate Limit
Див. LiteLLM API /user/new.
Створюємо юзера з rpm_limit:
$ curl -X POST http://localhost:4000/user/new \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-H "Content-Type: application/json" \
-d '{
"user_email": "[email protected]",
"rpm_limit": 1,
"user_role": "internal_user"
}'
В response отримуємо його ключ, там жеж бачимо, що team_id пусте – юзер не належить до групи, але має власний rpm_limit:
{..., "max_budget":null, "user_id":"7a9a6ced-ae6a-497e-a9ba-e35825139845", "team_id":null, ..., "rpm_limit":1, ..., "key":"sk-cazziUlsSKQEGTsTUGILRA", ... ,"user_email":"[email protected]","user_role":"internal_user", ... }
Задаємо цей ключ в змінну:
$ export LITELLM_USER_KEY=sk-cazziUlsSKQEGTsTUGILRA
Запускаємо скрипт два рази – і на другий знов отримуємо 429:
$ ./demo-llm.py
Hello! How can I assist you today?
...
$ ./demo-llm.py
...
openai.RateLimitError: Error code: 429 - {'error': {'message': 'Rate limit exceeded [...] }
Замість висновків
Система виглядає дійсно круто в плані того, що дозволяє мати загальний моніторинг LLM – з коробки маємо купу корисних метрик, маємо трейси. Ну і 50,000 зірок на GitHub далеко не всі збирають.
Трейси чудово інтегруються із зовнішніми системами, і дуже зручно, що з коробки можемо їх відправляти в кілька різних бекендів одночасно.
Але от з юзер менеджментом в мене виникли питання – бо якось не дуже інтуїтивно зроблено. Місцями, на перший погляд, заплутано, місцями з чимось, що виглядає як баги. Хоча вцілому можливостей по менеджменту доступів дійсно багато.
Все ж спробуємо її запустити у нас і подивимось вже в реальній роботі – благо, коли проект в MVP то можна собі дозволити експерименти.
Ну і коли буду запускати в Kubernetes – мабуть, ще раз окремо пройдусь по доступам і юзерам, бо тут треба розібратись додатково.
![]()




















