Skip to content

Commit 2f3accc

Browse files
Add OpenAI chat-latest support and generalize token handling
Add the chat-latest OpenAI API preset. Broaden the OpenAI token-parameter helper naming so chat-latest can share the max_completion_tokens path with GPT-5 latest models. Reference: - https://developers.openai.com/api/docs/models/chat-latest
1 parent 2cb883a commit 2f3accc

5 files changed

Lines changed: 28 additions & 14 deletions

File tree

src/config/index.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export const chatgptApiModelKeys = [
5050
'chatgptApi35_1106',
5151
'chatgptApi35_0125',
5252
'chatgptApi4o_128k',
53+
'chatgptApiChatLatest',
5354
'chatgptApi5Latest',
5455
'chatgptApi5',
5556
'chatgptApi5_1Latest',
@@ -255,6 +256,7 @@ export const Models = {
255256
value: 'gpt-4-0125-preview',
256257
desc: 'OpenAI (GPT-4-Turbo 128k 0125 Preview)',
257258
},
259+
chatgptApiChatLatest: { value: 'chat-latest', desc: 'OpenAI (Chat latest)' },
258260
chatgptApi5Latest: { value: 'gpt-5-chat-latest', desc: 'OpenAI (GPT-5 latest)' },
259261
chatgptApi5: { value: 'gpt-5', desc: 'OpenAI (GPT-5)' },
260262
chatgptApi5_1Latest: { value: 'gpt-5.1-chat-latest', desc: 'OpenAI (GPT-5.1 latest)' },

src/services/apis/openai-token-params.mjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
const GPT5_CHAT_COMPLETIONS_MODEL_PATTERN = /^gpt-5([.-]|$)/
1+
const OPENAI_MAX_COMPLETION_TOKENS_MODEL_PATTERN = /^(?:gpt-5(?:[.-]|$)|chat-latest$)/
22

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

77
switch (true) {
88
case normalizedProvider === 'openai' &&
9-
GPT5_CHAT_COMPLETIONS_MODEL_PATTERN.test(normalizedModel):
9+
OPENAI_MAX_COMPLETION_TOKENS_MODEL_PATTERN.test(normalizedModel):
1010
return true
1111
default:
1212
return false

tests/unit/services/apis/openai-api-compat.test.mjs

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@ import {
88
import { createFakePort } from '../../helpers/port.mjs'
99
import { createMockSseResponse } from '../../helpers/sse-response.mjs'
1010

11-
const gpt5LatestCompatModelNames = [
11+
const latestCompatModelNames = [
12+
'chatgptApi-chat-latest',
1213
'chatgptApi-gpt-5-chat-latest',
1314
'chatgptApi-gpt-5.1-chat-latest',
1415
'chatgptApi-gpt-5.2-chat-latest',
1516
'chatgptApi-gpt-5.3-chat-latest',
1617
]
17-
const gpt5LatestMappedModels = [
18+
const latestMappedModels = [
19+
['chatgptApiChatLatest', 'chat-latest'],
1820
['chatgptApi5Latest', 'gpt-5-chat-latest'],
1921
['chatgptApi5_1Latest', 'gpt-5.1-chat-latest'],
2022
['chatgptApi5_2Latest', 'gpt-5.2-chat-latest'],
@@ -93,7 +95,7 @@ test('generateAnswersWithOpenAiApiCompat sends expected request and aggregates S
9395
assert.deepEqual(session.conversationRecords.at(-1), { question: 'CurrentQ', answer: 'Hello' })
9496
})
9597

96-
test('generateAnswersWithOpenAiApiCompat uses max_completion_tokens for OpenAI latest gpt-5 compat models', async (t) => {
98+
test('generateAnswersWithOpenAiApiCompat uses max_completion_tokens for OpenAI latest compat models', async (t) => {
9799
t.mock.method(console, 'debug', () => {})
98100
setStorage({
99101
maxConversationContextLength: 3,
@@ -108,7 +110,7 @@ test('generateAnswersWithOpenAiApiCompat uses max_completion_tokens for OpenAI l
108110
])
109111
})
110112

111-
for (const modelName of gpt5LatestCompatModelNames) {
113+
for (const modelName of latestCompatModelNames) {
112114
capturedInit = undefined
113115
const session = {
114116
modelName,
@@ -133,7 +135,7 @@ test('generateAnswersWithOpenAiApiCompat uses max_completion_tokens for OpenAI l
133135
}
134136
})
135137

136-
test('generateAnswersWithOpenAiApiCompat uses latest mapped gpt-5 API model values', async (t) => {
138+
test('generateAnswersWithOpenAiApiCompat uses latest mapped API model values', async (t) => {
137139
t.mock.method(console, 'debug', () => {})
138140
setStorage({
139141
maxConversationContextLength: 3,
@@ -148,7 +150,7 @@ test('generateAnswersWithOpenAiApiCompat uses latest mapped gpt-5 API model valu
148150
])
149151
})
150152

151-
for (const [modelName, expectedModel] of gpt5LatestMappedModels) {
153+
for (const [modelName, expectedModel] of latestMappedModels) {
152154
capturedInit = undefined
153155
const session = {
154156
modelName,
@@ -279,7 +281,7 @@ test('generateAnswersWithOpenAiApi uses max_completion_tokens for GPT-5.4 nano',
279281
assert.equal(Object.hasOwn(body, 'max_tokens'), false)
280282
})
281283

282-
test('generateAnswersWithOpenAiApiCompat keeps max_tokens for latest mapped gpt-5 models in compat provider', async (t) => {
284+
test('generateAnswersWithOpenAiApiCompat keeps max_tokens for latest mapped models in compat provider', async (t) => {
283285
t.mock.method(console, 'debug', () => {})
284286
setStorage({
285287
maxConversationContextLength: 3,
@@ -294,7 +296,7 @@ test('generateAnswersWithOpenAiApiCompat keeps max_tokens for latest mapped gpt-
294296
])
295297
})
296298

297-
for (const [modelName, expectedModel] of gpt5LatestMappedModels) {
299+
for (const [modelName, expectedModel] of latestMappedModels) {
298300
capturedInit = undefined
299301
const session = {
300302
modelName,
@@ -361,7 +363,7 @@ test('generateAnswersWithOpenAiApiCompat removes conflicting token key from extr
361363
assert.equal(body.top_p, 0.9)
362364
})
363365

364-
test('generateAnswersWithOpenAiApiCompat removes max_tokens from extraBody for OpenAI gpt-5 models', async (t) => {
366+
test('generateAnswersWithOpenAiApiCompat removes max_tokens from extraBody for OpenAI latest models', async (t) => {
365367
t.mock.method(console, 'debug', () => {})
366368
setStorage({
367369
maxConversationContextLength: 3,
@@ -376,7 +378,7 @@ test('generateAnswersWithOpenAiApiCompat removes max_tokens from extraBody for O
376378
])
377379
})
378380

379-
for (const modelName of gpt5LatestCompatModelNames) {
381+
for (const modelName of latestCompatModelNames) {
380382
capturedInit = undefined
381383
const session = {
382384
modelName,
@@ -446,7 +448,7 @@ test('generateAnswersWithOpenAiApiCompat allows max_tokens override for compat p
446448
assert.equal(body.top_p, 0.75)
447449
})
448450

449-
test('generateAnswersWithOpenAiApiCompat allows max_completion_tokens override for OpenAI gpt-5 models', async (t) => {
451+
test('generateAnswersWithOpenAiApiCompat allows max_completion_tokens override for OpenAI latest models', async (t) => {
450452
t.mock.method(console, 'debug', () => {})
451453
setStorage({
452454
maxConversationContextLength: 3,
@@ -461,7 +463,7 @@ test('generateAnswersWithOpenAiApiCompat allows max_completion_tokens override f
461463
])
462464
})
463465

464-
for (const modelName of gpt5LatestCompatModelNames) {
466+
for (const modelName of latestCompatModelNames) {
465467
capturedInit = undefined
466468
const session = {
467469
modelName,

tests/unit/services/apis/openai-token-params.test.mjs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ test('uses max_completion_tokens for gpt-5.x chat models', () => {
88
})
99
})
1010

11+
test('uses max_completion_tokens for chat-latest model', () => {
12+
assert.deepEqual(getChatCompletionsTokenParams('openai', 'chat-latest', 1025), {
13+
max_completion_tokens: 1025,
14+
})
15+
})
16+
1117
test('uses max_tokens for provider-prefixed gpt-5.x model names', () => {
1218
assert.deepEqual(getChatCompletionsTokenParams('openai', 'openai/gpt-5.2', 2048), {
1319
max_tokens: 2048,
@@ -50,6 +56,9 @@ test('uses max_tokens for lookalike model names', () => {
5056
assert.deepEqual(getChatCompletionsTokenParams('openai', 'my-gpt-5-clone', 640), {
5157
max_tokens: 640,
5258
})
59+
assert.deepEqual(getChatCompletionsTokenParams('openai', 'my-chat-latest', 641), {
60+
max_tokens: 641,
61+
})
5362
})
5463

5564
test('uses max_tokens for empty model values', () => {

tests/unit/utils/model-name-convert.test.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,7 @@ test('modelNameToValue returns value for known model', () => {
258258
})
259259

260260
test('modelNameToValue returns endpoint for latest chatgptApi models', () => {
261+
assert.equal(modelNameToValue('chatgptApiChatLatest'), 'chat-latest')
261262
assert.equal(modelNameToValue('chatgptApi5Latest'), 'gpt-5-chat-latest')
262263
assert.equal(modelNameToValue('chatgptApi5_1Latest'), 'gpt-5.1-chat-latest')
263264
assert.equal(modelNameToValue('chatgptApi5_2Latest'), 'gpt-5.2-chat-latest')

0 commit comments

Comments
 (0)