mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-11-02 08:31:35 +00:00
fix(account): preserve combined model restrictions
This commit is contained in:
1 parent
18790386a7
commit
827764d7bd
4 files changed
+125
-93
No files matched your search
@@ -4,7 +4,7 @@ vi.mock('@/api/admin/accounts', () => ({
|
||||
getAntigravityDefaultModelMapping: vi.fn()
|
||||
}))
|
||||
|
||||
import { buildModelMappingObject, getModelsByPlatform } from '../useModelWhitelist'
|
||||
import { buildModelMappingObject, getModelsByPlatform, splitModelMappingObject } from '../useModelWhitelist'
|
||||
|
||||
describe('useModelWhitelist', () => {
|
||||
it('openai 模型列表包含 GPT-5.4 官方快照', () => {
|
||||
@@ -73,4 +73,34 @@ describe('useModelWhitelist', () => {
|
||||
'gpt-5.4-mini': 'gpt-5.4-mini'
|
||||
})
|
||||
})
|
||||
|
||||
it('combined 模式会同时保留白名单身份映射和模型映射', () => {
|
||||
const mapping = buildModelMappingObject(
|
||||
'combined',
|
||||
['gpt-5.4', 'claude-*'],
|
||||
[
|
||||
{ from: 'gpt-latest', to: 'gpt-5.4' },
|
||||
{ from: 'gpt-5.4', to: 'gpt-5.4-mini' }
|
||||
]
|
||||
)
|
||||
|
||||
expect(mapping).toEqual({
|
||||
'gpt-5.4': 'gpt-5.4-mini',
|
||||
'gpt-latest': 'gpt-5.4'
|
||||
})
|
||||
})
|
||||
|
||||
it('splitModelMappingObject 会把身份映射还原成白名单,其余保留为映射', () => {
|
||||
const parsed = splitModelMappingObject({
|
||||
'gpt-5.4': 'gpt-5.4',
|
||||
'gpt-latest': 'gpt-5.4',
|
||||
' ': 'gpt-empty',
|
||||
broken: 123
|
||||
})
|
||||
|
||||
expect(parsed).toEqual({
|
||||
allowedModels: ['gpt-5.4'],
|
||||
modelMappings: [{ from: 'gpt-latest', to: 'gpt-5.4' }]
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -395,23 +395,60 @@ export function isValidWildcardPattern(pattern: string): boolean {
|
||||
return starIndex === pattern.length - 1 && pattern.lastIndexOf('*') === starIndex
|
||||
}
|
||||
|
||||
export type ModelRestrictionMode = 'whitelist' | 'mapping' | 'combined'
|
||||
|
||||
export interface ModelMappingEntry {
|
||||
from: string
|
||||
to: string
|
||||
}
|
||||
|
||||
export function splitModelMappingObject(
|
||||
modelMapping?: Record<string, unknown> | null
|
||||
): { allowedModels: string[]; modelMappings: ModelMappingEntry[] } {
|
||||
const allowedModels: string[] = []
|
||||
const modelMappings: ModelMappingEntry[] = []
|
||||
|
||||
if (!modelMapping || typeof modelMapping !== 'object') {
|
||||
return { allowedModels, modelMappings }
|
||||
}
|
||||
|
||||
for (const [rawFrom, rawTo] of Object.entries(modelMapping)) {
|
||||
if (typeof rawTo !== 'string') continue
|
||||
const from = rawFrom.trim()
|
||||
const to = rawTo.trim()
|
||||
if (!from || !to) continue
|
||||
|
||||
if (from === to) {
|
||||
allowedModels.push(from)
|
||||
} else {
|
||||
modelMappings.push({ from, to })
|
||||
}
|
||||
}
|
||||
|
||||
return { allowedModels, modelMappings }
|
||||
}
|
||||
|
||||
export function buildModelMappingObject(
|
||||
mode: 'whitelist' | 'mapping',
|
||||
mode: ModelRestrictionMode,
|
||||
allowedModels: string[],
|
||||
modelMappings: { from: string; to: string }[]
|
||||
modelMappings: ModelMappingEntry[]
|
||||
): Record<string, string> | null {
|
||||
const mapping: Record<string, string> = {}
|
||||
|
||||
if (mode === 'whitelist') {
|
||||
if (mode === 'whitelist' || mode === 'combined') {
|
||||
for (const model of allowedModels) {
|
||||
const normalizedModel = model.trim()
|
||||
if (!normalizedModel) continue
|
||||
// whitelist 模式的本意是"精确模型列表",如果用户输入了通配符(如 claude-*),
|
||||
// 写入 model_mapping 会导致 GetMappedModel() 把真实模型映射成 "claude-*",从而转发失败。
|
||||
// 因此这里跳过包含通配符的条目。
|
||||
if (!model.includes('*')) {
|
||||
mapping[model] = model
|
||||
if (!normalizedModel.includes('*')) {
|
||||
mapping[normalizedModel] = normalizedModel
|
||||
}
|
||||
}
|
||||
} else {
|
||||
}
|
||||
|
||||
if (mode === 'mapping' || mode === 'combined') {
|
||||
for (const m of modelMappings) {
|
||||
const from = m.from.trim()
|
||||
const to = m.to.trim()
|
||||
|
||||
Reference in new issue
Block a user