feat: add per-model extraBody support#149
Conversation
Adds the ability to configure extraBody parameters at the model level
within a provider, not just at the provider level. Model-level extraBody
overrides provider-level extraBody when both are set.
Changes:
- Schema: added extra_body column to provider_models table (SQLite + Postgres)
- Config: added extraBody to ModelProviderConfigSchema (z.record)
- Repository: save/load extraBody when persisting/reading model configs
- Dispatcher: merge model-level extraBody after provider-level extraBody
- Frontend: per-model Extra Body Fields UI with add/edit/remove key-value pairs
Use case: configure different parameters per model within the same provider.
For example, disable thinking for Qwen models on OpenRouter with
{"reasoning": {"effort": "none"}} without affecting other models on
the same provider.
|
Very nice, and I just needed this myself the other day! I'm on vacation fo another day, but will try and review tomorrow. |
|
Thanks so much for this — it's a genuinely useful addition and the implementation is clean and well thought out. The merge precedence logic in the dispatcher is exactly right, and the motivation (per-model reasoning control on OpenRouter) is a use case I've bumped into myself. One small request before merge: in Everything else looks great. Happy to merge once that's tidied up! |
Swap JSON.stringify(cfg.extraBody) to toJson(cfg.extraBody) in both insert paths for consistency with other JSON fields (accessVia, pricingConfig, etc.) Per review feedback from @mcowger on PR mcowger#149.
|
Thanks for the review! Good catch — swapped both occurrences of |
* feat: add per-model extraBody support
Adds the ability to configure extraBody parameters at the model level
within a provider, not just at the provider level. Model-level extraBody
overrides provider-level extraBody when both are set.
Changes:
- Schema: added extra_body column to provider_models table (SQLite + Postgres)
- Config: added extraBody to ModelProviderConfigSchema (z.record)
- Repository: save/load extraBody when persisting/reading model configs
- Dispatcher: merge model-level extraBody after provider-level extraBody
- Frontend: per-model Extra Body Fields UI with add/edit/remove key-value pairs
Use case: configure different parameters per model within the same provider.
For example, disable thinking for Qwen models on OpenRouter with
{"reasoning": {"effort": "none"}} without affecting other models on
the same provider.
* fix: use toJson() helper for extraBody serialization
Swap JSON.stringify(cfg.extraBody) to toJson(cfg.extraBody) in both
insert paths for consistency with other JSON fields (accessVia,
pricingConfig, etc.)
Per review feedback from @mcowger on PR #149.
* fix: remove unrelated .gitignore change and fix indentation nits
---------
Co-authored-by: sbochna <sbochna@openclaw-mac.local>
Co-authored-by: Matt Cowger <matt@cowger.us>
Summary
Adds the ability to configure
extraBodyparameters at the individual model level within a provider, not just at the provider level. When both provider-level and model-levelextraBodyare set, model-level overrides provider-level.Motivation
Currently,
extraBodyis only configurable at the provider level. This means all models under a provider share the same extra parameters. In practice, different models on the same provider often need different configurations.Example use case: On OpenRouter, you may want to disable reasoning/thinking for Qwen models (
"reasoning": {"effort": "none"}) while keeping it enabled for other models on the same provider. Without per-modelextraBody, this requires creating separate providers pointing to the same endpoint — a workaround that clutters the configuration.Changes
Backend:
drizzle/schema/sqlite/provider-models.ts,drizzle/schema/postgres/provider-models.ts): Addedextra_bodycolumn toprovider_modelstableconfig.ts): AddedextraBody: z.record(z.any()).optional()toModelProviderConfigSchemaconfig-repository.ts): Save/loadextraBodywhen persisting/reading model configs from DBdispatcher.ts): Merge model-levelextraBodyafter provider-levelextraBody(model overrides provider)Frontend:
Providers.tsx): Per-model "Extra Body Fields" collapsible section with add/edit/remove key-value pairs, matching the existing provider-level Extra Body Fields UI patternMigration
ALTER TABLE provider_models ADD COLUMN extra_body TEXT;ALTER TABLE provider_models ADD COLUMN extra_body JSONB;The existing drizzle migration system should handle this automatically on startup.
Testing
Tested with:
{"reasoning": {"effort": "none"}}→ 0 reasoning tokens (vs 1400+ without){"chat_template_kwargs": {"enable_thinking": false}}→ thinking correctly overridden7 files changed, 137 insertions(+), 8 deletions(-)