Skip to content

Commit aabd810

Browse files
Use provider-aware token parameter for GPT-5-family chat completions
Use max_completion_tokens for OpenAI GPT-5-family Chat Completions and keep max_tokens for non-openai or non-GPT-5-family paths. Observed error on GPT-5.1-family responses: Unsupported parameter: 'max_tokens' is not supported with this model. Use 'max_completion_tokens' instead.
1 parent 055d6b4 commit aabd810

4 files changed

Lines changed: 92 additions & 3 deletions

File tree

src/services/apis/custom-api.mjs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { fetchSSE } from '../../utils/fetch-sse.mjs'
1010
import { getConversationPairs } from '../../utils/get-conversation-pairs.mjs'
1111
import { isEmpty } from 'lodash-es'
1212
import { pushRecord, setAbortController } from './shared.mjs'
13+
import { getChatCompletionsTokenParams } from './openai-token-params.mjs'
1314

1415
/**
1516
* @param {Browser.Runtime.Port} port
@@ -55,7 +56,7 @@ export async function generateAnswersWithCustomApi(
5556
messages: prompt,
5657
model: modelName,
5758
stream: true,
58-
max_tokens: config.maxResponseTokenLength,
59+
...getChatCompletionsTokenParams('custom', modelName, config.maxResponseTokenLength),
5960
temperature: config.temperature,
6061
}),
6162
onMessage(message) {

src/services/apis/openai-api.mjs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { getConversationPairs } from '../../utils/get-conversation-pairs.mjs'
66
import { isEmpty } from 'lodash-es'
77
import { getCompletionPromptBase, pushRecord, setAbortController } from './shared.mjs'
88
import { getModelValue } from '../../utils/model-name-convert.mjs'
9+
import { getChatCompletionsTokenParams } from './openai-token-params.mjs'
910

1011
/**
1112
* @param {Browser.Runtime.Port} port
@@ -103,6 +104,8 @@ export async function generateAnswersWithChatgptApi(port, question, session, api
103104
question,
104105
session,
105106
apiKey,
107+
{},
108+
'openai',
106109
)
107110
}
108111

@@ -113,6 +116,7 @@ export async function generateAnswersWithChatgptApiCompat(
113116
session,
114117
apiKey,
115118
extraBody = {},
119+
provider = 'compat',
116120
) {
117121
const { controller, messageListener, disconnectListener } = setAbortController(port)
118122
const model = getModelValue(session)
@@ -123,6 +127,12 @@ export async function generateAnswersWithChatgptApiCompat(
123127
false,
124128
)
125129
prompt.push({ role: 'user', content: question })
130+
const tokenParams = getChatCompletionsTokenParams(provider, model, config.maxResponseTokenLength)
131+
const conflictingTokenParamKey =
132+
'max_completion_tokens' in tokenParams ? 'max_tokens' : 'max_completion_tokens'
133+
// Avoid sending both token-limit fields when caller passes extraBody.
134+
const safeExtraBody = { ...extraBody }
135+
delete safeExtraBody[conflictingTokenParamKey]
126136

127137
let answer = ''
128138
let finished = false
@@ -143,9 +153,9 @@ export async function generateAnswersWithChatgptApiCompat(
143153
messages: prompt,
144154
model,
145155
stream: true,
146-
max_tokens: config.maxResponseTokenLength,
156+
...tokenParams,
147157
temperature: config.temperature,
148-
...extraBody,
158+
...safeExtraBody,
149159
}),
150160
onMessage(message) {
151161
console.debug('sse message', message)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
const GPT5_CHAT_COMPLETIONS_MODEL_PATTERN = /(^|\/)gpt-5([.-]|$)/
2+
3+
function shouldUseMaxCompletionTokens(provider, model) {
4+
const normalizedProvider = String(provider || '').toLowerCase()
5+
const normalizedModel = String(model || '').toLowerCase()
6+
7+
switch (true) {
8+
case normalizedProvider === 'openai' &&
9+
GPT5_CHAT_COMPLETIONS_MODEL_PATTERN.test(normalizedModel):
10+
return true
11+
default:
12+
return false
13+
}
14+
}
15+
16+
export function getChatCompletionsTokenParams(provider, model, maxResponseTokenLength) {
17+
if (shouldUseMaxCompletionTokens(provider, model))
18+
return { max_completion_tokens: maxResponseTokenLength }
19+
20+
return { max_tokens: maxResponseTokenLength }
21+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import test from 'node:test'
2+
import assert from 'node:assert/strict'
3+
import { getChatCompletionsTokenParams } from './openai-token-params.mjs'
4+
5+
test('uses max_completion_tokens for gpt-5.x chat models', () => {
6+
assert.deepEqual(getChatCompletionsTokenParams('openai', 'gpt-5.2-chat-latest', 1024), {
7+
max_completion_tokens: 1024,
8+
})
9+
})
10+
11+
test('uses max_completion_tokens for provider-prefixed gpt-5.x models', () => {
12+
assert.deepEqual(getChatCompletionsTokenParams('openai', 'openai/gpt-5.2', 2048), {
13+
max_completion_tokens: 2048,
14+
})
15+
})
16+
17+
test('uses max_completion_tokens for gpt-5 baseline model name', () => {
18+
assert.deepEqual(getChatCompletionsTokenParams('openai', 'gpt-5', 1536), {
19+
max_completion_tokens: 1536,
20+
})
21+
})
22+
23+
test('uses max_tokens for non gpt-5 chat models', () => {
24+
assert.deepEqual(getChatCompletionsTokenParams('openai', 'gpt-4o', 512), {
25+
max_tokens: 512,
26+
})
27+
})
28+
29+
test('uses max_tokens for lookalike model names', () => {
30+
assert.deepEqual(getChatCompletionsTokenParams('openai', 'my-gpt-5-clone', 640), {
31+
max_tokens: 640,
32+
})
33+
})
34+
35+
test('uses max_tokens for empty model values', () => {
36+
assert.deepEqual(getChatCompletionsTokenParams('openai', '', 256), {
37+
max_tokens: 256,
38+
})
39+
})
40+
41+
test('uses max_tokens for non OpenAI providers even with gpt-5 models', () => {
42+
assert.deepEqual(getChatCompletionsTokenParams('some-proxy-provider', 'openai/gpt-5.2', 257), {
43+
max_tokens: 257,
44+
})
45+
})
46+
47+
test('uses max_completion_tokens for mixed-case OpenAI provider and model', () => {
48+
assert.deepEqual(getChatCompletionsTokenParams('OpenAI', 'GPT-5.1', 258), {
49+
max_completion_tokens: 258,
50+
})
51+
})
52+
53+
test('uses max_tokens when provider is undefined', () => {
54+
assert.deepEqual(getChatCompletionsTokenParams(undefined, 'gpt-5.1', 259), {
55+
max_tokens: 259,
56+
})
57+
})

0 commit comments

Comments
 (0)