diff --git a/packages/app/src/lib/compare-slug.test.ts b/packages/app/src/lib/compare-slug.test.ts index fa612d90..f85aac9e 100644 --- a/packages/app/src/lib/compare-slug.test.ts +++ b/packages/app/src/lib/compare-slug.test.ts @@ -16,6 +16,7 @@ import { const DEEPSEEK_R1 = COMPARE_MODEL_SLUGS.find((m) => m.slug === 'deepseek-r1')!; const KIMI_K26 = COMPARE_MODEL_SLUGS.find((m) => m.slug === 'kimi-k26')!; const GLM_51 = COMPARE_MODEL_SLUGS.find((m) => m.slug === 'glm-5-1')!; +const GLM_52 = COMPARE_MODEL_SLUGS.find((m) => m.slug === 'glm-5-2')!; describe('parseCompareSlug — new model-prefixed form', () => { it('parses a canonical model-prefixed slug', () => { @@ -252,7 +253,8 @@ describe('compareModelDisplayLabel', () => { expect(compareModelDisplayLabel(KIMI_K26, 'gb200', 'mi355x')).toBe( 'Kimi K2.5/K2.6/K2.7-Code 1T — GB200 NVL72 vs MI355X', ); - expect(compareModelDisplayLabel(GLM_51, 'h100', 'h200')).toBe('GLM 5/5.1/5.2 — H100 vs H200'); + expect(compareModelDisplayLabel(GLM_51, 'h100', 'h200')).toBe('GLM 5/5.1 — H100 vs H200'); + expect(compareModelDisplayLabel(GLM_52, 'h100', 'h200')).toBe('GLM 5.2 — H100 vs H200'); }); }); @@ -260,6 +262,8 @@ describe('getCompareModelBySlug', () => { it('returns canonical models for canonical slugs', () => { expect(getCompareModelBySlug('deepseek-r1')).toBe(DEEPSEEK_R1); expect(getCompareModelBySlug('kimi-k26')).toBe(KIMI_K26); + expect(getCompareModelBySlug('glm-5-2')).toBe(GLM_52); + expect(GLM_52.dbKeys).toEqual(['glm5.2']); }); it('resolves alias slugs to their canonical model', () => { diff --git a/packages/app/src/lib/compare-slug.ts b/packages/app/src/lib/compare-slug.ts index 3891b4d3..10e9ed4f 100644 --- a/packages/app/src/lib/compare-slug.ts +++ b/packages/app/src/lib/compare-slug.ts @@ -66,10 +66,15 @@ export const COMPARE_MODEL_SLUGS: CompareModelSlug[] = [ { slug: 'glm-5-1', displayName: 'GLM-5', - // GLM-5 point releases share the same base architecture. Keep the existing - // canonical slug for URL stability while querying every DB bucket. - dbKeys: ['glm5.2', 'glm5.1', 'glm5'], - label: 'GLM 5/5.1/5.2', + // GLM-5 and GLM-5.1 remain grouped under the stable canonical slug. + dbKeys: ['glm5.1', 'glm5'], + label: 'GLM 5/5.1', + }, + { + slug: 'glm-5-2', + displayName: 'GLM-5.2', + dbKeys: ['glm5.2'], + label: 'GLM 5.2', }, { slug: 'minimax-m3', diff --git a/packages/app/src/lib/compare-ssr.test.ts b/packages/app/src/lib/compare-ssr.test.ts index 4bf99f89..356fc869 100644 --- a/packages/app/src/lib/compare-ssr.test.ts +++ b/packages/app/src/lib/compare-ssr.test.ts @@ -2,12 +2,18 @@ import { describe, expect, it } from 'vitest'; import type { BenchmarkRow } from '@/lib/api'; -import { computeCompareImageRows } from './compare-ssr'; +import { computeCompareImageRows, KNOWN_MODELS } from './compare-ssr'; // BenchmarkRow.id is required (stable per-point id from benchmark_results); // hand out a fresh one per stub so id-keyed logic can't collide across rows. let nextStubId = 1; +describe('compare URL validators', () => { + it('accepts GLM-5.2 as a model override', () => { + expect(KNOWN_MODELS.has('GLM-5.2')).toBe(true); + }); +}); + function stubRow(overrides: Partial = {}): BenchmarkRow { return { id: nextStubId++, diff --git a/packages/app/src/lib/compare-ssr.ts b/packages/app/src/lib/compare-ssr.ts index 27b85f2b..44e80939 100644 --- a/packages/app/src/lib/compare-ssr.ts +++ b/packages/app/src/lib/compare-ssr.ts @@ -70,6 +70,7 @@ export const KNOWN_MODELS = new Set([ 'MiniMax-M2.5', 'MiniMax-M3', 'GLM-5', + 'GLM-5.2', 'DeepSeek-V4-Pro', ]); export const KNOWN_SEQUENCES = new Set(['1k/1k', '1k/8k', '8k/1k']); diff --git a/packages/app/src/lib/data-mappings.test.ts b/packages/app/src/lib/data-mappings.test.ts index 485d8849..331a7a08 100644 --- a/packages/app/src/lib/data-mappings.test.ts +++ b/packages/app/src/lib/data-mappings.test.ts @@ -57,6 +57,11 @@ describe('getModelAndSequence', () => { expect(result).toEqual({ model: Model.Kimi_K2_5, sequence: Sequence.OneK_OneK }); }); + it('prefers the specific glm5.2 prefix over the glm5 family prefix', () => { + const result = getModelAndSequence('results_glm5.2_1k1k'); + expect(result).toEqual({ model: Model.GLM_5_2, sequence: Sequence.OneK_OneK }); + }); + it('returns undefined for unrecognized model prefix', () => { expect(getModelAndSequence('results_unknown_1k1k')).toBeUndefined(); }); @@ -224,7 +229,8 @@ describe('getModelLabel', () => { expect(getModelLabel(Model.GptOss)).toBe('gpt-oss 120B'); expect(getModelLabel(Model.Qwen3_5)).toBe('Qwen3.5 397B'); expect(getModelLabel(Model.Kimi_K2_5)).toBe('Kimi K2.5/2.6/2.7-Code 1T'); - expect(getModelLabel(Model.GLM_5)).toBe('GLM5/5.1/5.2 744B'); + expect(getModelLabel(Model.GLM_5)).toBe('GLM5/5.1 744B'); + expect(getModelLabel(Model.GLM_5_2)).toBe('GLM5.2'); expect(getModelLabel(Model.MiniMax_M2_5)).toBe('MiniMax M2.5/2.7 230B'); }); diff --git a/packages/app/src/lib/data-mappings.ts b/packages/app/src/lib/data-mappings.ts index 6076d501..4793a8a1 100644 --- a/packages/app/src/lib/data-mappings.ts +++ b/packages/app/src/lib/data-mappings.ts @@ -10,6 +10,7 @@ export enum Model { MiniMax_M2_5 = 'MiniMax-M2.5', MiniMax_M3 = 'MiniMax-M3', GLM_5 = 'GLM-5', + GLM_5_2 = 'GLM-5.2', DeepSeek_V4_Pro = 'DeepSeek-V4-Pro', } @@ -111,7 +112,8 @@ const MODEL_CONFIG: Record = { prefix: 'dsr1', category: 'maintenance', }, - [Model.GLM_5]: { label: 'GLM5/5.1/5.2 744B', prefix: 'glm5', category: 'default' }, + [Model.GLM_5]: { label: 'GLM5/5.1 744B', prefix: 'glm5', category: 'default' }, + [Model.GLM_5_2]: { label: 'GLM5.2', prefix: 'glm5.2', category: 'default' }, [Model.Qwen3_5]: { label: 'Qwen3.5 397B', prefix: 'qwen3.5', category: 'default' }, [Model.GptOss]: { label: 'gpt-oss 120B', prefix: 'gptoss', category: 'deprecated' }, [Model.MiniMax_M2_5]: { @@ -191,6 +193,12 @@ export const MODEL_PREFIX_MAPPING: Record = Object.fromEntries( .map(([m, c]) => [c.prefix, m]), ); +// Specific point-release prefixes must win over family prefixes such as +// `glm5`; precompute once rather than sorting for every artifact. +const MODEL_PREFIXES_LONGEST_FIRST = Object.keys(MODEL_PREFIX_MAPPING).toSorted( + (a, b) => b.length - a.length, +); + // --------------------------------------------------------------------------- // Sequences // --------------------------------------------------------------------------- @@ -356,7 +364,7 @@ export function getModelAndSequence( let model: Model | undefined; let sequence: Sequence | undefined; - for (const key in MODEL_PREFIX_MAPPING) { + for (const key of MODEL_PREFIXES_LONGEST_FIRST) { if (artifactName.includes(key)) { model = MODEL_PREFIX_MAPPING[key]; break; diff --git a/packages/app/src/lib/models-mapping.test.ts b/packages/app/src/lib/models-mapping.test.ts index 7d941375..55bbc936 100644 --- a/packages/app/src/lib/models-mapping.test.ts +++ b/packages/app/src/lib/models-mapping.test.ts @@ -30,10 +30,9 @@ describe('DISPLAY_MODEL_TO_DB', () => { expect(DISPLAY_MODEL_TO_DB['gpt-oss-120b']).toEqual(['gptoss120b']); }); - it('groups point-release DB keys under one display', () => { - expect(DISPLAY_MODEL_TO_DB['GLM-5']).toEqual( - expect.arrayContaining(['glm5', 'glm5.1', 'glm5.2']), - ); + it('keeps GLM-5.2 separate from the GLM-5/5.1 display bucket', () => { + expect(DISPLAY_MODEL_TO_DB['GLM-5']).toEqual(['glm5', 'glm5.1']); + expect(DISPLAY_MODEL_TO_DB['GLM-5.2']).toEqual(['glm5.2']); }); }); diff --git a/packages/constants/src/models.test.ts b/packages/constants/src/models.test.ts index 10766c5e..4f382117 100644 --- a/packages/constants/src/models.test.ts +++ b/packages/constants/src/models.test.ts @@ -15,10 +15,9 @@ describe('DB_MODEL_TO_DISPLAY / DISPLAY_MODEL_TO_DB consistency', () => { expect(totalDbKeys).toBe(Object.keys(DB_MODEL_TO_DISPLAY).length); }); - it('groups point-release DB keys under the same display name', () => { - expect(DISPLAY_MODEL_TO_DB['GLM-5']).toEqual( - expect.arrayContaining(['glm5', 'glm5.1', 'glm5.2']), - ); + it('keeps GLM-5.2 separate from the GLM-5/5.1 display bucket', () => { + expect(DISPLAY_MODEL_TO_DB['GLM-5']).toEqual(['glm5', 'glm5.1']); + expect(DISPLAY_MODEL_TO_DB['GLM-5.2']).toEqual(['glm5.2']); expect(DISPLAY_MODEL_TO_DB['Kimi-K2.5']).toEqual( expect.arrayContaining(['kimik2.5', 'kimik2.6', 'kimik2.7-code']), ); diff --git a/packages/constants/src/models.ts b/packages/constants/src/models.ts index eab03c33..2aeb6f46 100644 --- a/packages/constants/src/models.ts +++ b/packages/constants/src/models.ts @@ -19,7 +19,7 @@ export const DB_MODEL_TO_DISPLAY: Record = { minimaxm3: 'MiniMax-M3', glm5: 'GLM-5', 'glm5.1': 'GLM-5', - 'glm5.2': 'GLM-5', + 'glm5.2': 'GLM-5.2', dsv4: 'DeepSeek-V4-Pro', };