Repository navigation
Add jev: Jev decides which model, Cerberus executes it - #29
Merged
Merged
Conversation
Phase 2 of Jev routing. A jev alias asks Jev, through OpenRouter's Decisions API, which pool model should serve a request, then runs that model through Cerberus's own failover loop. Cerberus answers "what may run?", Jev answers "what should run?", the provider runs it. - Schema: `jev` mode with a `jev` block (decider credential, endpoint, model pinned to typesafe/jev-1.13, input policy, max_input_chars, timeout, allow_paid_pool). Registry models gain `strength` and `description`; a jev pool model must declare a strength. Unlike jev-router, the pool may span any providers, local hosts included. - The pool is filtered by router/pool.py; the surviving distinct models are offered under opaque ids, described by registry facts only. Provider and credential names never leave. - What the decision reads is policy: the latest user message (default), the whole conversation, or request metadata with no prompt text, always truncated. With one model or none left, no decision is asked. - The answer can only reorder: the chosen model goes first, the rest of the cost-admitted pool follows in configured order, and dispatch() executes it through a DispatchPlan, so jev aliases stream, fail over and cool down like any dispatch alias. A plan that names a target the alias does not configure is refused. - No usable answer (no backend or credential, decider down, timeout, transport/HTTP error, unreadable response, absent or unknown choice) runs the request in configured order and records why. - Telemetry schema 7 adds a `jev` record (options, chosen/skipped/ fallback, reason, choice, probability, decision latency, status, usage) and never any request text. /admin/routes and the console show the pool and the decider's readiness separately. The Decisions API fields inside one question and one answer are not yet confirmed; the request uses the plainest form and the answer is read tolerantly, so a misread degrades to configured order. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P4Nm4MVSMmG6JyZi8WumtK
Deep-Sixed
marked this pull request as ready for review
October 5, 2026 17:21
This was referenced Oct 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Phase 2 of Jev routing: native
jevaliases. Cerberus asks Jev, through OpenRouter's Decisions API, which pool model should serve a request, then runs that model through its own failover loop. The pool may include local hosts, Cloudflare, OpenRouter, or any other registered provider.What Jev sees
router/pool.py(the filter added in Deep-Sixed/Cerberus#28). Each distinct model is offered under an opaque id (m1,m2, …).strength(new registry field, required for jev pool models), cost tier, context window, capabilities and description. Provider and credential names never leave.jev.inputcontrols what request text is sent: the latest user message by default, the full conversation if opted in, or metadata only (no prompt text). Text is always truncated, and Jev always gets the request's shape: turns, size, tools, response format and requested output length.What Jev can change
dispatch()takes a newDispatchPlanand runs it through the normal loop: health, cooldown and credential checks, 429 cooldowns, failover, streaming and telemetry.When Jev can't answer: configured order (your choice)
Any of these runs the request in configured order: missing backend or credential, the decider's provider marked down, timeout, transport or HTTP error, an unreadable response, or a missing or unknown choice. The reason is recorded. A decision outage never takes the alias down.
Telemetry and console
jevrecord: options offered,chosen/skipped/fallback, reason, choice, probability, decision latency, status and usage. It never includes request text./admin/routesand the console report the pool's readiness and the decider's readiness separately.binding_type = 'jev'withpoolmembers. No migration is needed.Decisions API contract
Confirmed (from the material you sent):
POST https://openrouter.ai/api/alpha/decisionswith a bearer keymodel/state/questionsbodychoice/noul/scorequestion typesanswerskeyed by question id, plususagetypesafe/jev-1.13Not yet confirmed: the field names inside a single question and a single answer.
{"id": "route", "type": "choice", "question": …, "options": [ids]}.choice/result/value/answer/selected, or the option with the highest probability.Test plan
./scripts/ci.sh: ruff clean, 418 passed. New tests: 35 intests/test_jev_dispatch.pyand 7 jev cases intests/test_schema.py.jev.input, and ignoring the choice each fail 3 tests.uv build --no-build-isolation; the wheel includescerberus/jev.node --check src/cerberus/ui/static/app.jspython scripts/check-public-history.py: 0 findings.jev.reasonwill readhttp_error(request shape) orno_choice(answer shape), and the fix stays insidesrc/cerberus/jev/backend.py.🤖 Generated with Claude Code
https://claude.ai/code/session_01P4Nm4MVSMmG6JyZi8WumtK
Generated by Claude Code