forked from boos/BoosAPI
fix explicit-group source-model fallback
This commit is contained in:
@@ -93,6 +93,27 @@ describe('TokenRouter patterns and model mapping', () => {
|
||||
return { route, channel };
|
||||
}
|
||||
|
||||
async function createExplicitGroupRoute(
|
||||
displayName: string,
|
||||
sourceRouteIds: number[],
|
||||
) {
|
||||
const route = await db.insert(schema.tokenRoutes).values({
|
||||
modelPattern: displayName,
|
||||
displayName,
|
||||
routeMode: 'explicit_group',
|
||||
enabled: true,
|
||||
}).returning().get();
|
||||
|
||||
await db.insert(schema.routeGroupSources).values(
|
||||
sourceRouteIds.map((sourceRouteId) => ({
|
||||
groupRouteId: route.id,
|
||||
sourceRouteId,
|
||||
})),
|
||||
).run();
|
||||
|
||||
return route;
|
||||
}
|
||||
|
||||
it('matches routes with re: regex patterns', async () => {
|
||||
await createRouteWithSingleChannel('re:^claude-(opus|sonnet)-4-6$');
|
||||
const router = new TokenRouter();
|
||||
@@ -181,4 +202,19 @@ describe('TokenRouter patterns and model mapping', () => {
|
||||
expect(selected?.actualModel).toBe('claude-opus-4-6');
|
||||
expect(decision.actualModel).toBe('claude-opus-4-6');
|
||||
});
|
||||
|
||||
it('falls back to the source exact-route model when explicit-group channels omit sourceModel', async () => {
|
||||
const source = await createRouteWithSingleChannel('claude-opus-4-5');
|
||||
await createExplicitGroupRoute('claude-test-4.6-sonnet', [source.route.id]);
|
||||
const router = new TokenRouter();
|
||||
|
||||
const selected = await router.selectChannel('claude-test-4.6-sonnet');
|
||||
const decision = await router.explainSelection('claude-test-4.6-sonnet');
|
||||
|
||||
expect(selected).toBeTruthy();
|
||||
expect(selected?.actualModel).toBe('claude-opus-4-5');
|
||||
expect(decision.actualModel).toBe('claude-opus-4-5');
|
||||
expect(decision.summary).toContain('按显示名命中:claude-test-4.6-sonnet');
|
||||
expect(decision.summary).toContain('实际转发模型:claude-opus-4-5');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -748,21 +748,26 @@ async function loadRouteMatch(route: RouteRow, nowMs = Date.now()): Promise<Rout
|
||||
return cached.match;
|
||||
}
|
||||
|
||||
const enabledRoutes = await loadEnabledRoutes(nowMs);
|
||||
const routeIds = (() => {
|
||||
if (!isExplicitGroupRoute(route)) {
|
||||
return [route.id];
|
||||
}
|
||||
return Array.from(new Set(route.sourceRouteIds.filter((routeId) => Number.isFinite(routeId) && routeId > 0)));
|
||||
})();
|
||||
const enabledSourceRouteIds = isExplicitGroupRoute(route)
|
||||
? (await loadEnabledRoutes(nowMs))
|
||||
.filter((item) => (
|
||||
const enabledSourceRoutes = isExplicitGroupRoute(route)
|
||||
? enabledRoutes.filter((item) => (
|
||||
routeIds.includes(item.id)
|
||||
&& !isExplicitGroupRoute(item)
|
||||
&& isExactRouteModelPattern(item.modelPattern)
|
||||
))
|
||||
.map((item) => item.id)
|
||||
: routeIds;
|
||||
: enabledRoutes.filter((item) => routeIds.includes(item.id));
|
||||
const enabledSourceRouteIds = enabledSourceRoutes.map((item) => item.id);
|
||||
const fallbackSourceModelByRouteId = new Map<number, string>(
|
||||
enabledSourceRoutes
|
||||
.filter((item) => isExactRouteModelPattern(item.modelPattern))
|
||||
.map((item) => [item.id, (item.modelPattern || '').trim()]),
|
||||
);
|
||||
const channels = enabledSourceRouteIds.length > 0
|
||||
? await db
|
||||
.select()
|
||||
@@ -775,7 +780,12 @@ async function loadRouteMatch(route: RouteRow, nowMs = Date.now()): Promise<Rout
|
||||
: [];
|
||||
|
||||
const mapped = channels.map((row) => ({
|
||||
channel: row.route_channels,
|
||||
channel: {
|
||||
...row.route_channels,
|
||||
sourceModel: normalizeChannelSourceModel(row.route_channels.sourceModel)
|
||||
|| fallbackSourceModelByRouteId.get(row.route_channels.routeId)
|
||||
|| null,
|
||||
},
|
||||
account: row.accounts,
|
||||
site: row.sites,
|
||||
token: row.account_tokens,
|
||||
|
||||
Reference in New Issue
Block a user