Repository navigation
Add jev-router: Jev as a model-selection strategy under Cerberus policy - #28
Merged
Merged
Conversation
A jev-router alias hands model selection to OpenRouter's hosted Jev Router while Cerberus keeps the policy. Cerberus answers "what may run?"; Jev answers "which of those should run?" for one request. - Schema: new `jev-router` mode with a `jev_router` block (backend, timeout_seconds, allow_paid_pool). The pool is exact registry slugs under one OpenRouter credential; patterns, duplicates, per-candidate reasoning budgets and allow_paid_fallback are refused at validation. - router/pool.py: one strategy-neutral pool filter (cost, then health, cooldown and credential in the failover loop's order), shared by dispatch and /admin/routes and reusable by a native Jev strategy. - Dispatch sends only the filtered pool as the jev-router plugin's include list, never an empty one, and always requests router metadata. A response is accepted only when the jev-router stage is present, reports no list_fallback, and names served and resolved models inside the pool sent; models_ignored and every other miss is withheld with 502 as out_of_policy while its billed usage is still recorded. - Callers may send an allowlist of generation fields and function tools; model lists, plugins, route, preset, provider, server tools, reasoning controls and unknown fields are refused with 400 before any call. - Telemetry schema 6 adds a jev_router record and the out_of_policy, jev_router_unavailable and invalid_request outcomes. The console projects the alias as a pool with readiness and decision evidence. Not streamed: the decision is verified before the answer is returned. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P4Nm4MVSMmG6JyZi8WumtK
The merged caller-field allowlist refuses non-function tools in app.py for every alias except fusion, ahead of mode dispatch and without a routing event. jev-router enforces a stricter allowlist of its own with an invalid_request event, so it is exempted the same way fusion is, and its allowlist is now the shared CALLER_FIELDS less the reasoning controls rather than a second copy that could drift. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P4Nm4MVSMmG6JyZi8WumtK
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 1 of Jev routing. This adds a
jev-routeralias mode: OpenRouter's hosted Jev Router becomes one model-selection strategy, alongside dedicated paths and Fusion. Cerberus keeps the policy and decides what may run. Jev decides which of those should run for each request.Cerberus filters first; Jev only sees what survives
registry/schema.py):~familypatterns (they could match models the registry never listed), duplicates, per-candidatereasoning_effort/chat_template_kwargs(Jev chooses reasoning effort per request), andallow_paid_fallback.allow_paid_pool.router/pool.py(new) is one filter used by dispatch and/admin/routes. It applies cost first, then health → cooldown → credential, the same order the failover loop uses. It doesn't depend on Jev, so the planned native Jev strategy can reuse it.jev-routerplugin'smodelslist. An empty pool is never sent: OpenRouter treats a list that matches nothing as no list at all and routes over its whole pool, so Cerberus answers 503 instead.models_ignoredand other out-of-pool responses are rejectedEvery call sends
X-OpenRouter-Metadata: enabled. A response is accepted only when all of these hold:jev-routerpipeline stage is presentlist_fallbackresolved_modelsentry are in the pool that was sent (a slug also matches its-YYYYMMDDrevisions, as the plugin documents)Any other response is withheld with 502 and recorded as
out_of_policy. Because the call has already been billed, its usage and reported cost are still recorded.openrouter_metadatais stripped from the payload. The decision is reported in thecerberusenvelope and in telemetry instead.Callers can't influence routing
jev-router allows the shared
CALLER_FIELDSfrom Deep-Sixed/Cerberus#23, minusreasoning_effort/chat_template_kwargs, plus function tools only.models,route,plugins,preset,provider, server tools (openrouter:*,web_search, …),reasoning_effort/reasoning, and any field not on the list.invalid_requestrouting event.Telemetry and console
jev_routerrecord (pool sent, returned model, router decision, violation) and three new outcomes:out_of_policy,jev_router_unavailable,invalid_request./admin/routesand the console show the alias as a pool (each membereligibleorexcluded, neverstandby), with readiness and decision evidence.binding_type = 'jev-router'androute_membersrows with rolepool. No migration is needed.Not in this PR
Test plan
./scripts/ci.shon the branch with Forward only standard chat fields and function tools upstream #23 merged in: ruff clean, 376 passed.uv build --no-build-isolation; the wheel includescerberus/jev_router.node --check src/cerberus/ui/static/app.jspython scripts/check-public-history.py: 0 findings.openrouter_metadatastage shape matches the documentation. If it differs, every jev-router response is withheld asdecision_unverifiable; that is the safe failure.🤖 Generated with Claude Code
https://claude.ai/code/session_01P4Nm4MVSMmG6JyZi8WumtK