Skip to content

feat(profile): add extra_body deep-merged into translated request bodies#7

Open
GeoffreyDjof wants to merge 1 commit into
StringKe:mainfrom
GeoffreyDjof:feat/profile-extra-body
Open

feat(profile): add extra_body deep-merged into translated request bodies#7
GeoffreyDjof wants to merge 1 commit into
StringKe:mainfrom
GeoffreyDjof:feat/profile-extra-body

Conversation

@GeoffreyDjof

Copy link
Copy Markdown

Motivation

Some provider parameters cannot be produced by protocol translation because they have no equivalent in the incoming Anthropic request:

  • reasoning effort on the OpenAI Responses API (reasoning.effort) — today a ChatGPT/Codex subscription profile always runs at the backend's default effort, with no way to request a higher one;
  • DashScope (Qwen) thinking mode (enable_thinking, thinking_budget);
  • any other provider-specific body field.

What this adds

An optional per-profile extra_body table that is deep-merged into the translated request body right before it is sent upstream:

# OpenAI Responses API: request a specific reasoning effort
[profiles.extra_body.reasoning]
effort = "high"
# DashScope (Qwen): enable thinking mode with a budget
[profiles.extra_body]
enable_thinking = true
thinking_budget = 32768

Design notes:

  • Deep merge: objects merge recursively, scalars/arrays are overwritten by the configured value — so a translator-produced sibling field (e.g. a future reasoning.summary) is preserved when the user only sets reasoning.effort.
  • Applied after strip_params (single merge point in try_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 show prints the configured extra_body.
  • Not serialized back to the config file when unset (skip_serializing_if).

Testing

  • Unit tests for the merge semantics (insert, deep merge, scalar overwrite, object-replaces-scalar, non-object no-op) and a TOML parse test for the new field.
  • cargo fmt --check, cargo clippy, cargo test (348 tests) clean — remaining clippy warnings are pre-existing.
  • Verified end-to-end with a release build: proxy debug logs show the merged fields in the outgoing body, and live requests were accepted by both a ChatGPT/Codex subscription profile (reasoning.effort) and a DashScope token-plan endpoint (enable_thinking + thinking_budget, Qwen3 models).

Docs

  • EN + zh-CN configuration reference (extra_body field row + dedicated section with examples).
  • config.example.toml / config.example.yaml schema comments.

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.
Copilot AI review requested due to automatic review settings July 20, 2026 09:28

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread src/proxy/handler.rs

// extra_body 在 strip_params 之后合并:用户显式配置的字段优先于自动剥离
if let Some(extra) = &profile.extra_body {
super::util::merge_extra_body(&mut translated.body, extra);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Comment thread src/proxy/handler.rs

// extra_body 在 strip_params 之后合并:用户显式配置的字段优先于自动剥离
if let Some(extra) = &profile.extra_body {
super::util::merge_extra_body(&mut translated.body, extra);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants