Skip to content

Commit c8de5f4

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 c8de5f4

2 files changed

Lines changed: 174 additions & 44 deletions

File tree

src/config/index.mjs

Lines changed: 159 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,155 @@ 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+
const migratedModelName = migrateLegacyModelName(modelName)
645+
if (typeof migratedModelName !== 'string' || !migratedModelName) return fallbackModelName
646+
647+
if (migratedModelName in Models) return migratedModelName
648+
649+
// Keep valid custom model names such as `customModel-xxx`.
650+
if (migratedModelName.includes('-')) {
651+
const presetPart = migratedModelName.split('-')[0]
652+
if (presetPart in Models || presetPart in ModelGroups) return migratedModelName
653+
}
654+
655+
return fallbackModelName
656+
}
657+
658+
function normalizeApiMode(apiMode) {
659+
if (!apiMode || typeof apiMode !== 'object') return null
660+
661+
const normalizedApiMode = { ...apiMode }
662+
if (typeof normalizedApiMode.itemName === 'string')
663+
normalizedApiMode.itemName = migrateLegacyModelName(normalizedApiMode.itemName)
664+
665+
if (AlwaysCustomGroups.includes(normalizedApiMode.groupName)) return normalizedApiMode
666+
667+
if (typeof normalizedApiMode.groupName !== 'string') return null
668+
const modelGroup = ModelGroups[normalizedApiMode.groupName]
669+
if (!modelGroup) return null
670+
671+
if (normalizedApiMode.itemName === 'custom') {
672+
return {
673+
...normalizedApiMode,
674+
isCustom: true,
675+
customName:
676+
typeof normalizedApiMode.customName === 'string' ? normalizedApiMode.customName : '',
677+
}
678+
}
679+
680+
if (
681+
typeof normalizedApiMode.itemName === 'string' &&
682+
modelGroup.value.includes(normalizedApiMode.itemName)
683+
)
684+
return normalizedApiMode
685+
686+
const fallbackModelName = modelGroup.value[0]
687+
if (!fallbackModelName) return null
688+
689+
return {
690+
...normalizedApiMode,
691+
itemName: fallbackModelName,
692+
isCustom: false,
693+
customName: '',
694+
}
695+
}
696+
697+
function normalizeActiveApiModes(activeApiModes) {
698+
if (!Array.isArray(activeApiModes)) return defaultConfig.activeApiModes
699+
if (activeApiModes.length === 0) return []
700+
701+
const normalized = activeApiModes
702+
.map((modelName) => normalizeModelName(modelName, null))
703+
.filter((modelName) => typeof modelName === 'string' && modelName.length > 0)
704+
705+
if (normalized.length === 0) return defaultConfig.activeApiModes
706+
return [...new Set(normalized)]
707+
}
708+
709+
function normalizeCustomApiModes(customApiModes) {
710+
if (!Array.isArray(customApiModes)) return defaultConfig.customApiModes
711+
712+
return customApiModes.map((apiMode) => normalizeApiMode(apiMode)).filter((apiMode) => apiMode)
713+
}
714+
715+
function normalizeModelAndApiMode(
716+
{ modelName, apiMode },
717+
fallbackModelName = defaultConfig.modelName,
718+
) {
719+
const normalizedApiMode = normalizeApiMode(apiMode)
720+
let normalizedModelName = normalizeModelName(modelName, fallbackModelName)
721+
722+
if (!normalizedApiMode && apiMode?.groupName && apiMode.groupName in ModelGroups) {
723+
normalizedModelName = ModelGroups[apiMode.groupName].value[0]
724+
}
725+
726+
return {
727+
modelName: normalizedModelName,
728+
apiMode: normalizedApiMode,
729+
}
730+
}
731+
732+
function sortObjectKeys(input) {
733+
if (!input || typeof input !== 'object') return input
734+
return Object.keys(input)
735+
.sort()
736+
.reduce((acc, key) => {
737+
acc[key] = input[key]
738+
return acc
739+
}, {})
740+
}
741+
742+
function isApiModeEqual(a, b) {
743+
return JSON.stringify(sortObjectKeys(a)) === JSON.stringify(sortObjectKeys(b))
744+
}
745+
746+
export function normalizeSessionModelSelection(session, fallbackConfig = defaultConfig) {
747+
const sessionToNormalize = session && typeof session === 'object' ? session : {}
748+
const fallbackModelName = normalizeModelName(fallbackConfig?.modelName, defaultConfig.modelName)
749+
const normalizedSelection = normalizeModelAndApiMode(sessionToNormalize, fallbackModelName)
750+
const normalizedSession = {
751+
...sessionToNormalize,
752+
...normalizedSelection,
753+
}
754+
const changed =
755+
sessionToNormalize.modelName !== normalizedSession.modelName ||
756+
!isApiModeEqual(sessionToNormalize.apiMode, normalizedSession.apiMode)
757+
758+
return {
759+
normalizedSession,
760+
changed,
761+
}
762+
}
763+
656764
export function getNavigatorLanguage() {
657765
const l = navigator.language.toLowerCase()
658766
if (['zh-hk', 'zh-mo', 'zh-tw', 'zh-cht', 'zh-hant'].includes(l)) return 'zhHant'
@@ -756,7 +864,16 @@ export async function getUserConfig() {
756864
const options = await Browser.storage.local.get(Object.keys(defaultConfig))
757865
if (options.customChatGptWebApiUrl === 'https://chat.openai.com')
758866
options.customChatGptWebApiUrl = 'https://chatgpt.com'
759-
return defaults(options, defaultConfig)
867+
const normalizedSelection = normalizeModelAndApiMode(options)
868+
869+
const normalizedOptions = {
870+
...options,
871+
...normalizedSelection,
872+
activeApiModes: normalizeActiveApiModes(options.activeApiModes),
873+
customApiModes: normalizeCustomApiModes(options.customApiModes),
874+
}
875+
876+
return defaults(normalizedOptions, defaultConfig)
760877
}
761878

762879
/**

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)