feat: per-key spend accounting and per-task complexity pinning

API keys page: the table now answers "which department spends what" — per-key
requests, tokens in/out and cost for a selectable window (24h/7d/30d) plus a
cumulative all-time cost column, joined from stats by the key's account.

The stats log stays bounded while the accounting survives: once stats.jsonl
exceeds max_file_mb (default 16, 0 disables), records older than the
retention window fold into cumulative per-account totals in a small sidecar
file (tmp+rename on both) and leave the log. alltime = folded + in-file, a
record is never counted twice, verified end-to-end on synthetic data
(1.2 MB → 301 KB, totals identical before/after compaction and restarts).

Routing: a task rule can now PIN the complexity tier —
`[routing.tasks.legal] models=[...] tier="high"` (old plain-array rules parse
unchanged). The pinned tier replaces the router's verdict before the
escalation check and reports itself as route_source "router+task-rule@high".
The Routing page grows a per-task «Complexity» selector (Auto/low/medium/
high); verified live: a code prompt with a pinned high went
router+task-rule@high → the expensive model.

13 new strings in all 7 languages; docs/ROUTING(.ru) updated.
This commit is contained in:
infosave2007
2026-08-08 08:42:11 +03:00
parent ef28a6633d
commit 95e560dc72
18 changed files with 441 additions and 50 deletions
+14
View File
@@ -177,6 +177,20 @@ code = ["claude-sonnet", "gpt"] # код — сильнейшему ко
сложности следует за ними как резерв — правило по задаче не ослабляет failover.
Задачи без правила маршрутизируются только по сложности.
**Закрепление сложности за задачей.** Правило задачи принимает и полную форму
с фиксированным уровнем — вердикт роутера для этой задачи заменяется до всех
остальных шагов:
```toml
[routing.tasks.legal]
models = ["strong-model"]
tier = "high" # юридические тексты — всегда high, что бы ни решил роутер
```
Ответы с закреплённым уровнем сообщают `route_source: "router+task-rule@high"`.
На вкладке Роутинг это селектор «Сложность» у каждой задачи (Авто / low /
medium / high).
**Исключение — высокая сложность эскалирует.** Когда роутер оценил запрос как
`high` (и цепочка `high` настроена), цепочка уровня идёт первой, а правило
задачи становится резервом. Иначе правило вида `code → дешёвая-модель`