From eb7fe25dd28073e22b08b56da2368760c2c34069 Mon Sep 17 00:00:00 2001 From: lzt404 <2596933790@qq.com> Date: Mon, 3 Aug 2026 23:17:28 +0800 Subject: [PATCH] fix(model-aliases): avoid collisions with existing model names --- src/pages/ThinkingAliasesPage.tsx | 29 ++++++++++++++++++++++++++--- tests/thinkingAliases.test.ts | 11 +++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/pages/ThinkingAliasesPage.tsx b/src/pages/ThinkingAliasesPage.tsx index ae4a28f..33f1828 100644 --- a/src/pages/ThinkingAliasesPage.tsx +++ b/src/pages/ThinkingAliasesPage.tsx @@ -120,6 +120,28 @@ export const defaultModelAlias = ( return `${normalizedModel}${normalizedEffort ? `-${normalizedEffort}` : ''}${fast ? '-fast' : ''}`; }; +export const uniqueModelAlias = ( + alias: string, + existingModelNames: string[], +) => { + const normalizedAlias = alias.trim(); + if (!normalizedAlias) return ''; + const existingNames = new Set( + existingModelNames + .map((name) => name.trim().toLowerCase()) + .filter(Boolean), + ); + if (!existingNames.has(normalizedAlias.toLowerCase())) return normalizedAlias; + + let suffix = 2; + let candidate = `${normalizedAlias}-${suffix}`; + while (existingNames.has(candidate.toLowerCase())) { + suffix += 1; + candidate = `${normalizedAlias}-${suffix}`; + } + return candidate; +}; + export const thinkingAliasSourceKindLabel = (kind: string) => { if (kind === 'codex-oauth') return 'Codex OAuth'; if (kind === 'codex-api') return 'Codex API'; @@ -295,6 +317,7 @@ export function ThinkingAliasesPage() { const normalizedEffort = effort.trim().toLowerCase(); const defaultAlias = defaultModelAlias(selectedSource?.model, normalizedEffort, fastEnabled); + const uniqueDefaultAlias = uniqueModelAlias(defaultAlias, sources.map((source) => source.model)); const customEffortSelected = Boolean( normalizedEffort && !effortOptions.some((option) => option.value === normalizedEffort), @@ -305,10 +328,10 @@ export function ThinkingAliasesPage() { const currentValue = current.trim(); const canReplace = !currentValue || currentValue === generatedAliasRef.current; if (!canReplace) return current; - generatedAliasRef.current = defaultAlias; - return defaultAlias; + generatedAliasRef.current = uniqueDefaultAlias; + return uniqueDefaultAlias; }); - }, [defaultAlias]); + }, [uniqueDefaultAlias]); const createAlias = async () => { if (!selectedSource) { diff --git a/tests/thinkingAliases.test.ts b/tests/thinkingAliases.test.ts index ca1f88b..d2da751 100644 --- a/tests/thinkingAliases.test.ts +++ b/tests/thinkingAliases.test.ts @@ -4,6 +4,7 @@ import { combineModelAliasSources, defaultModelAlias, thinkingAliasSourceKindLabel, + uniqueModelAlias, } from '../src/pages/ThinkingAliasesPage'; describe('模型别名默认名称', () => { @@ -13,6 +14,16 @@ describe('模型别名默认名称', () => { expect(defaultModelAlias('gpt-5.6-sol', 'xhigh', true)).toBe('gpt-5.6-sol-xhigh-fast'); expect(defaultModelAlias('gpt-5.6-sol', '', false)).toBe(''); }); + + test('默认别名与现有模型重名时递增数字后缀', () => { + expect(uniqueModelAlias('gpt-5.6-sol-fast', ['gpt-5.6-sol-fast'])).toBe('gpt-5.6-sol-fast-2'); + expect(uniqueModelAlias('gpt-5.6-sol-fast', [ + 'gpt-5.6-sol-fast', + 'gpt-5.6-sol-fast-2', + ])).toBe('gpt-5.6-sol-fast-3'); + expect(uniqueModelAlias('gpt-5.6-sol-fast', ['GPT-5.6-SOL-FAST'])).toBe('gpt-5.6-sol-fast-2'); + expect(uniqueModelAlias('gpt-5.6-sol-fast', ['gpt-5.6-sol'])).toBe('gpt-5.6-sol-fast'); + }); }); describe('思考别名', () => {