Model Router engine — route requests across existing model engines - #2869
Merged
Conversation
- New ModelRouterEngine that routes LLM queries to backing engines - Three modes: keyword, llm (LLM classifier), weighted (round-robin) - Config loaded from router.json in engine assets folder (ROUTER_CONFIG SMSS key) - Registered MODEL_ROUTER in ModelTypeEnum
Contributor
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
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
Adds a new
MODEL_ROUTERengine type: a virtual model engine that owns no model itself and dispatches each request to one of the model engines already in the catalog. Routing is configured entirely in arouter.jsonfile in the engine's assets folder — the SMSS only carries the standard engine identity keys, no routing config.{ "mode": "llm", "sticky": true, "classifier_engine": "<engineId>", "default_route": "<engineId>", "fallbacks": ["<engineId>"], "embeddings_engine": "<engineId>", "routes": [ { "name": "code", "engine_id": "<engineId>", "description": "Programming questions: debugging, writing and reviewing code", "keywords": ["java", "python", "debug"], "weight": 70 } ] }Routing modes
scoredoes not match "underscore"); otherwise the default route.description(required on every route in this mode; validated at engine open). Falls back to keyword matching when classification fails, so keywords act as the optional deterministic safety net.Behavior
fallbacksin order → default route. Each attempt gets a fresh copy of the parameters; the last failure is rethrown (preservingSemossModelEngineExceptionfor the FE) when every candidate fails.askCalldelegates straight to the target'saskRoomwith the caller's room, somessage_json/tools pass through verbatim, tool-calling works through the router, and the target's inference log lands under the real room id. Route selection uses the latest user-authored message, not the serialized conversation blob.SecurityEngineUtils.userCanViewEngineand denied candidates are skipped (classifier is exempt as internal plumbing).route_ask/route_embeddingsvia a newinferenceLogMessageMethodhook inAbstractModelEngine, so ask-history queries and usage aggregations only count the target's rows.getContextWindow()returns the minimum across all serving targets (lazy, cached per config load) so agent auto-compaction works when a router is the agent's model. An explicitCONTEXT_WINDOWin SMSS/metadata still wins.embeddings_engineis configured; fails fast with a clear message otherwise.Runtime config editing
GetModelRouterConfig(engine=[...])— returns the rawrouter.json(edit access required; the config exposes target engine ids).UpdateModelRouterConfig(engine=[...], map=[{...}])— validates with the same rules as engine open (nothing is written when validation fails), persists the file, and applies it to the running instance immediately viareloadConfig()— no engine reload. Pushes the engine in clustered deployments.parseAndValidateConfigshared by open and update; config-derived state is swapped as immutable volatile snapshots; applying config resets sticky pins, the RR counter, and the derived context window.IModelRouterEngineinterface (readConfigJson/updateConfig/reloadConfig), advertised by theEngineProxyFactoryguard proxy so the reactors work on guarded engines.Files
engine/api/ModelTypeEnum.javaMODEL_ROUTERengine/api/IModelRouterEngine.javaengine/impl/model/ModelRouterEngine.javaengine/impl/model/AbstractModelEngine.javainferenceLogMessageMethodhook (identity by default)engine/impl/pipeline/EngineProxyFactory.javaIModelRouterEnginefor routersreactor/model/GetModelRouterConfigReactor.javareactor/model/UpdateModelRouterConfigReactor.java