feat: model_override adapter with parameterized adapter config#438
Merged
Conversation
Introduce a conditional model-override adapter that rewrites the
provider model name based on request payload fields (e.g. switching
to a -fast variant when reasoning is disabled).
Key changes:
- Adapter config format upgraded from bare strings to uniform
{ name, options } entries. Legacy string format is normalized
lazily on read via z.preprocess() and normalizeAdapterEntries()
in config-repository — no DB migration needed.
- ProviderAdapter interface gains optional 'options' parameter on
preDispatch/postDispatch/stream hooks. Existing adapters
(reasoning_content, suppress_developer_role) updated with
_options param (backward compatible).
- resolveAdapters() now returns ResolvedAdapter[] (adapter +
options pair) instead of bare ProviderAdapter[].
- Dispatcher updated to destructure { adapter, options } and pass
options through to all adapter call sites.
- model_override adapter: evaluates OR-semantics conditions against
the transformed provider payload using dotted-path field
resolution. Rewrites payload.model when any condition matches.
- Frontend: KNOWN_ADAPTERS updated, model-level adapter checkbox
logic adapted for { name, options } format, inline rule editor
for model_override conditions added to ProviderModelsEditor.
- All 1344 backend tests passing.
…ation When the OpenAI transformer builds the outbound provider payload for a chat-to-chat transformation, it now starts from a spread of the original request body instead of an empty object. Explicitly-mapped fields are overlaid on top, so transformations (e.g. tool normalization, system message prepending) still apply correctly, but unknown fields like enable_thinking, thinking, chat_template_kwargs, thinking_budget, and budget_tokens are no longer silently dropped. Cross-API transformations (chat→messages, chat→gemini) remain fully explicit since they are fundamentally different protocols. This fixes the model_override adapter's inability to match on non-standard reasoning fields that were previously stripped during transformation.
UI:
- Make match model input 2x wider (flex: 2) so long model names fit
- Add column headers above conditions: 'Field path (dotted)' and
'Value (blank = presence check)' with a delete-button spacer
- Shorten condition input placeholders to avoid crowding under headers
Docs:
- Add model_override adapter to CONFIGURATION.md adapters table
- Add dedicated 'Model Override Adapter' section with rule/condition
field descriptions, JSON config example, and usage notes
OpenAPI:
- Update ProviderConfig.yaml adapter schema to accept { name, options }
object entries in addition to bare strings
- Add model_override to the available adapters list with description
- Update model-level adapter field to match the new schema
The match model field in a model_override rule is always the model name of the target being configured — it makes no sense to edit it. Changed to a read-only shaded div showing the model name (mId) with ellipsis overflow for long names. New rules auto-populate the model field with mId.
The Input component has w-full (width: 100%) on its inner <input>, which overrode any flex value set via the style prop. Moving flex to a wrapper div makes the field-path column genuinely 2x wider than the value column.
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.
Why
Some LLM providers don't respect reasoning-related fields in the request body. Instead, they expose reasoning variants as separate model names — e.g.
deepseek-r1(with reasoning) vsdeepseek-r1-fast(without). When a client sendsreasoning: { effort: "none" }orenable_thinking: false, the provider ignores it because the client addressed the "thinking" model, not the "fast" one.Plexus currently has no way to bridge this gap — the router picks a target model, and that's what gets sent to the provider. There was no mechanism to say "if the client is asking for no reasoning, switch to the fast variant instead."
What This Enables
Conditional model rewriting based on request content. You can now configure rules that say:
This means:
glm-5.1) and let Plexus automatically pick the right provider variant based on what features they're requestingBonus Fix
The OpenAI chat→chat transformer was silently dropping any fields it didn't explicitly know about (e.g.
enable_thinking,chat_template_kwargs,thinking_budget,budget_tokens,thinking). This meant even if you configured conditions for those fields, the adapter could never match them because the fields were gone by the time it saw the payload.Now, same-API-type transformations (chat→chat) preserve all unknown fields from the original request body, overlaying only the fields that need explicit transformation. This makes the adapter useful for any non-standard field a provider might use.
How to Use It
Configure the
model_overrideadapter on a per-model basis in the provider settings (via the Plexus UI or management API):zai-org/GLM-5.1-FP8)zai-org/GLM-5.1-FP8)glm-5.1-fast)Example conditions for detecting "no reasoning" requests:
reasoning.enabledfalsereasoning.effort"none"enable_thinkingfalsethinking.type"disabled"chat_template_kwargs.enable_thinkingfalsethinking_budget0budget_tokens0If
valueis left empty, the condition matches on field presence alone (any value).Implementation Notes
{ name, options }entries. Legacy format is normalized lazily on read — no DB migration needed, rows self-heal on next saveoptionsparameter on all hooks; existing adapters are backward-compatible