Merge pull request #207 from cita-777/codex/issue-200-group-route-alias

[codex] fix explicit-group source-model fallback
This commit is contained in:
Cita
2026-03-20 16:55:28 +08:00
committed by GitHub
2 changed files with 61 additions and 15 deletions
@@ -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');
});
});
+20 -10
View File
@@ -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) => (
routeIds.includes(item.id)
&& !isExplicitGroupRoute(item)
&& isExactRouteModelPattern(item.modelPattern)
))
.map((item) => item.id)
: routeIds;
const enabledSourceRoutes = isExplicitGroupRoute(route)
? enabledRoutes.filter((item) => (
routeIds.includes(item.id)
&& !isExplicitGroupRoute(item)
&& isExactRouteModelPattern(item.modelPattern)
))
: 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,