@@ -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,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+
656764export 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/**
0 commit comments