mirror of
https://github.com/vxcontrol/langchaingo.git
synced 2026-07-19 21:54:17 -04:00
fix(anthropic): drop top_p with budget thinking to satisfy the API
Budget thinking pins temperature to 1.0, but the Messages API rejects temperature and top_p specified together (and rejects top_p below 0.95 with thinking enabled). The code forwarded the caller's top_p unchanged, so WithReasoning(...) + WithTopP(anything) returned 400. Drop top_p on the budget path, matching the adaptive path which already omits both. Live-verified against the real API: WithReasoning(Medium,0)+WithTopP(0.9) and +WithTopP(0.97) both returned 400 before, 200 after. The budget wire test now asserts top_p is absent rather than passed through. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -16,7 +16,7 @@ import (
|
||||
// captureMessagesRequest drives a real GenerateContent call against a recording
|
||||
// server and returns the decoded outbound /v1/messages body and headers, so
|
||||
// assertions cover generateMessagesContent (thinking construction, sampling-param
|
||||
// handling, beta headers), not a hand-built payload.
|
||||
// handling, beta headers).
|
||||
func captureMessagesRequest(t *testing.T, callOpts ...llms.CallOption) (map[string]any, http.Header) {
|
||||
t.Helper()
|
||||
|
||||
@@ -124,9 +124,11 @@ func TestAnthropic_BudgetThinkingRequest(t *testing.T) {
|
||||
_, hasOutputConfig := payload["output_config"]
|
||||
assert.False(t, hasOutputConfig, "budget thinking has no output_config")
|
||||
|
||||
// Budget thinking pins temperature to 1.0 and keeps top_p as provided.
|
||||
// Budget thinking pins temperature to 1.0 and drops top_p — the API rejects
|
||||
// temperature and top_p together.
|
||||
assert.EqualValues(t, 1.0, payload["temperature"])
|
||||
assert.EqualValues(t, 0.9, payload["top_p"])
|
||||
_, hasTopP := payload["top_p"]
|
||||
assert.False(t, hasTopP, "budget thinking must drop top_p")
|
||||
assert.EqualValues(t, 4096, payload["max_tokens"]) // max(budget*2, maxTokens)
|
||||
}
|
||||
|
||||
|
||||
@@ -210,8 +210,10 @@ func generateMessagesContent(ctx context.Context, o *LLM, messages []llms.Messag
|
||||
}
|
||||
}
|
||||
|
||||
// Budget thinking pins temperature to 1.0; adaptive models reject sampling
|
||||
// params, so omit temperature/top_p entirely for them.
|
||||
// Thinking constrains sampling params: the API rejects temperature and top_p
|
||||
// together, and requires temperature=1.0 with budget thinking. So pin
|
||||
// temperature and drop top_p for budget; drop both for adaptive (which
|
||||
// rejects sampling params outright).
|
||||
temperature, topP, maxTokens := opts.Temperature, opts.TopP, opts.GetMaxTokens()
|
||||
switch {
|
||||
case thinking != nil && thinking.Type == "adaptive":
|
||||
@@ -219,6 +221,7 @@ func generateMessagesContent(ctx context.Context, o *LLM, messages []llms.Messag
|
||||
topP = nil
|
||||
case thinking != nil && thinking.Type == "enabled" && thinking.Budget > 0:
|
||||
temperature = getFloatPointer(1.0)
|
||||
topP = nil
|
||||
maxTokens = max(thinking.Budget*2, maxTokens) // 2x the budget for thinking
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user