Skip to content
Merged
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
3 changes: 2 additions & 1 deletion src/services/apis/custom-api.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { fetchSSE } from '../../utils/fetch-sse.mjs'
import { getConversationPairs } from '../../utils/get-conversation-pairs.mjs'
import { isEmpty } from 'lodash-es'
import { pushRecord, setAbortController } from './shared.mjs'
import { getChatCompletionsTokenParams } from './openai-token-params.mjs'

/**
* @param {Browser.Runtime.Port} port
Expand Down Expand Up @@ -55,7 +56,7 @@ export async function generateAnswersWithCustomApi(
messages: prompt,
model: modelName,
stream: true,
max_tokens: config.maxResponseTokenLength,
...getChatCompletionsTokenParams('custom', modelName, config.maxResponseTokenLength),
temperature: config.temperature,
}),
Comment on lines 56 to 61
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

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

generateAnswersWithCustomApi always passes 'custom' as the provider, which guarantees max_tokens even when the custom URL/model are actually targeting OpenAI GPT‑5 chat completions (a configuration that currently exists via customModelApiUrl/customModelName). That setup would still hit the original "Unsupported parameter: 'max_tokens'" error. Consider accepting a provider argument (from session.apiMode / config) or inferring it from apiUrl so GPT‑5+OpenAI can use max_completion_tokens when appropriate.

Copilot uses AI. Check for mistakes.
onMessage(message) {
Expand Down
14 changes: 12 additions & 2 deletions src/services/apis/openai-api.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { getConversationPairs } from '../../utils/get-conversation-pairs.mjs'
import { isEmpty } from 'lodash-es'
import { getCompletionPromptBase, pushRecord, setAbortController } from './shared.mjs'
import { getModelValue } from '../../utils/model-name-convert.mjs'
import { getChatCompletionsTokenParams } from './openai-token-params.mjs'

/**
* @param {Browser.Runtime.Port} port
Expand Down Expand Up @@ -103,6 +104,8 @@ export async function generateAnswersWithChatgptApi(port, question, session, api
question,
session,
apiKey,
{},
'openai',
)
}

Expand All @@ -113,6 +116,7 @@ export async function generateAnswersWithChatgptApiCompat(
session,
apiKey,
extraBody = {},
provider = 'compat',
) {
const { controller, messageListener, disconnectListener } = setAbortController(port)
const model = getModelValue(session)
Expand All @@ -123,6 +127,12 @@ export async function generateAnswersWithChatgptApiCompat(
false,
)
prompt.push({ role: 'user', content: question })
const tokenParams = getChatCompletionsTokenParams(provider, model, config.maxResponseTokenLength)
const conflictingTokenParamKey =
'max_completion_tokens' in tokenParams ? 'max_tokens' : 'max_completion_tokens'
// Avoid sending both token-limit fields when caller passes extraBody.
const safeExtraBody = { ...extraBody }
delete safeExtraBody[conflictingTokenParamKey]

let answer = ''
let finished = false
Expand All @@ -143,9 +153,9 @@ export async function generateAnswersWithChatgptApiCompat(
messages: prompt,
model,
stream: true,
max_tokens: config.maxResponseTokenLength,
...tokenParams,
temperature: config.temperature,
...extraBody,
...safeExtraBody,
}),
onMessage(message) {
console.debug('sse message', message)
Expand Down
21 changes: 21 additions & 0 deletions src/services/apis/openai-token-params.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const GPT5_CHAT_COMPLETIONS_MODEL_PATTERN = /(^|\/)gpt-5([.-]|$)/
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

OpenAI's o1 and o3 model families also require max_completion_tokens and do not support max_tokens. Consider expanding this regex to include these models to prevent similar errors when they are used via the OpenAI API provider.

Suggested change
const GPT5_CHAT_COMPLETIONS_MODEL_PATTERN = /(^|\/)gpt-5([.-]|$)/
const GPT5_CHAT_COMPLETIONS_MODEL_PATTERN = /(^|\/)(gpt-5|o[13])([.-]|$)/


function shouldUseMaxCompletionTokens(provider, model) {
const normalizedProvider = String(provider || '').toLowerCase()
const normalizedModel = String(model || '').toLowerCase()

switch (true) {
case normalizedProvider === 'openai' &&
GPT5_CHAT_COMPLETIONS_MODEL_PATTERN.test(normalizedModel):
return true
default:
return false
}
Comment on lines +7 to +13
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The switch (true) block is unnecessary for a single conditional check. Simplifying this to a direct return statement improves readability and follows standard JavaScript idioms.

  return (
    normalizedProvider === 'openai' &&
    GPT5_CHAT_COMPLETIONS_MODEL_PATTERN.test(normalizedModel)
  )

}

export function getChatCompletionsTokenParams(provider, model, maxResponseTokenLength) {
if (shouldUseMaxCompletionTokens(provider, model))
return { max_completion_tokens: maxResponseTokenLength }

return { max_tokens: maxResponseTokenLength }
}
57 changes: 57 additions & 0 deletions src/services/apis/openai-token-params.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import test from 'node:test'
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This file was placed at the wrong place 😓

import assert from 'node:assert/strict'
import { getChatCompletionsTokenParams } from './openai-token-params.mjs'
Comment thread
PeterDaveHello marked this conversation as resolved.

Comment on lines +1 to +4
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

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

This test file won’t be executed in CI as-is: the repo’s PR workflow only runs npm run lint and npm run build, and package.json doesn’t define a test script (e.g. node --test). Consider wiring these unit tests into the standard verification path (add a test script and run it in .github/workflows/pr-tests.yml) so regressions in token-param selection are caught automatically.

Copilot uses AI. Check for mistakes.
test('uses max_completion_tokens for gpt-5.x chat models', () => {
assert.deepEqual(getChatCompletionsTokenParams('openai', 'gpt-5.2-chat-latest', 1024), {
max_completion_tokens: 1024,
})
})

test('uses max_completion_tokens for provider-prefixed gpt-5.x models', () => {
assert.deepEqual(getChatCompletionsTokenParams('openai', 'openai/gpt-5.2', 2048), {
max_completion_tokens: 2048,
})
})

test('uses max_completion_tokens for gpt-5 baseline model name', () => {
assert.deepEqual(getChatCompletionsTokenParams('openai', 'gpt-5', 1536), {
max_completion_tokens: 1536,
})
})

test('uses max_tokens for non gpt-5 chat models', () => {
assert.deepEqual(getChatCompletionsTokenParams('openai', 'gpt-4o', 512), {
max_tokens: 512,
})
})

test('uses max_tokens for lookalike model names', () => {
assert.deepEqual(getChatCompletionsTokenParams('openai', 'my-gpt-5-clone', 640), {
max_tokens: 640,
})
})

test('uses max_tokens for empty model values', () => {
assert.deepEqual(getChatCompletionsTokenParams('openai', '', 256), {
max_tokens: 256,
})
})

test('uses max_tokens for non OpenAI providers even with gpt-5 models', () => {
assert.deepEqual(getChatCompletionsTokenParams('some-proxy-provider', 'openai/gpt-5.2', 257), {
max_tokens: 257,
})
})

test('uses max_completion_tokens for mixed-case OpenAI provider and model', () => {
assert.deepEqual(getChatCompletionsTokenParams('OpenAI', 'GPT-5.1', 258), {
max_completion_tokens: 258,
})
})

test('uses max_tokens when provider is undefined', () => {
assert.deepEqual(getChatCompletionsTokenParams(undefined, 'gpt-5.1', 259), {
max_tokens: 259,
})
})