dsh-routerai-price-watcher
Verifieddsh-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). - Экспорт сервиса: регистрирует сервис
routeraiPricesCordis, чтобы другие плагины (например, 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.