feat(profile): add extra_body deep-merged into translated request bodies#7
feat(profile): add extra_body deep-merged into translated request bodies#7GeoffreyDjof wants to merge 1 commit into
Conversation
Providers accept parameters that protocol translation never produces: reasoning effort on the OpenAI Responses API, enable_thinking / thinking_budget on DashScope, and similar provider-specific fields. Add an optional per-profile [profiles.extra_body] table that is deep-merged into the translated request body (objects merge recursively, scalars are overwritten). It is applied after strip_params, so an explicitly configured field is never auto-stripped, and it works with all three provider types. Docs: EN + zh-CN config reference, config.example.toml/yaml.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6b5ad8c73a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
|
||
| // extra_body 在 strip_params 之后合并:用户显式配置的字段优先于自动剥离 | ||
| if let Some(extra) = &profile.extra_body { | ||
| super::util::merge_extra_body(&mut translated.body, extra); |
There was a problem hiding this comment.
Prevent extra_body from overriding stream mode
When a profile configures extra_body.stream, this merge changes the upstream request after is_streaming was already computed from the inbound body in handle_messages, so the response handling can take the wrong branch. For example, a non-streaming client request with extra_body = { stream = true } makes the upstream return SSE while this code still calls resp.json(), and the inverse wraps a JSON response as an SSE stream; either reject/control-field overrides like stream or derive the response path from the post-merge body.
Useful? React with 👍 / 👎.
|
|
||
| // extra_body 在 strip_params 之后合并:用户显式配置的字段优先于自动剥离 | ||
| if let Some(extra) = &profile.extra_body { | ||
| super::util::merge_extra_body(&mut translated.body, extra); |
There was a problem hiding this comment.
Preserve max_tokens cap after extra_body merge
For OpenAICompatible profiles that set max_tokens as a cap, anthropic_to_openai(..., profile.max_tokens) caps the inbound request before this merge, but an extra_body.max_tokens value inserted here overwrites that capped value. In a profile intended to enforce max_tokens = 1024, a larger extra_body value is still sent upstream, so the configured cost/limit guard no longer holds; reapply the cap after merging or forbid max_tokens in extra_body.
Useful? React with 👍 / 👎.
Motivation
Some provider parameters cannot be produced by protocol translation because they have no equivalent in the incoming Anthropic request:
reasoning.effort) — today a ChatGPT/Codex subscription profile always runs at the backend's default effort, with no way to request a higher one;enable_thinking,thinking_budget);What this adds
An optional per-profile
extra_bodytable that is deep-merged into the translated request body right before it is sent upstream:Design notes:
reasoning.summary) is preserved when the user only setsreasoning.effort.strip_params(single merge point intry_forward): an explicitly configured field is never auto-stripped, and the merge covers streaming, non-streaming, and backup-failover paths for all three provider types.claudex profile showprints the configuredextra_body.skip_serializing_if).Testing
cargo fmt --check,cargo clippy,cargo test(348 tests) clean — remaining clippy warnings are pre-existing.reasoning.effort) and a DashScope token-plan endpoint (enable_thinking+thinking_budget, Qwen3 models).Docs
extra_bodyfield row + dedicated section with examples).config.example.toml/config.example.yamlschema comments.