tensorgrid-ui 2.1.0 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -2519,15 +2519,21 @@ window.__ModuleLoader__.load({
2519
2519
  }
2520
2520
 
2521
2521
  /**
2522
- * Отметка на карточке: по подписке или за отдельные деньги.
2522
+ * Отметка на карточке: по подписке, за отдельные деньги или молчание.
2523
2523
  *
2524
- * Ровно два состояния, как и в списке моделей. Разница между
2525
- * сохранённым ключом и ключом из окружения человека не касается:
2526
- * и то и другое означает счёт по токенам.
2524
+ * ТРИ состояния, и третье обязательно. Прежде их было два, и всё, что
2525
+ * не подписка, объявлялось платным включая случай «сказать нечего».
2526
+ * Получалось, что человек видел «по токенам» на модели, которая ему
2527
+ * давно оплачена, а внешний продукт с собственной подпиской получал
2528
+ * ту же метку.
2529
+ *
2530
+ * Молчание честнее догадки: отсутствие отметки означает «не знаю», и
2531
+ * это ровно то, что есть на самом деле.
2527
2532
  */
2528
2533
  const billingLabel = (kind) => {
2529
- if (kind === undefined || kind === null) return null
2530
- return kind === 'subscription' ? t('review.bySubscription') : t('review.byApiKey')
2534
+ if (kind === 'subscription') return t('review.bySubscription')
2535
+ if (kind === 'api-key') return t('review.byApiKey')
2536
+ return null
2531
2537
  }
2532
2538
 
2533
2539
  const atLimit = reviewers.length >= 8
@@ -2607,18 +2613,25 @@ window.__ModuleLoader__.load({
2607
2613
  // ключ из окружения — с этой стороны одно и то
2608
2614
  // же, а третья группа оказывалась почти пустой и
2609
2615
  // только мешала выбирать.
2610
- groups: [
2611
- {
2612
- label: t('review.groupSubscription'),
2613
- options: models.filter((m) => m.billing === 'subscription')
2614
- .map((m) => ({ value: m.id, label: m.provider + ' \u00b7 ' + m.id })),
2615
- },
2616
- {
2617
- label: t('review.groupPaid'),
2618
- options: models.filter((m) => m.billing !== 'subscription')
2619
- .map((m) => ({ value: m.id, label: (m.provider === null ? '' : m.provider + ' \u00b7 ') + m.id })),
2620
- },
2621
- ],
2616
+ // Группы появляются, ТОЛЬКО когда признак оплаты
2617
+ // действительно известен. Иначе — простой список:
2618
+ // деление на «подписка» и «по токенам», в котором
2619
+ // первая корзина пуста, а вторая содержит всё,
2620
+ // сообщает неправду, а не отсутствие сведений.
2621
+ groups: models.some((m) => m.billing !== null && m.billing !== undefined)
2622
+ ? [
2623
+ {
2624
+ label: t('review.groupSubscription'),
2625
+ options: models.filter((m) => m.billing === 'subscription')
2626
+ .map((m) => ({ value: m.id, label: m.provider + ' \u00b7 ' + m.id })),
2627
+ },
2628
+ {
2629
+ label: t('review.groupPaid'),
2630
+ options: models.filter((m) => m.billing !== 'subscription')
2631
+ .map((m) => ({ value: m.id, label: (m.provider === null ? '' : m.provider + ' \u00b7 ') + m.id })),
2632
+ },
2633
+ ]
2634
+ : undefined,
2622
2635
  onPick: (value) => {
2623
2636
  const found = models.find((m) => m.id === value)
2624
2637
  patch(entry.id, {
@@ -285,6 +285,22 @@ async function listRouteModels(llm, credentials) {
285
285
  // работает, но записи не имеет, настроен извне: у OpenRouter ключ лежит
286
286
  // переменной окружения, и по записям он выглядел бы ненастроенным, хотя
287
287
  // отвечает. Проверено на живых данных.
288
+ //
289
+ // «НЕ ЗНАЮ» — ОТДЕЛЬНЫЙ ОТВЕТ, А НЕ «ПО ТОКЕНАМ».
290
+ //
291
+ // Здесь стояла дыра, которая врала в самую дорогую сторону. Когда служба
292
+ // хранилища недоступна, прежний код помечал ВСЕ модели как платные —
293
+ // включая подписочные. Человек видел «по токенам» на claude-opus-5,
294
+ // который ему давно оплачен, и мог отказаться от ревизора из-за счёта,
295
+ // которого не будет.
296
+ //
297
+ // Случай не выдуманный: он воспроизвёлся сразу. Обработчики
298
+ // перечитываются на лету, а оболочка — только при старте, и до
299
+ // перезапуска новый код спрашивал хранилище у старой оболочки, которая
300
+ // его не передавала. Все 388 моделей стали «по токенам».
301
+ //
302
+ // Теперь неизвестность возвращается как `null`, и показ обязан молчать.
303
+ let known = false
288
304
  const billing = new Map()
289
305
  if (credentials !== undefined && credentials !== null) {
290
306
  try {
@@ -294,8 +310,9 @@ async function listRouteModels(llm, credentials) {
294
310
  const name = slash >= 0 ? key.slice(slash + 1) : key
295
311
  billing.set(name, record.kind === 'grant' ? 'subscription' : 'api-key')
296
312
  }
313
+ known = true
297
314
  } catch {
298
- // Хранилище недоступно — покажем модели без отметки, это лучше отказа.
315
+ // Хранилище не ответило — покажем модели без отметки, это лучше лжи.
299
316
  }
300
317
  }
301
318
 
@@ -303,7 +320,10 @@ async function listRouteModels(llm, credentials) {
303
320
  for (const info of providers) {
304
321
  const name = typeof info === 'string' ? info : info?.name ?? info?.provider
305
322
  if (typeof name !== 'string' || name === '') continue
306
- const kind = billing.get(name) ?? 'external'
323
+ // Провайдер без записи, но отвечающий, настроен вне dsh — ключом из
324
+ // окружения. Для человека это значит то же, что сохранённый ключ: счёт
325
+ // придёт. А вот когда хранилище вовсе не спросили, сказать нечего.
326
+ const kind = known ? billing.get(name) ?? 'api-key' : null
307
327
  try {
308
328
  const models = await llm.listModels(name)
309
329
  for (const model of models ?? []) {
@@ -346,7 +366,13 @@ export async function handleReview(req, res, loadModule, services) {
346
366
  label: spec.label,
347
367
  internal: spec.kind === 'internal',
348
368
  optional: spec.optional === true,
349
- models: spec.kind === 'internal' ? routeModels : spec.models.map((id) => ({ provider: null, id, billing: 'external' })),
369
+ // У ВНЕШНИХ ПРОДУКТОВ ОТМЕТКИ ОПЛАТЫ НЕТ, и это не упущение.
370
+ //
371
+ // Claude Code и Codex работают на СВОИХ подписках и нашего маршрута не
372
+ // касаются — мы не знаем и не можем знать, во что обойдётся их запуск.
373
+ // Прежде им доставалась метка «по токенам» просто потому, что она была
374
+ // всем, кроме подписки. Это было неверно.
375
+ models: spec.kind === 'internal' ? routeModels : spec.models.map((id) => ({ provider: null, id, billing: null })),
350
376
  // Для ВНЕШНИХ продуктов список остаётся зашитым: спросить у Claude Code
351
377
  // или Codex их уровни нечем, они не наш маршрут.
352
378
  efforts: spec.kind === 'internal' ? [] : spec.efforts,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tensorgrid-ui",
3
- "version": "2.1.0",
3
+ "version": "2.1.1",
4
4
  "description": "TENSOR GRID — фирменный интерфейс поверх DeepSeek Harness: палитра, живой ambient-слой, айдентика и русский язык-пакет",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",