Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions crates/agent-gui/test/models/model-catalog.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,27 @@ test("normalizeModelIdCandidates yields the decorated-id chain in order without
assert.deepEqual(catalog.normalizeModelIdCandidates("grok-4.5"), ["grok-4.5"]);
});

test("normalizeModelIdCandidates strips a month-day date suffix only when no full date is present", () => {
// 中转站给官方模型加四位月日后缀(deepseek-ai/deepseek-v4-pro-0813)。
assert.deepEqual(catalog.normalizeModelIdCandidates("deepseek-ai/deepseek-v4-pro-0813"), [
"deepseek-ai/deepseek-v4-pro-0813",
"deepseek-ai/deepseek-v4-pro",
"deepseek-v4-pro-0813",
"deepseek-v4-pro",
]);
// 年份、型号数字不是月日,不剥。
assert.deepEqual(catalog.normalizeModelIdCandidates("gpt-4o-2024"), ["gpt-4o-2024"]);
assert.deepEqual(catalog.normalizeModelIdCandidates("model-1345"), ["model-1345"]);
});

test("a dated relay id resolves to the catalog entry, an exact dated id keeps its own entry", () => {
const relayed = catalog.findCatalogModelAcrossProviders("deepseek-ai/deepseek-v4-pro-0813");
assert.equal(relayed?.id, "deepseek-v4-pro");
assert.equal(relayed?.contextWindow, 1000000);
assert.equal(catalog.findCatalogModelAcrossProviders("deepseek-r1-0528")?.id, "deepseek-r1-0528");
assert.equal(catalog.findCatalogModelAcrossProviders("deepseek-r1-0601")?.id, "deepseek-r1");
});

test("findCatalogModel resolves exact and decorated ids across providers", () => {
assert.equal(catalog.findCatalogModel("xai", "grok-4.5")?.id, "grok-4.5");
// 候选链对全部供应商生效:大小写、[1m]、日期后缀、@版本。
Expand Down
14 changes: 12 additions & 2 deletions crates/agent-ui/src/lib/models/modelCatalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,18 +79,28 @@ export function normalizeModelIdCandidates(modelId: string): string[] {
push(withoutAtVersion);
const withoutContextSuffix = withoutAtVersion.replace(/\[1m\]$/i, "");
push(withoutContextSuffix);
push(withoutContextSuffix.replace(/-20\d{6}$/, ""));
for (const candidate of withoutDateSuffix(withoutContextSuffix)) push(candidate);
// 中转聚合商常在模型 id 前加自家路径前缀(bailian/deepseek-v4-pro、
// openrouter/xxx 等),目录里存的是裸 id。放链尾——所有精确形态、
// 全部分区都查空后才尝试剥前缀,避免裸段误撞目录里无关的同名模型。
const lastSegment = withoutContextSuffix.split("/").pop() ?? "";
if (lastSegment !== withoutContextSuffix) {
push(lastSegment);
push(lastSegment.replace(/-20\d{6}$/, ""));
for (const candidate of withoutDateSuffix(lastSegment)) push(candidate);
}
return candidates;
}

// 日期后缀有两种写法:完整的 -20260115 和中转站常用的四位月日 -0813 /
// -0731(deepseek-ai/deepseek-v4-pro-0813)。四位形态只在没有完整日期
// 时才剥,且只认合法的月日,避免把 -2024 之类的年份或型号数字误当日期。
function withoutDateSuffix(modelId: string): string[] {
const withoutFullDate = modelId.replace(/-20\d{6}$/, "");
if (withoutFullDate !== modelId) return [withoutFullDate];
const withoutMonthDay = modelId.replace(/-(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])$/, "");
return withoutMonthDay !== modelId ? [withoutMonthDay] : [];
}

const catalogIndexByProvider = new Map<CatalogProviderId, Map<string, CatalogModelEntry>>();

function getCatalogIndex(catalogProvider: CatalogProviderId): Map<string, CatalogModelEntry> {
Expand Down
Loading