fix: address gemini native and route coverage regressions

This commit is contained in:
ciat-777
2026-03-15 21:47:00 +08:00
parent a64a51aa0c
commit 4b42751953
15 changed files with 1006 additions and 74 deletions
@@ -154,4 +154,31 @@ describe('TokenRouter patterns and model mapping', () => {
expect(decision.actualModel).toBe('claude-opus-4-5');
expect(exposedModels).toContain('claude-opus-4-6');
});
it('prefers an exact route over a colliding group display-name alias', async () => {
await createRouteWithSingleChannel(
're:^claude-(opus|sonnet)-4-5$',
undefined,
{
displayName: 'claude-opus-4-6',
sourceModel: 'claude-opus-4-5',
},
);
const exact = await createRouteWithSingleChannel(
'claude-opus-4-6',
undefined,
{
sourceModel: 'claude-opus-4-6',
},
);
const router = new TokenRouter();
const selected = await router.selectChannel('claude-opus-4-6');
const decision = await router.explainSelection('claude-opus-4-6');
expect(selected).toBeTruthy();
expect(selected?.channel.id).toBe(exact.channel.id);
expect(selected?.actualModel).toBe('claude-opus-4-6');
expect(decision.actualModel).toBe('claude-opus-4-6');
});
});
+20 -11
View File
@@ -257,9 +257,9 @@ export function parseRegexModelPattern(pattern: string): RegExp | null {
}
}
export function matchesModelPattern(model: string, pattern: string): boolean {
const normalizedPattern = (pattern || '').trim();
if (!normalizedPattern) return false;
export function matchesModelPattern(model: string, pattern: string): boolean {
const normalizedPattern = (pattern || '').trim();
if (!normalizedPattern) return false;
if (normalizedPattern === model) return true;
@@ -268,8 +268,15 @@ export function matchesModelPattern(model: string, pattern: string): boolean {
return !!re && re.test(model);
}
return minimatch(model, normalizedPattern);
}
return minimatch(model, normalizedPattern);
}
function isExactRouteModelPattern(pattern: string): boolean {
const normalizedPattern = (pattern || '').trim();
if (!normalizedPattern) return false;
if (isRegexModelPattern(normalizedPattern)) return false;
return !/[\*\?\[]/.test(normalizedPattern);
}
function normalizeRouteDisplayName(displayName: string | null | undefined): string {
return (displayName || '').trim();
@@ -993,12 +1000,14 @@ export class TokenRouter {
routes = routes.filter((route) => allowSet.has(route.id));
}
// Find matching route by model pattern or display alias.
const matchedRoute = routes.find((r) => {
return matchesRouteRequestModel(model, r);
});
if (!matchedRoute) return null;
const matchedRoute = routes.find((route) => (
isExactRouteModelPattern(route.modelPattern)
&& (route.modelPattern || '').trim() === model
))
|| routes.find((route) => isRouteDisplayNameMatch(model, route.displayName))
|| routes.find((route) => matchesModelPattern(model, route.modelPattern));
if (!matchedRoute) return null;
return await this.loadRouteMatch(matchedRoute);
}