Перейти к содержимому
OmniRoute source

Usage, Quota & Spend Tracking (Русский)

Каждый запрос, проходящий через OmniRoute, создаёт запись об использовании, содержащую:

  • Идентификационные данные: какой API-ключ, поставщик, модель и комбинация использовались
  • Токены: токены промпта, токены завершения, кэшированные токены и общее количество
  • Стоимость: сумма в USD, рассчитанная на основе данных о тарифах
  • Время: задержка, временные метки начала и завершения
  • Статус: успешно, ошибка, ограничение частоты запросов и т. д.

Эти записи агрегируются в аналитику, сохраняются в виде снимков квот и используются для обеспечения соблюдения ограничений бюджета для каждого ключа.

Запрос ──▶ chatCore ──▶ usage.record() ──▶ SQLite
│
┌───────┼───────┐
▼ ▼ ▼
аналитика квота биллинг
(панель) (контроль) (экспорт)

Сервис usage.ts регистрирует событие использования для каждого запроса:

Поле Тип Источник
id string UUID, создаваемый при записи
apiKeyId string API-ключ, инициировавший запрос
provider string Идентификатор поставщика (openai, anthropic и т. д.)
model string Идентификатор модели (gpt-5, claude-opus-4-6 и т. д.)
comboId string? Идентификатор комбинации, если запрос направлен через неё
promptTokens number Из ответа вышестоящего сервиса
completionTokens number Из ответа вышестоящего сервиса
cachedTokens number Токены, полученные из кэша (кэширование промптов Anthropic и т. д.)
totalTokens number промпт + завершение
costUsd number Рассчитывается на основе данных о тарифах
latencyMs number Полная длительность обработки запроса
status enum success, error, rate_limited, timeout, cancelled
errorClass string? Класс ошибки, если статус != success
timestamp string ISO 8601 UTC
metadata object Пользовательские данные, добавленные плагином

Данные о токенах извлекаются из ответа вышестоящего поставщика в обработчике ответа:

// Из open-sse/handlers/chatCore.ts
const response = await providerExecutor.execute(provider, request);
const usage = response.usage || {
prompt_tokens: 0,
completion_tokens: 0,
cached_tokens: 0,
};

Для поставщиков, которые не возвращают данные об использовании (например, некоторых поставщиков с веб-аутентификацией через cookie), OmniRoute оценивает количество токенов с помощью эвристики ~4 символа на токен (см. open-sse/services/autoCombo/pipelineRouter.ts).

OmniRoute отслеживает cached_tokens отдельно от prompt_tokens, поскольку:

  • При кэшировании промптов Anthropic кэшированные токены тарифицируются по сниженной ставке (10% от обычной)
  • Некоторые поставщики возвращают cache_read_input_tokens, стоимость которых должна рассчитываться иначе
  • Аналитика может показывать долю попаданий в кэш = cached_tokens / prompt_tokens

Стоимость рассчитывается на основе данных о ценах, синхронизированных с LiteLLM (src/lib/pricingSync.ts):

Модель Вход, $/1M Выход, $/1M Кэш, $/1M
gpt-5 $2.50 $10.00 —
claude-opus-4-6 $15.00 $75.00 $1.50
claude-sonnet-4-5 $3.00 $15.00 $0.30
gemini-2.5-pro $1.25 $10.00 —

Формула расчёта стоимости (src/lib/usage/costCalculator.ts):

cost =
(prompt_tokens - cached_tokens) * input_price +
cached_tokens * cached_price +
completion_tokens * output_price;

Зачем вычитать кэшированные токены из токенов промпта? Кэшированная часть тарифицируется отдельно; применение цены входных токенов ко всему промпту привело бы к завышенному расчёту.

Данные о ценах автоматически синхронизируются с LiteLLM через эндпоинт /api/pricing/sync (запускается встроенной задачей cron, а не пользовательской переменной окружения):

Окно терминала
# Запуск вручную
curl -X POST http://localhost:20128/api/pricing/sync

Для моделей, по которым отсутствуют данные о ценах, OmniRoute выполняет оценочный расчёт стоимости, используя внутренние средние тарифы (полученные из данных о ценах LiteLLM).


Модуль usageAnalytics.ts вычисляет виджеты панели мониторинга на основе необработанных данных об использовании. Он поддерживает 7 временных диапазонов:

Диапазон Период Сценарий использования
1d Последние 24 часа Выявление почасовых скачков стоимости
7d Последние 7 дней Еженедельный анализ
30d Последние 30 дней Ежемесячный биллинг
90d Последние 90 дней Ежеквартальный анализ
ytd С 1 января текущего года Отслеживание годового бюджета
all За всё время Статистика за весь период
custom Заданные пользователем даты начала и окончания Аудиты, разовые запросы

Для любого диапазона дат аналитический слой вычисляет:

Виджет Описание
Карточки сводки Общее количество запросов, общая стоимость, общее количество токенов, доля успешных запросов
График динамики по дням Стоимость и токены за день с разбивкой по моделям
Тепловая карта активности Сетка «час дня × день недели», цвет = количество запросов
Разбивка по моделям Круговая диаграмма стоимости по моделям
Разбивка по провайдерам Столбчатая диаграмма запросов по провайдерам
Лучшие API-ключи Таблица 10 ключей с наибольшей стоимостью
Анализ ошибок Доля ошибок во времени, наиболее частые классы ошибок
import { computeAnalytics } from "@/lib/usageAnalytics";
const analytics = await computeAnalytics(
history, // записи истории использования
"7d", // временной диапазон: "1d" | "7d" | "30d" | "90d" | "ytd" | "all" | "custom"
connectionMap, // карта подключений провайдеров (connectionId → имя учётной записи)
{
startDate: "2025-01-01", // необязательно: для диапазона "custom"
endDate: "2025-06-01", // необязательно: для диапазона "custom"
}
);
console.log(analytics.summary.totalCost); // 12.34 (цента)
console.log(analytics.byModel[0]); // { model, cost, requests, promptTokens, completionTokens }
---
## Контроль квот
Квота для каждого API-ключа контролируется в двух местах:
1. **Мягкий лимит** (`quotaWarnAt`): предупреждение на панели мониторинга, когда использование превышает пороговое значение
2. **Жёсткий лимит** (`quotaLimit`): запрос отклоняется с HTTP 429 при превышении лимита
### Конфигурация
```ts
// Для каждого API-ключа
await updateApiKey(keyId, {
quotaWarnAt: 5_00, // $5.00 — показать предупреждение
quotaLimit: 10_00, // $10.00 — жёсткая остановка
quotaWindow: "month", // "day" | "week" | "month" | "all"
});
Запрос ──▶ quotaCheck()
│
├── В пределах лимита? ──▶ разрешить
│
└── Лимит превышен? ──▶ 429 Too Many Requests
с заголовком Retry-After

Таблица quotaSnapshots хранит историческое состояние квот для анализа тенденций:

| Поле | Описание | | ———– | ———————————— | —— | —–– | | apiKeyId | Отслеживаемый ключ | | window | “day” | “week” | “month” | | used | Расходы в этом окне (в центах) | | limit | Лимит (в центах) | | resetAt | Время сброса окна | | createdAt | Время создания снимка |

Снимки создаются при каждом запросе, стоимость которого > 0, и используются для следующих целей:

  • Отображение индикатора использования квоты на панели мониторинга
  • Отображение графиков динамики квоты за 30 дней
  • Отправка оповещений, когда использование приближается к лимиту

Получение списка записей об использовании

Заголовок раздела «Получение списка записей об использовании»
Окно терминала
GET /api/usage?range=7d&limit=100
GET /api/usage?apiKeyId=key-123&range=30d
GET /api/usage?provider=openai&range=1d

Ответ:

{
"records": [
{
"id": "uuid",
"apiKeyId": "key-123",
"provider": "openai",
"model": "gpt-5",
"promptTokens": 1234,
"completionTokens": 567,
"totalTokens": 1801,
"costUsd": 0.005,
"latencyMs": 1234,
"status": "success",
"timestamp": "2026-06-08T12:00:00Z"
}
],
"total": 1234,
"nextCursor": "..."
}
Окно терминала
GET /api/usage/analytics?range=7d&groupBy=model

Ответ:

{
"summary": {
"totalCost": 12.34,
"totalRequests": 5678,
"totalTokens": 12345678,
"successRate": 0.987,
"avgLatencyMs": 1234
},
"models": [
{ "model": "gpt-5", "cost": 8.5, "requests": 1234, "tokens": 4567890 },
{
"model": "claude-opus-4-6",
"cost": 3.84,
"requests": 234,
"tokens": 234567
}
],
"daily": [
{ "date": "2026-06-01", "cost": 1.5, "requests": 800 },
{ "date": "2026-06-02", "cost": 2.0, "requests": 1000 }
]
}

Данные об использовании доступны через панель мониторинга или инструменты MCP, а не через прямые конечные точки экспорта REST. Доступная аналитика:

  • /api/usage/analytics — агрегированные метрики использования (группировка по модели, провайдеру, ключу)
  • /api/usage/quota — текущее состояние квоты для каждого API-ключа
  • /api/usage/history — журналы истории запросов

Два инструмента MCP предоставляют агентам доступ к данным об использовании (см. open-sse/mcp-server/tools/):

Инструмент Описание
omniroute_cost_report Создаёт отчёт о расходах по ключам за указанный период
omniroute_check_quota Возвращает текущее состояние квоты для API-ключа

Пример вызова агентом:

{
"tool": "omniroute_cost_report",
"args": { "period": "week" }
}

Объём данных об использовании увеличивается примерно на 1–10 КБ с каждым запросом. При больших масштабах это может стать существенным.

Срок хранения истории использования настраивается в разделе Database Settings пользовательского интерфейса или через /api/settings/database.

По умолчанию история использования хранится 90 дней.

Старые записи очищаются модулем src/lib/db/cleanup.ts:

  • Очистка запускается фоновым процессом cron
  • Удаляются записи из usage_history, возраст которых превышает срок хранения, заданный настройкой usageHistory
Частота запросов Хранение за 30 дней Хранение за 90 дней
100 запросов/день ~3 МБ ~9 МБ
1 000 запросов/день ~30 МБ ~90 МБ
10 000 запросов/день ~300 МБ ~900 МБ
100 000 запросов/день ~3 ГБ ~9 ГБ

При очень высокой нагрузке рекомендуется:

  • Сократить срок хранения через Database Settings
  • Использовать aggregated_metrics вместо необработанных записей (только для аналитики)

Окно терминала
# Быстрый ответ — используйте дешёвую и быструю модель
curl -d '{"model":"auto/fast","messages":[...]}'
# Сложная задача — используйте качественную модель
curl -d '{"model":"auto/smart","messages":[...]}'

Кэширование промптов Anthropic позволяет сэкономить 90% при повторном использовании контекста:

// Кэширование выполняется автоматически — просто используйте тот же объёмный системный промпт
const response = await openai.chat({
model: "claude-sonnet-4-5",
system: longSystemPrompt, // Будет кэшировано автоматически
messages: [{ role: "user", content: "..." }],
});

Сжатие RTK + Caveman позволяет сэкономить 15–95% в сеансах с интенсивным использованием инструментов:

const config = {
compression: {
engine: "rtk",
intensity: "aggressive",
},
};

Всегда задавайте quotaLimit, чтобы избежать неконтролируемых расходов:

await updateApiKey(keyId, { quotaLimit: 10_00 }); // Ограничение — $10 в месяц

Используйте панель мониторинга или /api/usage/analytics, чтобы сгруппировать данные по API-ключу и отсортировать их по затратам:

Окно терминала
GET /api/usage/analytics?groupBy=apiKey

  1. Проверьте /api/usage/analytics?groupBy=model — найдите дорогостоящую модель
  2. Проверьте /api/usage/analytics?groupBy=apiKey — найдите крупнейшего потребителя
  3. Убедитесь, что данные о ценах актуальны: POST /api/pricing/sync
  • Проверьте настройки хранения БД в разделе Dashboard → Database → Cleanup — старые записи удаляются периодической задачей очистки (src/lib/db/cleanup.ts)
  • Проверьте наличие ошибок в src/lib/db/usage*.ts — сбои записи в БД регистрируются в журнале, но не выводятся пользователю
  • Убедитесь, что запрос действительно достиг chatCore — проверьте комбинированную маршрутизацию
  • Проверьте настройку quotaLimit ключа
  • Убедитесь, что quotaWindow настроено правильно
  • Проверьте наличие записей quotaSnapshots — они должны создаваться при каждом запросе

  • DATABASE_GUIDE.md — схема таблиц использования
  • ENVIRONMENT.md — переменные окружения для синхронизации цен
  • AUTO-COMBO.md — как auto/fast и auto/cheap снижают затраты
  • API_REFERENCE.md — полное справочное руководство по /api/usage/*
  • Исходный код: open-sse/services/usage.ts, src/lib/usageAnalytics.ts, src/lib/db/usage*.ts

Исходный код OmniRoute (a58000c7685f)

HagiCode

HagiCode — агентная среда разработки со структурированными процессами, параллельным выполнением несколькими агентами и интерфейсами Hero Dungeon.

Превращайте идеи в полезное ПО с более умным, быстрым и увлекательным агентным рабочим процессом.

Главный экран HagiCode в светлой теме
  • SmartСтруктурированные процессы превращают намерение в исполнимый путь от идеи до готового изменения.
  • EfficientМультиагентные процессы параллельно продвигают исследование, реализацию и проверку.
  • FunHero Dungeon делает длительную совместную разработку наглядной и увлекательной.
Перейти на HagiCode