@@ -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]
119118export 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+
656776export 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/**
0 commit comments