Skip to content

Commit 4ab8169

Browse files
Remove Unavailable OpenRouter And AIML Models
Remove OpenRouter and AIML model IDs that no longer appear in provider model catalogs as of 2026-02-17. Add config-load migration for deprecated model keys so persisted user settings continue to resolve to supported models instead of failing with Unknown model configuration. Removed OpenRouter: - deepseek/deepseek-chat-v3-0324:free Removed AIML: - anthropic/claude-opus-4 - anthropic/claude-opus-4-1 - anthropic/claude-sonnet-4 - anthropic/claude-sonnet-4-5 - google/gemini-2.5-pro-preview-05-06 - google/gemini-2.5-flash-preview - deepseek/deepseek-chat Sources: - https://openrouter.ai/api/v1/models - https://api.aimlapi.com/v1/models
1 parent f55c577 commit 4ab8169

2 files changed

Lines changed: 186 additions & 44 deletions

File tree

src/config/index.mjs

Lines changed: 171 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -114,19 +114,11 @@ export const openRouterApiModelKeys = [
114114
'openRouter_google_gemini_2_5_flash',
115115
'openRouter_openai_o3',
116116
'openRouter_openai_gpt_4_1_mini',
117-
'openRouter_deepseek_deepseek_chat_v3_0324_free',
118117
]
119118
export const aimlApiModelKeys = [
120-
'aiml_anthropic_claude_opus_4',
121-
'aiml_anthropic_claude_sonnet_4',
122-
'aiml_anthropic_claude_sonnet_4_5',
123-
'aiml_anthropic_claude_opus_4_1',
124119
'aiml_claude_3_7_sonnet_20250219',
125-
'aiml_google_gemini_2_5_pro_preview_05_06',
126-
'aiml_google_gemini_2_5_flash_preview',
127120
'aiml_openai_o3_2025_04_16',
128121
'aiml_openai_gpt_4_1_2025_04_14',
129-
'aiml_deepseek_deepseek_chat',
130122
'aiml_moonshot_kimi_k2_preview',
131123
]
132124

@@ -411,39 +403,10 @@ export const Models = {
411403
value: 'openai/gpt-4.1-mini',
412404
desc: 'OpenRouter (GPT-4.1 Mini)',
413405
},
414-
openRouter_deepseek_deepseek_chat_v3_0324_free: {
415-
value: 'deepseek/deepseek-chat-v3-0324:free',
416-
desc: 'OpenRouter (DeepSeek Chat v3 Free)',
417-
},
418-
419-
aiml_anthropic_claude_opus_4: {
420-
value: 'anthropic/claude-opus-4',
421-
desc: 'AIML (Claude Opus 4)',
422-
},
423-
aiml_anthropic_claude_opus_4_1: {
424-
value: 'anthropic/claude-opus-4-1',
425-
desc: 'AIML (Claude Opus 4.1)',
426-
},
427-
aiml_anthropic_claude_sonnet_4: {
428-
value: 'anthropic/claude-sonnet-4',
429-
desc: 'AIML (Claude Sonnet 4)',
430-
},
431-
aiml_anthropic_claude_sonnet_4_5: {
432-
value: 'anthropic/claude-sonnet-4-5',
433-
desc: 'AIML (Claude Sonnet 4.5)',
434-
},
435406
aiml_claude_3_7_sonnet_20250219: {
436407
value: 'claude-3-7-sonnet-20250219',
437408
desc: 'AIML (Claude 3.7 Sonnet)',
438409
},
439-
aiml_google_gemini_2_5_pro_preview_05_06: {
440-
value: 'google/gemini-2.5-pro-preview-05-06',
441-
desc: 'AIML (Gemini 2.5 Pro)',
442-
},
443-
aiml_google_gemini_2_5_flash_preview: {
444-
value: 'google/gemini-2.5-flash-preview',
445-
desc: 'AIML (Gemini 2.5 Flash)',
446-
},
447410
aiml_openai_o3_2025_04_16: {
448411
value: 'openai/o3-2025-04-16',
449412
desc: 'AIML (GPT-o3)',
@@ -452,10 +415,6 @@ export const Models = {
452415
value: 'openai/gpt-4.1-2025-04-14',
453416
desc: 'AIML (GPT-4.1)',
454417
},
455-
aiml_deepseek_deepseek_chat: {
456-
value: 'deepseek/deepseek-chat',
457-
desc: 'AIML (DeepSeek Chat)',
458-
},
459418
aiml_moonshot_kimi_k2_preview: {
460419
value: 'moonshot/kimi-k2-preview',
461420
desc: 'AIML (Kimi K2)',
@@ -653,6 +612,167 @@ export const defaultConfig = {
653612
],
654613
}
655614

615+
const legacyModelNameMigrations = {
616+
chatgptFree4o: 'chatgptFree4oMini',
617+
chatgptApi4oLatest: 'chatgptApi4o_128k',
618+
chatgptApi4_32k: 'chatgptApi4_128k',
619+
chatgptApi4_32k_0613: 'chatgptApi4_8k_0613',
620+
gptApiDavinci: 'gptApiInstruct',
621+
claude12Api: 'claude3HaikuApi',
622+
claude2Api: 'claude3HaikuApi',
623+
claude21Api: 'claude3HaikuApi',
624+
claude3SonnetApi: 'claude37SonnetApi',
625+
claude3OpusApi: 'claudeOpus4Api',
626+
claude35SonnetApi: 'claude37SonnetApi',
627+
ollama: 'ollamaModel',
628+
openRouter_deepseek_deepseek_chat_v3_0324_free: 'openRouter_openai_gpt_4_1_mini',
629+
aiml_anthropic_claude_opus_4: 'aiml_claude_3_7_sonnet_20250219',
630+
aiml_anthropic_claude_opus_4_1: 'aiml_claude_3_7_sonnet_20250219',
631+
aiml_anthropic_claude_sonnet_4: 'aiml_claude_3_7_sonnet_20250219',
632+
aiml_anthropic_claude_sonnet_4_5: 'aiml_claude_3_7_sonnet_20250219',
633+
aiml_google_gemini_2_5_pro_preview_05_06: 'aiml_openai_gpt_4_1_2025_04_14',
634+
aiml_google_gemini_2_5_flash_preview: 'aiml_openai_gpt_4_1_2025_04_14',
635+
aiml_deepseek_deepseek_chat: 'aiml_openai_gpt_4_1_2025_04_14',
636+
}
637+
638+
function migrateLegacyModelName(modelName) {
639+
if (typeof modelName !== 'string' || !modelName) return modelName
640+
return legacyModelNameMigrations[modelName] || modelName
641+
}
642+
643+
function normalizeModelName(modelName, fallbackModelName = defaultConfig.modelName) {
644+
let migratedModelName = migrateLegacyModelName(modelName)
645+
if (migratedModelName === modelName && typeof modelName === 'string' && modelName.includes('-')) {
646+
const firstSeparatorIndex = modelName.indexOf('-')
647+
const presetPart = modelName.slice(0, firstSeparatorIndex)
648+
const customPart = modelName.slice(firstSeparatorIndex + 1)
649+
const migratedPresetPart = migrateLegacyModelName(presetPart)
650+
if (migratedPresetPart !== presetPart) migratedModelName = `${migratedPresetPart}-${customPart}`
651+
}
652+
if (typeof migratedModelName !== 'string' || !migratedModelName) return fallbackModelName
653+
654+
if (migratedModelName in Models) return migratedModelName
655+
656+
// Keep valid custom model names such as `customModel-xxx`.
657+
if (migratedModelName.includes('-')) {
658+
const presetPart = migratedModelName.split('-')[0]
659+
if (presetPart in Models || presetPart in ModelGroups) return migratedModelName
660+
}
661+
662+
return fallbackModelName
663+
}
664+
665+
function normalizeApiMode(apiMode) {
666+
if (!apiMode || typeof apiMode !== 'object') return null
667+
668+
const normalizedApiMode = { ...apiMode }
669+
if (typeof normalizedApiMode.itemName === 'string')
670+
normalizedApiMode.itemName = migrateLegacyModelName(normalizedApiMode.itemName)
671+
672+
if (AlwaysCustomGroups.includes(normalizedApiMode.groupName))
673+
return {
674+
...normalizedApiMode,
675+
customName:
676+
typeof normalizedApiMode.customName === 'string' ? normalizedApiMode.customName : '',
677+
}
678+
679+
if (typeof normalizedApiMode.groupName !== 'string') return null
680+
const modelGroup = ModelGroups[normalizedApiMode.groupName]
681+
if (!modelGroup) return null
682+
683+
if (normalizedApiMode.itemName === 'custom') {
684+
return {
685+
...normalizedApiMode,
686+
isCustom: true,
687+
customName:
688+
typeof normalizedApiMode.customName === 'string' ? normalizedApiMode.customName : '',
689+
}
690+
}
691+
692+
if (
693+
typeof normalizedApiMode.itemName === 'string' &&
694+
modelGroup.value.includes(normalizedApiMode.itemName)
695+
)
696+
return normalizedApiMode
697+
698+
const fallbackModelName = modelGroup.value[0]
699+
if (!fallbackModelName) return null
700+
701+
return {
702+
...normalizedApiMode,
703+
itemName: fallbackModelName,
704+
isCustom: false,
705+
customName: '',
706+
}
707+
}
708+
709+
function normalizeActiveApiModes(activeApiModes) {
710+
if (!Array.isArray(activeApiModes)) return defaultConfig.activeApiModes
711+
if (activeApiModes.length === 0) return []
712+
713+
const normalized = activeApiModes
714+
.map((modelName) => normalizeModelName(modelName, null))
715+
.filter((modelName) => typeof modelName === 'string' && modelName.length > 0)
716+
717+
if (normalized.length === 0) return defaultConfig.activeApiModes
718+
return [...new Set(normalized)]
719+
}
720+
721+
function normalizeCustomApiModes(customApiModes) {
722+
if (!Array.isArray(customApiModes)) return defaultConfig.customApiModes
723+
724+
return customApiModes.map((apiMode) => normalizeApiMode(apiMode)).filter((apiMode) => apiMode)
725+
}
726+
727+
function normalizeModelAndApiMode(
728+
{ modelName, apiMode },
729+
fallbackModelName = defaultConfig.modelName,
730+
) {
731+
const normalizedApiMode = normalizeApiMode(apiMode)
732+
let normalizedModelName = normalizeModelName(modelName, fallbackModelName)
733+
734+
if (!normalizedApiMode && apiMode?.groupName && apiMode.groupName in ModelGroups) {
735+
normalizedModelName = ModelGroups[apiMode.groupName].value[0]
736+
}
737+
738+
return {
739+
modelName: normalizedModelName,
740+
apiMode: normalizedApiMode,
741+
}
742+
}
743+
744+
function sortObjectKeys(input) {
745+
if (!input || typeof input !== 'object') return input
746+
return Object.keys(input)
747+
.sort()
748+
.reduce((acc, key) => {
749+
acc[key] = input[key]
750+
return acc
751+
}, {})
752+
}
753+
754+
function isApiModeEqual(a, b) {
755+
return JSON.stringify(sortObjectKeys(a)) === JSON.stringify(sortObjectKeys(b))
756+
}
757+
758+
export function normalizeSessionModelSelection(session, fallbackConfig = defaultConfig) {
759+
const sessionToNormalize = session && typeof session === 'object' ? session : {}
760+
const fallbackModelName = normalizeModelName(fallbackConfig?.modelName, defaultConfig.modelName)
761+
const normalizedSelection = normalizeModelAndApiMode(sessionToNormalize, fallbackModelName)
762+
const normalizedSession = {
763+
...sessionToNormalize,
764+
...normalizedSelection,
765+
}
766+
const changed =
767+
sessionToNormalize.modelName !== normalizedSession.modelName ||
768+
!isApiModeEqual(sessionToNormalize.apiMode, normalizedSession.apiMode)
769+
770+
return {
771+
normalizedSession,
772+
changed,
773+
}
774+
}
775+
656776
export function getNavigatorLanguage() {
657777
const l = navigator.language.toLowerCase()
658778
if (['zh-hk', 'zh-mo', 'zh-tw', 'zh-cht', 'zh-hant'].includes(l)) return 'zhHant'
@@ -756,7 +876,16 @@ export async function getUserConfig() {
756876
const options = await Browser.storage.local.get(Object.keys(defaultConfig))
757877
if (options.customChatGptWebApiUrl === 'https://chat.openai.com')
758878
options.customChatGptWebApiUrl = 'https://chatgpt.com'
759-
return defaults(options, defaultConfig)
879+
const normalizedSelection = normalizeModelAndApiMode(options)
880+
881+
const normalizedOptions = {
882+
...options,
883+
...normalizedSelection,
884+
activeApiModes: normalizeActiveApiModes(options.activeApiModes),
885+
customApiModes: normalizeCustomApiModes(options.customApiModes),
886+
}
887+
888+
return defaults(normalizedOptions, defaultConfig)
760889
}
761890

762891
/**

src/services/local-session.mjs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import Browser from 'webextension-polyfill'
22
import { initSession } from './init-session.mjs'
3-
import { getUserConfig } from '../config/index.mjs'
3+
import { getUserConfig, normalizeSessionModelSelection } from '../config/index.mjs'
44

55
export const initDefaultSession = async () => {
66
const config = await getUserConfig()
@@ -69,6 +69,19 @@ export const resetSessions = async () => {
6969

7070
export const getSessions = async () => {
7171
const { sessions } = await Browser.storage.local.get('sessions')
72-
if (sessions && sessions.length > 0) return sessions
72+
if (sessions && sessions.length > 0) {
73+
const config = await getUserConfig()
74+
let changed = false
75+
const normalizedSessions = sessions.map((session) => {
76+
const { normalizedSession, changed: sessionChanged } = normalizeSessionModelSelection(
77+
session,
78+
config,
79+
)
80+
if (sessionChanged) changed = true
81+
return normalizedSession
82+
})
83+
if (changed) await Browser.storage.local.set({ sessions: normalizedSessions })
84+
return normalizedSessions
85+
}
7386
return await resetSessions()
7487
}

0 commit comments

Comments
 (0)