Skip to content

fix(relay): sanitize Opus 4.7 params for bare model and reasoning_effort paths - #4337

Closed
forgottener wants to merge 3 commits into
QuantumNous:mainfrom
forgottener:fix/opus-4-7-bare-model-params
Closed

fix(relay): sanitize Opus 4.7 params for bare model and reasoning_effort paths#4337
forgottener wants to merge 3 commits into
QuantumNous:mainfrom
forgottener:fix/opus-4-7-bare-model-params

Conversation

@forgottener

@forgottener forgottener commented Apr 18, 2026

Copy link
Copy Markdown

Summary

Follow-up to 47d7bca — two code paths were missed for Claude Opus 4.7 compatibility:

  1. Bare claude-opus-4-7 (no effort/thinking suffix): client-supplied temperature/top_p/top_k are forwarded as-is to the Anthropic API, causing HTTP 400 "Improperly formed request". The existing effort-suffix and -thinking branches handle this correctly, but the bare model name falls through without sanitization.

  2. reasoning_effort / reasoning parameters: these set thinking.type="enabled" which Opus 4.7 also rejects (only "adaptive" is accepted). This affects the OpenAI-compat relay path.

Changes

  • relay/channel/claude/relay-claude.go:

    • After effort/thinking branches, unconditionally clear temperature/top_p/top_k for any claude-opus-4-7 prefix model
    • After ReasoningEffort/Reasoning branches, convert thinking.type="enabled""adaptive" (with display="summarized") for Opus 4.7
  • relay/claude_handler.go:

    • After effort/thinking branches, unconditionally clear temperature/top_p/top_k for any claude-opus-4-7 prefix model

Test plan

  • Send request to bare claude-opus-4-7 with temperature: 0.7 — should succeed (param stripped)
  • Send request to claude-opus-4-7 with reasoning_effort: "high" — thinking type should be adaptive, not enabled
  • Send request to claude-opus-4-7-high — should still work as before (no regression)
  • Send request to claude-opus-4-6 — should still work as before (no regression)

Summary by CodeRabbit

  • Bug Fixes
    • Ensured requests for the claude-opus-4-7 model are accepted by upstream by removing unsupported numeric tuning parameters.
    • Standardized advanced "thinking"/effort settings for that model by converting incompatible modes and applying sensible defaults to ensure consistent, non-rejected responses.

…ort paths

Claude Opus 4.7 rejects non-default temperature/top_p/top_k with HTTP 400
"Improperly formed request". The existing effort-suffix and -thinking branches
already handle this, but two code paths were missed:

1. Bare `claude-opus-4-7` (no suffix) — client-supplied temperature/top_p/top_k
   are forwarded as-is, causing 400. Fix: unconditionally clear these params
   after the effort/thinking branches for any claude-opus-4-7 prefix model.

2. `reasoning_effort` / `reasoning` parameters — these set
   `thinking.type="enabled"` which Opus 4.7 also rejects. Fix: convert
   thinking.type to "adaptive" (with display="summarized") for Opus 4.7
   after the reasoning params are applied.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 37f514eb-8678-47f1-8227-6ea64c9cc6dd

📥 Commits

Reviewing files that changed from the base of the PR and between 688da55 and 0c25543.

📒 Files selected for processing (1)
  • relay/claude_handler.go

Walkthrough

Added model-specific normalization for claude-opus-4-7: after existing reasoning-effort/adapter mapping, clear sampling params (Temperature, TopP, TopK) for bare-model requests and convert Thinking.Type == "enabled" to "adaptive", clear Thinking.BudgetTokens, and default Thinking.Display to "summarized" when unset.

Changes

Cohort / File(s) Summary
Claude Opus 4.7 Parameter Normalization
relay/channel/claude/relay-claude.go, relay/claude_handler.go
Post-mapping enforcement for requests whose model starts with claude-opus-4-7: unset Temperature, TopP, TopK for bare-model cases; if Thinking.Type == "enabled" convert to "adaptive", clear Thinking.BudgetTokens, and set Thinking.Display = "summarized" when empty.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Suggested reviewers

  • seefs001

Poem

🐰 Opus four-seven, tidy and bright,
I hop through fields and set defaults right.
Temperatures gone, thinking trimmed with care,
Summarized thoughts float lightly in air. ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding special parameter sanitization (clearing temperature/top_p/top_k and converting thinking types) specifically for Claude Opus 4.7 models across the bare model and reasoning_effort code paths.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
relay/channel/claude/relay-claude.go (2)

249-256: Effort granularity is dropped when converting enabledadaptive.

The conversion is correct for satisfying the Opus 4.7 schema, but a user-supplied reasoning_effort of low/medium/high (or a reasoning.max_tokens) is effectively discarded here: the BudgetTokens mapping set at lines 217/222/227 (and 242-245) is cleared without a corresponding OutputConfig={"effort":"<level>"} translation. Compare the effort-suffix branch (line 162) which does preserve effort via OutputConfig. Consider mapping reasoning_effortOutputConfig for Opus 4.7 so the user's requested effort level is still signaled to the upstream.

♻️ Proposed mapping
 	// Opus 4.7 rejects thinking.type="enabled"; convert to adaptive
 	if strings.HasPrefix(claudeRequest.Model, "claude-opus-4-7") && claudeRequest.Thinking != nil && claudeRequest.Thinking.Type == "enabled" {
 		claudeRequest.Thinking.Type = "adaptive"
 		claudeRequest.Thinking.BudgetTokens = nil
 		if claudeRequest.Thinking.Display == "" {
 			claudeRequest.Thinking.Display = "summarized"
 		}
+		if len(claudeRequest.OutputConfig) == 0 && textRequest.ReasoningEffort != "" {
+			claudeRequest.OutputConfig = json.RawMessage(fmt.Sprintf(`{"effort":"%s"}`, textRequest.ReasoningEffort))
+		}
 	}

Please confirm with the Anthropic Opus 4.7 docs whether thinking.type="adaptive" without output_config.effort behaves equivalently to an explicit effort level for the low/medium/high tiers, to decide whether this mapping is worth adding.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@relay/channel/claude/relay-claude.go` around lines 249 - 256, When converting
claudeRequest.Thinking.Type from "enabled" to "adaptive" in the Opus 4.7 branch,
preserve user effort and token-budget settings by translating
Thinking.BudgetTokens and any reasoning_effort into the corresponding
OutputConfig fields instead of clearing BudgetTokens; specifically, populate
claudeRequest.OutputConfig.effort from
claudeRequest.Thinking.reasing_effort/effort-level (low/medium/high) and map
reasoning.max_tokens (or Thinking.BudgetTokens) into OutputConfig.max_tokens (or
the schema-appropriate field) before setting Thinking.Type="adaptive" and
nulling Thinking.BudgetTokens, and ensure the code that handles the
effort-suffix branch (where OutputConfig is used) is reused for this branch so
effort is signaled upstream (also add a TODO to verify with Opus 4.7 docs
whether adaptive without output_config.effort is equivalent).

206-211: Opus 4.7 bare-model sanitization — correct, with minor duplication.

The fix correctly closes the bare-path gap. Note that this block now overlaps with the same nil-assignments already performed in the effort-suffix branch (lines 167-169) and the -thinking branch (lines 182-184), making those earlier clears redundant. Harmless, but you could collapse them into this single unconditional post-block clear for Opus 4.7 to keep the normalization in one place.

♻️ Optional consolidation
 		if strings.HasPrefix(baseModel, "claude-opus-4-7") {
-			// Opus 4.7 rejects non-default temperature/top_p/top_k with 400
-			// and defaults display to "omitted"; restore the 4.6 visible summary.
+			// Opus 4.7 defaults display to "omitted"; restore the 4.6 visible summary.
+			// temperature/top_p/top_k are cleared unconditionally below.
 			claudeRequest.Thinking.Display = "summarized"
-			claudeRequest.Temperature = nil
-			claudeRequest.TopP = nil
-			claudeRequest.TopK = nil
 		} else {
 			claudeRequest.TopP = nil
 			claudeRequest.Temperature = common.GetPointer[float64](1.0)
 		}

(and similarly drop lines 182-184 in the -thinking branch)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@relay/channel/claude/relay-claude.go` around lines 206 - 211, The niling of
temperature/top_p/top_k for Opus 4.7 is duplicated across branches; consolidate
by removing the earlier clears in the effort-suffix and -thinking branches and
keep a single unconditional clear for any model matching
strings.HasPrefix(claudeRequest.Model, "claude-opus-4-7") where you set
claudeRequest.Temperature = nil, claudeRequest.TopP = nil, claudeRequest.TopK =
nil so normalization lives only in that one post-check block.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@relay/claude_handler.go`:
- Around line 106-111: The Claude-format handler (ClaudeHelper) currently clears
temperature/top_p/top_k for models with prefix "claude-opus-4-7" but does not
perform the companion translation of thinking.type="enabled" to
thinking.type="adaptive", which causes Anthropic to reject such requests; after
the existing if block that nils request.Temperature/TopP/TopK, detect if
request.Model has prefix "claude-opus-4-7" and if request.Thinking != nil and
request.Thinking.Type == "enabled" then set request.Thinking.Type = "adaptive"
(mirroring the conversion in relay/channel/claude/relay-claude.go) so
pass-through Claude-format clients are accepted.

---

Nitpick comments:
In `@relay/channel/claude/relay-claude.go`:
- Around line 249-256: When converting claudeRequest.Thinking.Type from
"enabled" to "adaptive" in the Opus 4.7 branch, preserve user effort and
token-budget settings by translating Thinking.BudgetTokens and any
reasoning_effort into the corresponding OutputConfig fields instead of clearing
BudgetTokens; specifically, populate claudeRequest.OutputConfig.effort from
claudeRequest.Thinking.reasing_effort/effort-level (low/medium/high) and map
reasoning.max_tokens (or Thinking.BudgetTokens) into OutputConfig.max_tokens (or
the schema-appropriate field) before setting Thinking.Type="adaptive" and
nulling Thinking.BudgetTokens, and ensure the code that handles the
effort-suffix branch (where OutputConfig is used) is reused for this branch so
effort is signaled upstream (also add a TODO to verify with Opus 4.7 docs
whether adaptive without output_config.effort is equivalent).
- Around line 206-211: The niling of temperature/top_p/top_k for Opus 4.7 is
duplicated across branches; consolidate by removing the earlier clears in the
effort-suffix and -thinking branches and keep a single unconditional clear for
any model matching strings.HasPrefix(claudeRequest.Model, "claude-opus-4-7")
where you set claudeRequest.Temperature = nil, claudeRequest.TopP = nil,
claudeRequest.TopK = nil so normalization lives only in that one post-check
block.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ec7f0c64-bc0a-4609-9ef5-8683ab881c56

📥 Commits

Reviewing files that changed from the base of the PR and between f995a86 and 3dff584.

📒 Files selected for processing (2)
  • relay/channel/claude/relay-claude.go
  • relay/claude_handler.go

Comment thread relay/claude_handler.go Outdated
forgottener and others added 2 commits April 19, 2026 00:31
…deHelper

The native Claude API path (ClaudeHelper) was missing the thinking.type
conversion that relay-claude.go already performs. A client sending
thinking.type="enabled" directly on a claude-opus-4-7 request would still
get HTTP 400 from Anthropic.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Combine temperature/top_p/top_k clearing and thinking.type="enabled" →
"adaptive" conversion into one if-block for clarity.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@forgottener forgottener closed this by deleting the head repository Jul 6, 2026
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