Skip to content

dsh-routerai-price-watcher

Verified

dsh-routerai-price-watcher · v0.2.0 · MIT · Web UI

Standalone DSH plugin: discovers watched models from the DSH catalog, polls RouterAI per-provider pricing, persists JSONL history, and exposes a routeraiPrices service.

Install

dsh plugin add dsh-routerai-price-watcher

Confirm the layer applied with dsh --profile default --dump-config — see the install guide.

Source

Tags

Creators

Readme

dsh-routerai-price-watcher

Автономный плагин DSH (DeepSeek Harness), который отслеживает цены по каждому провайдеру для моделей RouterAI и предоставляет их как сервис для других плагинов. Этот файл — русское зеркало README.md (на английском).

Что он делает

  • Обнаружение моделей из DSH: при каждом снапшоте читает каталог моделей настроенного провайдера DSH (по умолчанию rai-models). Жёстко заданного списка моделей нет — единственный источник правды — выбор моделей DSH для этого провайдера.
  • Периодический опрос цен: для каждой обнаруженной модели получает цены по провайдерам из https://routerai.ru/api/v1/models/{author}/{model}/endpoints. Интервал по умолчанию: 6 часов (настраивается).
  • Локальная история: добавляет одну JSONL-запись на провайдера на модель на снапшот в $DSH_HOME/routerai-price-watcher/YYYY-MM-DD.jsonl (fallback: ~/.dsh/routerai-price-watcher/).
  • Выбор лучшего провайдера: взвешенная сумма input*w.input + output*w.output + cacheRead*w.cacheRead + cacheWrite*w.cacheWrite с настраиваемыми весами (по умолчанию: 1, 1, 0.2, 0.2).
  • Экспорт сервиса: регистрирует сервис routeraiPrices Cordis, чтобы другие плагины (например, relay провайдера или счётчик стоимости) могли потреблять цены без повторного запроса.
  • Кэш-значения: парсер читает input_cache_read/input_cache_write из объекта pricing внутри эндпоинта, а не из корня ответа. Если RouterAI API предоставляет эти значения, они сохраняются в истории; если отсутствуют — записываются как 0.

Самостоятельный по дизайну: не зависит от других плагинов.

Настройки клиента (RouterAI Prices)

Раздел «RouterAI Prices» в настройках клиента DSH:

  • Селектор провайдера: выпадающий список провайдеров DSH (через listProviders). Выбор провайдера вызывает setProvider, который переключает наблюдаемого провайдера и запускает повторный снапшот.
  • Редактор весов: четыре числовых поля для весов input, output, cacheRead, cacheWrite и кнопка сброса. Веса влияют на то, какая строка провайдера выделяется как самая дешёвая (минимальный взвешенный результат). Изменения сохраняются в браузере (localStorage).
  • Топ-3 провайдеров: по умолчанию для каждой модели показываются три самых дешёвых провайдера; остальные скрыты за переключателем «показать ещё».
  • Компактный столбец модели: столбец модели отображается в компактном виде (только идентификатор RouterAI без префикса DSH).
  • Без столбца «DSH alias»: столбец DSH-алиаса убран — вместо него компактный столбец модели.

Контракт сервиса (routeraiPrices)

Использование: inject: ['routeraiPrices'], затем вызовы методов на ctx.routeraiPrices:

interface RouteraiPrices {
  getSnapshot(): Snapshot | null
  getWatchedModels(): { model: string; dshModel: string | null }[]
  getModelPrices(model: string): ProviderPrice[]
  bestProvider(model: string, weights?: Weights): BestProvider | null
  getHistory(filter?: HistoryFilter): Promise<HistoryRecord[]>
  listProviders(): ProviderInfo[]
  setProvider(providerId: string): Promise<Snapshot | null>
  refresh(): Promise<Snapshot | null>
}
  • Нормализация идентификатора модели: аргумент, начинающийся с ${providerId}/ (точно, с учётом регистра), приводится к чистому идентификатору RouterAI; всё остальное используется как есть. И deepseek/deepseek-v4-pro-0813, и rai-models/deepseek/deepseek-v4-pro-0813 разрешаются в один ключ. Цены везде ключуются по исходному id.
  • Подсчёт bestProvider: input*w.input + output*w.output + cacheRead*w.cacheRead + cacheWrite*w.cacheWrite; при равенстве — первый провайдер в порядке маршрутизации RouterAI.
  • refresh() заново обнаруживает модели, затем запрашивает цены. Возвращает результирующий снапшот — или сохранённый предыдущий, если текущий запуск не дал результата (null до первого успешного снапшота).

Формат записи истории

{
  "ts": "2026-10-17T12:00:00.000Z",
  "model": "deepseek/deepseek-v4-pro-0813",
  "dshModel": "rai-models/deepseek/deepseek-v4-pro-0813",
  "provider": "baidu",
  "input": 0.00002028,
  "output": 0.00006086,
  "cacheRead": 0.000000645,
  "cacheWrite": 0,
  "pricingUnits": { "prompt": "token", "completion": "token" }
}
  • model — исходный идентификатор RouterAI, используемый в HTTP-запросе; dshModel — DSH-идентификатор, зафиксированный в момент снапшота (null только для записей аварийного режима), чтобы последующая смена DSH-префикса не «осиротила» историю.
  • Цены — за один токен, в рублях (умножьте на 1e6 для отображения «за 1M токенов»). Файлы дополняются только в конец (append-only); компактификации нет. Отсутствующие поля кэша записываются как 0; pricingUnits сохраняется verbatim, чтобы будущая агрегация никогда не смешивала несовместимые единицы.

Конфигурация

Настраивается через bundle-конфиг DSH ≥ 0.1.7 (страница плагинов) или композицию профиля. Поля:

Поле Тип По умолчанию Значение
providerId string rai-models идентификатор провайдера DSH, чьи модели наблюдаются
pollIntervalHours number 6 часов между снапшотами
requestTimeoutMs number 15000 таймаут запроса на модель
requestDelayMs number 250 пауза между запросами по моделям (защита от rate-limit)
weights object { input: 1, output: 1, cacheRead: 0.2, cacheWrite: 0.2 } веса для расчёта лучшего провайдера
historyDir string? — переопределение базовой директории истории
fallbackModelIds string[] [] только для аварийного режима (офлайн/тестирование): используется исключительно когда обнаружение не возвращает моделей. Записи — уже очищенные идентификаторы RouterAI.

Список наблюдаемых моделей не настраивается — он всегда обнаруживается из DSH.

Установка

Плагин-бандл: package.json объявляет dsh.bundle с cordis.patch.yml; устанавливается из npm или локального пути, затем компонуется в стек слоёв профиля.

Разработка

pnpm install --prefer-offline --store-dir .pnpm-store
pnpm run check        # tsc --noEmit
pnpm run test         # vitest run
pnpm run build        # tsdown → lib/index.js + lib/index.d.ts
node --check lib/index.js

Обработка ошибок

  • Ошибки сети, таймауты, не-2xx → предупреждение; продолжение работы с оставшимися моделями.
  • HTTP 404 → трактуется как «активных провайдеров пока нет» (debug, не предупреждение).
  • Сбой обнаружения или ноль моделей → предупреждение; fallbackModelIds используются при наличии; иначе снапшот пропускается, предыдущий in-memory снапшот сохраняется.
  • Ноль записей в снапшоте → предупреждение; предыдущий снапшот сохраняется.
  • Исключения плагина никогда не выходят за пределы apply() или callback интервала.

Лицензия

MIT — см. LICENSE.