Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions providers/openai.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ func (p *OpenAIProvider) needsMaxCompletionTokens() bool {
return true
}

// Check for gpt-5 and newer chat models, which require max_completion_tokens
// and reject max_tokens with HTTP 400 (e.g. gpt-5, gpt-5-mini, gpt-5.4-2026-03-05)
if strings.HasPrefix(p.model, "gpt-5") {
return true
}

return false
}

Expand Down
24 changes: 24 additions & 0 deletions providers/openai_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package providers

import (
"encoding/json"
"testing"

"github.com/stretchr/testify/assert"
Expand All @@ -21,6 +22,9 @@ func TestNeedsMaxCompletionTokens(t *testing.T) {
{"o-preview", true, "o-preview model should use max_completion_tokens"},
{"gpt-4o", true, "GPT-4o model should use max_completion_tokens"},
{"gpt-4o-mini", true, "GPT-4o mini model should use max_completion_tokens"},
{"gpt-5", true, "GPT-5 model should use max_completion_tokens"},
{"gpt-5-mini", true, "GPT-5 mini model should use max_completion_tokens"},
{"gpt-5.4-2026-03-05", true, "Dated GPT-5 model should use max_completion_tokens"},
}

for _, tc := range testCases {
Expand All @@ -38,3 +42,23 @@ func TestNeedsMaxCompletionTokens(t *testing.T) {
})
}
}

// TestPrepareRequestGPT5UsesMaxCompletionTokens verifies that a gpt-5 request
// carries max_completion_tokens and never max_tokens — the parameter gpt-5-class
// models reject with HTTP 400.
func TestPrepareRequestGPT5UsesMaxCompletionTokens(t *testing.T) {
provider, ok := NewOpenAIProvider("fake-api-key", "gpt-5.4-2026-03-05", nil).(*OpenAIProvider)
assert.True(t, ok, "Provider should be of type *OpenAIProvider")

body, err := provider.PrepareRequest("hello", map[string]interface{}{"max_tokens": 4096})
assert.NoError(t, err)

var req map[string]interface{}
assert.NoError(t, json.Unmarshal(body, &req))

_, hasMaxTokens := req["max_tokens"]
maxCompletion, hasMaxCompletion := req["max_completion_tokens"]
assert.False(t, hasMaxTokens, "gpt-5 request must not send max_tokens (OpenAI rejects it with HTTP 400)")
assert.True(t, hasMaxCompletion, "gpt-5 request must send max_completion_tokens instead")
assert.Equal(t, float64(4096), maxCompletion, "token budget must be preserved across the conversion")
}