diff --git a/packages/app/src/lib/api-route-catalog.ts b/packages/app/src/lib/api-route-catalog.ts index bba48de0a..7e23a541e 100644 --- a/packages/app/src/lib/api-route-catalog.ts +++ b/packages/app/src/lib/api-route-catalog.ts @@ -404,10 +404,10 @@ export const apiContractSourceDigests = [ }, { source: '../constants/src/models.ts', - // Reviewed for the release-date table move: it adds MODEL_RELEASE_DATES and - // getModelReleaseDate. No published model name, alias, or parameter enum is - // touched, and no endpoint exposes a release date, so the docs stand. - sourceSha256: 'fde7ec056e048452663ddcaa46af137e9ec192676b94c701f57bcc4498417157', + // Reviewed again for the release-date corrections: values inside + // MODEL_RELEASE_DATES only. No published model name, alias, or parameter enum + // is touched, and no endpoint exposes a release date, so the docs stand. + sourceSha256: 'f8f46a341bf57384c0080c2ed75d867801142675ded08225281cf7c102236628', reviewArea: { en: 'Published benchmark and TCO model names, aliases, and parameter enums.', zh: '已发布基准与 TCO 模型名称、别名和参数枚举。', diff --git a/packages/app/src/lib/model-architectures.test.ts b/packages/app/src/lib/model-architectures.test.ts index 430147c73..6cac00991 100644 --- a/packages/app/src/lib/model-architectures.test.ts +++ b/packages/app/src/lib/model-architectures.test.ts @@ -1,7 +1,7 @@ import { MODEL_RELEASE_DATES, getModelReleaseDate } from '@semianalysisai/inferencex-constants'; import { describe, expect, it } from 'vitest'; -import { Model } from '@/lib/data-mappings'; +import { MODEL_OPTIONS, Model } from '@/lib/data-mappings'; import { type SubBlockFlow, @@ -21,7 +21,35 @@ import { MODEL_ARCHITECTURES, } from './model-architectures'; -describe('MODEL_RELEASE_DATES keys', () => { +describe('MODEL_RELEASE_DATES coverage', () => { + it('has a sourced date for every model the user can select', () => { + // Without a date the Fleet Lifecycle axis falls back to the model's first + // benchmark run, which reads as "this is when the model appeared" when it is + // really "this is when we got to it" — for gpt-oss that gap is five weeks. + // A model whose release date genuinely cannot be sourced belongs in an + // explicit allowlist here, so the gap stays visible; do not delete the test. + const missing = MODEL_OPTIONS.filter((model) => !getModelReleaseDate(model)); + expect(missing).toEqual([]); + }); + + it('anchors each model to its weights publication, not its first sweep', () => { + // Pinned so a silent revert fails. Every one of these predates the model's + // first InferenceX sweep, which is the invariant that caught the four wrong + // dates: a model cannot be benchmarked before its weights exist. + expect(getModelReleaseDate(Model.Kimi_K3)).toBe('2026-07-27'); + expect(getModelReleaseDate(Model.GLM_5_2)).toBe('2026-06-13'); + expect(getModelReleaseDate(Model.MiniMax_M3)).toBe('2026-06-07'); + expect(getModelReleaseDate(Model.DeepSeek_V4_Pro)).toBe('2026-04-24'); + expect(getModelReleaseDate(Model.GLM_5)).toBe('2026-02-13'); + expect(getModelReleaseDate(Model.Qwen3_5)).toBe('2026-02-16'); + expect(getModelReleaseDate(Model.MiniMax_M2_5)).toBe('2026-02-12'); + expect(getModelReleaseDate(Model.Kimi_K2_5)).toBe('2026-01-27'); + expect(getModelReleaseDate(Model.GptOss)).toBe('2025-08-05'); + expect(getModelReleaseDate(Model.DeepSeek_R1)).toBe('2025-05-28'); + expect(getModelReleaseDate(Model.Llama3_3_70B)).toBe('2024-12-06'); + expect(getModelReleaseDate(Model.Llama3_1_70B)).toBe('2024-07-23'); + }); + it('names a real model, so no entry is dead weight', () => { // The table is keyed by string because it lives in the constants package, // below the `Model` enum. A key that matches no model never throws — the @@ -72,12 +100,11 @@ describe('MODEL_ARCHITECTURES', () => { it('sources every architecture caption from the one release-date table', () => { // The diagram prints "Released by {developer} on {date}", so an entry with a - // developer and no date silently drops the whole caption. Listing the models - // that have no sourced date keeps that visible instead of invisible. + // developer and no date silently drops the whole caption. const missing = Object.values(MODEL_ARCHITECTURES) .filter((arch) => arch?.developer && !getModelReleaseDate(arch.model)) .map((arch) => arch!.model); - expect(missing).toEqual([Model.MiniMax_M3]); + expect(missing).toEqual([]); }); it('ensures dense models have equal active and total params', () => { diff --git a/packages/constants/src/models.test.ts b/packages/constants/src/models.test.ts index c251996ac..3f5a87221 100644 --- a/packages/constants/src/models.test.ts +++ b/packages/constants/src/models.test.ts @@ -70,8 +70,10 @@ describe('MODEL_RELEASE_DATES', () => { }); it('returns null rather than throwing for a model with no sourced date', () => { - expect(getModelReleaseDate('GLM-5.2')).toBeNull(); + // Every model shipped today has a date, so this documents the fallback path + // that a future model arrives on rather than an existing gap. expect(getModelReleaseDate('not-a-model')).toBeNull(); + expect(getModelReleaseDate('')).toBeNull(); }); }); diff --git a/packages/constants/src/models.ts b/packages/constants/src/models.ts index e3c126ea2..0dd46ea40 100644 --- a/packages/constants/src/models.ts +++ b/packages/constants/src/models.ts @@ -105,14 +105,66 @@ export function rowToSequence(row: { * earliest date they actually have data for. */ export const MODEL_RELEASE_DATES: Record = { + // Newest first. `sweep:` is the model's "Date added" from MODELS.md — the first + // InferenceX benchmark — recorded so the "cannot predate its own weights" + // invariant above is checkable by eye at review time. + // + // Weights published on Hugging Face 2026-07-27 under the Kimi K3 License, + // eleven days after the 2026-07-16 product launch; the gap was to let vLLM, + // NVIDIA and AMD prepare day-zero support. sweep: 2026-07-27 — day zero. + 'Kimi-K3': '2026-07-27', + // Zhipu launched GLM-5.2 on 2026-06-13 via the GLM Coding Plan with MIT + // weights at zai-org/GLM-5.2; standalone API and provider support followed + // over the next few days. One source dates the Hugging Face upload 2026-06-16 + // instead, so this is ±3 days — immaterial on a multi-month axis, but it is an + // announcement-day date rather than a confirmed upload day. sweep: 2026-07-18. + 'GLM-5.2': '2026-06-13', + // Weights and the official MXFP8 quant live on Hugging Face at + // MiniMaxAI/MiniMax-M3 by 2026-06-07, under the custom minimax-community + // license. The 2026-06-01 launch shipped API access only; the arXiv report + // followed on 2026-06-11. sweep: 2026-06-12. + 'MiniMax-M3': '2026-06-07', + // V4 Preview — V4-Pro (1.6T total / 49B active) and V4-Flash (284B/13B) + // published together under MIT on Hugging Face, the API and chat.deepseek.com. + // V4-Pro-0813 went GA on 2026-08-13, but the Hugging Face repo still hosts the + // April preview build, so the weights date is unchanged. sweep: 2026-04-25. + 'DeepSeek-V4-Pro': '2026-04-24', + // Bucket covers GLM-5 and GLM-5.1, so the date is GLM-5's: released + // 2026-02-13, 744B/40B active, MIT, trained entirely on Huawei Ascend. Sources + // put the weights in "mid-February" without a day, and reporting clusters on + // 2026-02-13 and 2026-02-17; the Hugging Face commit history at zai-org/GLM-5 + // is the authority if this ever needs to be exact. GLM-5.1 followed in April + // 2026. sweep: 2026-03-06. + 'GLM-5': '2026-02-13', + // Apache 2.0 weights, plus official FP8 and GPTQ-Int4 quants, published at + // Qwen/Qwen3.5-397B-A17B on 2026-02-16 — the same day InferenceX first swept + // it. sweep: 2026-02-16 — day zero. + 'Qwen-3.5-397B-A17B': '2026-02-16', + // Bucket covers M2.5 and M2.7, so the date is M2.5's: announced 2026-02-12 + // with weights on Hugging Face, architecturally unchanged from M2 (230B/10B). + // Was 2025-10-25, which is M2's launch, not M2.5's — `model-architectures.ts` + // still points its sourceUrl at MiniMax-M2, which is where that came from. + // sweep: 2026-02-18. + 'MiniMax-M2.5': '2026-02-12', + // Weights at moonshotai/Kimi-K2.5 on 2026-01-27 under a modified MIT license, + // 1.04T total / 32B active. sweep: 2026-02-17. + 'Kimi-K2.5': '2026-01-27', + // Weights landed on Hugging Face 2025-08-05 under Apache 2.0, alongside + // gpt-oss-20b — OpenAI's first open-weight release since GPT-2. AWS Bedrock + // lists the same launch date; the model-card paper followed on 2025-08-08. Was + // 2025-06-13, which precedes the release by seven weeks and matches no + // published event. sweep: 2025-09-09. + 'gpt-oss-120b': '2025-08-05', + // Announced by WeChat post and pushed to Hugging Face on 2025-05-28 under MIT + // — the date the model name encodes. The model card commit landed a day later, + // weights first. sweep: 2025-08-13. 'DeepSeek-R1-0528': '2025-05-28', + // Meta published Llama 3.3 70B Instruct on 2024-12-06. sweep: 2025-08-12, + // shipped as workflow templates in the initial repo import. 'Llama-3.3-70B-Instruct-FP8': '2024-12-06', + // Llama 3.1 was published 2024-07-23. Hidden in the model selector, but the + // architecture diagram still captions it. No InferenceX sweep of its own. 'Llama-3.1-70B-Instruct-FP8-KV': '2024-07-23', - 'gpt-oss-120b': '2025-06-13', - 'Kimi-K2.5': '2026-01-27', - 'Kimi-K3': '2026-06-13', - 'MiniMax-M2.5': '2025-10-25', - 'DeepSeek-V4-Pro': '2026-06-08', }; /** Release date for a display model name, or null when we have no sourced date. */