feat(routing): per-task rules on top of complexity tiers; hosted router is the default
The router has always returned two signals — task label and complexity tier — but only the tier picked models. [routing.tasks] adds `label → ordered models`: a matching rule's models lead the candidate chain and the tier chain follows as fallback, so pinning translations to a local model never weakens failover. Requests whose chain was headed by a task rule report route_source "router+task-rule". The Routing page grows a per-task editor built from the account's LIVE taxonomy (GET /v1/taxonomies via the new /admin/api/routing/labels proxy; built-in label set when the router is unreachable), with localized task names, each task's share of 7-day traffic, "by complexity (no rule)" as the explicit empty state, and custom labels for other taxonomies. Deleting a model scrubs it from task rules like it always did from tiers. Also fixes the default config pointing at a placeholder localhost:8080 router with a nonexistent taxonomy — a fresh install now targets the hosted allaigate router (IP endpoint, verify_tls off, data-assistant, 12 s oracle timeout) and degrades gracefully until a key is set. Verified end-to-end against the live router: translation → task-rule model, math → tier path. 16 new strings in all 7 languages.
This commit is contained in:
@@ -162,3 +162,23 @@ fine-tune the task mask. Over time this makes routing fit your specific pool mor
|
||||
enabled = false
|
||||
# the ground-truth source is set by the integrator (e.g. the X-Cortiq-Correct-Label header)
|
||||
```
|
||||
|
||||
## Task-based routing
|
||||
|
||||
The router classifies every request twice: a **task label** (what kind of work it
|
||||
is) and a **complexity tier** (how hard it is). Tiers pick *how much model* you
|
||||
need; task rules pick *which* model is best at the job:
|
||||
|
||||
```toml
|
||||
[routing.tasks]
|
||||
translation = ["local-qwen"] # translations are cheap — keep them local
|
||||
code = ["claude-sonnet", "gpt"] # code goes to the strongest coder first
|
||||
```
|
||||
|
||||
A matching rule's models go **first** in the candidate chain; the complexity
|
||||
chain follows as fallback, so a task pin never weakens failover. Tasks without
|
||||
a rule route by complexity alone. The available labels come from the router
|
||||
(`GET /v1/taxonomies`) — the admin console's Routing page builds the editor
|
||||
from your account's live taxonomy and shows each task's share of recent
|
||||
traffic. When a rule supplied the head of the chain the response reports
|
||||
`route_source: "router+task-rule"`.
|
||||
|
||||
@@ -160,3 +160,22 @@ cooldown = "30s"
|
||||
enabled = false
|
||||
# источник истины задаёт интегратор (например, заголовок X-Cortiq-Correct-Label)
|
||||
```
|
||||
|
||||
## Распределение по задачам
|
||||
|
||||
Роутер классифицирует каждый запрос дважды: **тип задачи** (что это за работа)
|
||||
и **уровень сложности** (насколько она трудна). Уровни выбирают, *сколько
|
||||
модели* нужно; правила по задачам — *какая* модель лучше в этом деле:
|
||||
|
||||
```toml
|
||||
[routing.tasks]
|
||||
translation = ["local-qwen"] # переводы дёшевы — оставляем локально
|
||||
code = ["claude-sonnet", "gpt"] # код — сильнейшему кодеру
|
||||
```
|
||||
|
||||
Модели подходящего правила идут **первыми** в цепочке кандидатов; цепочка по
|
||||
сложности следует за ними как резерв — правило по задаче не ослабляет failover.
|
||||
Задачи без правила маршрутизируются только по сложности. Список меток приходит
|
||||
с роутера (`GET /v1/taxonomies`) — вкладка Роутинг строит редактор по живой
|
||||
таксономии аккаунта и показывает долю каждой задачи в трафике. Когда цепочку
|
||||
возглавило правило задачи, ответ сообщает `route_source: "router+task-rule"`.
|
||||
|
||||
Reference in New Issue
Block a user