Spaces:
Running
Running
| import { describe, expect, it } from "vitest" | |
| // Executable spec for `notes/transformations/10-params-parsing.md`. | |
| // | |
| // Five implementations of params-billions parsing exist in TS: | |
| // - Variant A: lib/model-data.ts:312-354 (parseParamsBillions) | |
| // - Variant B: components/eval-detail.tsx:81-119 (parseParamsBillionsFromText) | |
| // - Variant C: components/eval-detail.tsx:121-155 (parseParamsBillionsFromModelName) | |
| // - Variant D: components/eval-detail.tsx:157-184 (getParamsBillionsFromModelInfo β orchestrator) | |
| // - Variant E: components/model-compare-dialog.tsx:44-60 (parseParamsBillionsFromModelName) | |
| // - Variant F: app/evals/[id]/page.tsx:434-437 (inline regex on `name + " " + id`) | |
| // | |
| // They DIVERGE on edge cases (units accepted, anchoring, fallback chains, β€0 handling) | |
| // but converge on the most common production inputs (clean "7B" / "34.389" strings). | |
| // Migration target: pipeline emits a single canonical numeric `params_billions` per | |
| // model-result; all five parsers delete. | |
| // === Variant A: lib/model-data.ts:312 === | |
| function parseParamsBillions(value: unknown): number | null { | |
| if (typeof value === "number") { | |
| return Number.isFinite(value) && value > 0 ? value : null | |
| } | |
| if (typeof value !== "string") return null | |
| const normalized = value.trim().toLowerCase() | |
| if (!normalized) return null | |
| const compact = normalized.replace(/,/g, "") | |
| const tokenMatch = compact.match(/(\d+(?:\.\d+)?)\s*(trillion|tn|t|billion|bn|b|million|mn|m|thousand|k)\b/) | |
| if (tokenMatch) { | |
| const amount = Number.parseFloat(tokenMatch[1]) | |
| if (!Number.isFinite(amount) || amount <= 0) return null | |
| const unit = tokenMatch[2] | |
| if (unit === "trillion" || unit === "tn" || unit === "t") return amount * 1000 | |
| if (unit === "billion" || unit === "bn" || unit === "b") return amount | |
| if (unit === "million" || unit === "mn" || unit === "m") return amount / 1000 | |
| if (unit === "thousand" || unit === "k") return amount / 1_000_000 | |
| } | |
| const numeric = Number.parseFloat(compact) | |
| return Number.isFinite(numeric) && numeric > 0 ? numeric : null | |
| } | |
| // === Variant B: components/eval-detail.tsx:81 === | |
| function parseParamsBillionsFromText(value: string | null | undefined): number | null { | |
| if (!value) return null | |
| const normalized = value.trim().toLowerCase() | |
| if (!normalized) return null | |
| const compact = normalized.replace(/,/g, "") | |
| const tokenMatch = compact.match(/(\d+(?:\.\d+)?)\s*(trillion|tn|t|billion|bn|b|million|mn|m|thousand|k)\b/) | |
| if (tokenMatch) { | |
| const amount = Number.parseFloat(tokenMatch[1]) | |
| if (!Number.isFinite(amount)) return null | |
| const unit = tokenMatch[2] | |
| if (unit === "trillion" || unit === "tn" || unit === "t") return amount * 1000 | |
| if (unit === "billion" || unit === "bn" || unit === "b") return amount | |
| if (unit === "million" || unit === "mn" || unit === "m") return amount / 1000 | |
| if (unit === "thousand" || unit === "k") return amount / 1_000_000 | |
| } | |
| const numeric = Number.parseFloat(compact) | |
| return Number.isFinite(numeric) ? numeric : null | |
| } | |
| // === Variant C: components/eval-detail.tsx:121 === | |
| function parseParamsBillionsFromModelNameC(modelName: string | null | undefined): number | null { | |
| if (!modelName) return null | |
| const sizeTokens = Array.from(modelName.matchAll(/\b(\d+(?:\.\d+)?)\s*([tmbk])\b/gi)) | |
| if (sizeTokens.length === 0) return null | |
| const lastToken = sizeTokens[sizeTokens.length - 1] | |
| const numericValue = Number.parseFloat(lastToken[1]) | |
| if (!Number.isFinite(numericValue)) return null | |
| const unit = lastToken[2].toLowerCase() | |
| if (unit === "t") return numericValue * 1000 | |
| if (unit === "b") return numericValue | |
| if (unit === "m") return numericValue / 1000 | |
| if (unit === "k") return numericValue / 1_000_000 | |
| return null | |
| } | |
| // === Variant E: components/model-compare-dialog.tsx:44 === | |
| function parseParamsBillionsFromModelNameE(modelName: string | null | undefined): number | null { | |
| if (!modelName) return null | |
| const sizeTokens = Array.from(modelName.matchAll(/\b(\d+(?:\.\d+)?)\s*([bm])\b/gi)) | |
| if (sizeTokens.length === 0) return null | |
| const lastToken = sizeTokens[sizeTokens.length - 1] | |
| const numericValue = Number(lastToken[1]) | |
| if (!Number.isFinite(numericValue)) return null | |
| const unit = lastToken[2].toLowerCase() | |
| if (unit === "b") return numericValue | |
| if (unit === "m") return numericValue / 1000 | |
| return null | |
| } | |
| // === Variant F: app/evals/[id]/page.tsx:434-437 === | |
| function parseParamsBillionsInline(name: string, id: string): number | null { | |
| const sizeMatch = (name + " " + id).match(/\b(\d+(?:\.\d+)?)\s*[bB]\b/) | |
| if (sizeMatch) return parseFloat(sizeMatch[1]) | |
| return null | |
| } | |
| // --------------------------------------------------------------------------- | |
| // Group A β Variant A (parseParamsBillions, lib/model-data.ts) | |
| // --------------------------------------------------------------------------- | |
| describe("Group A β parseParamsBillions (Variant A, lib/model-data.ts)", () => { | |
| describe("number-input branch", () => { | |
| const cases = [ | |
| { input: 7, expected: 7, why: "finite + >0 β return as-is" }, | |
| { input: 34.389, expected: 34.389, why: "float passes through" }, | |
| { input: 0, expected: null, why: ">0 reject" }, | |
| { input: -3, expected: null, why: ">0 reject" }, | |
| { input: NaN, expected: null, why: "isFinite reject" }, | |
| { input: Infinity, expected: null, why: "isFinite reject" }, | |
| ] | |
| it.each(cases)("$input β $expected ($why)", ({ input, expected }) => { | |
| expect(parseParamsBillions(input)).toBe(expected) | |
| }) | |
| }) | |
| describe("non-string non-number β null", () => { | |
| it.each([ | |
| { input: null }, | |
| { input: undefined }, | |
| { input: [] }, | |
| { input: {} }, | |
| { input: true }, | |
| ])("$input β null", ({ input }) => { | |
| expect(parseParamsBillions(input)).toBe(null) | |
| }) | |
| }) | |
| describe("string-input branch β unit tokens", () => { | |
| const cases = [ | |
| { input: "7B", expected: 7, why: "b β as-is" }, | |
| { input: "7b", expected: 7, why: "lowercased" }, | |
| { input: "70B params", expected: 70, why: "regex anchors at b\\b; trailing text ignored" }, | |
| { input: "1.5B", expected: 1.5, why: "decimal supported" }, | |
| { input: "405b", expected: 405, why: "lowercased" }, | |
| { input: "7 billion", expected: 7, why: "billion β as-is" }, | |
| { input: "7bn", expected: 7, why: "bn alias" }, | |
| { input: "1.2T", expected: 1200, why: "t β Γ1000" }, | |
| { input: "2 trillion", expected: 2000, why: "trillion β Γ1000" }, | |
| { input: "2T params", expected: 2000, why: "t\\b anchor; trailing text ignored" }, | |
| { input: "3.5tn", expected: 3500, why: "tn alias" }, | |
| { input: "560M", expected: 0.56, why: "m β Γ·1000" }, | |
| { input: "560 million", expected: 0.56, why: "million β Γ·1000" }, | |
| { input: "1000K", expected: 0.001, why: "k β Γ·1_000_000" }, | |
| { input: "1,500B", expected: 1500, why: "comma stripped" }, | |
| { input: "7 B", expected: 7, why: "\\s* matches multiple spaces" }, | |
| ] | |
| it.each(cases)("'$input' β $expected ($why)", ({ input, expected }) => { | |
| expect(parseParamsBillions(input)).toBe(expected) | |
| }) | |
| }) | |
| describe("string-input branch β parseFloat fallback (no unit token)", () => { | |
| const cases = [ | |
| { input: "34.389", expected: 34.389, why: "no unit β parseFloat fallback" }, | |
| { input: "7", expected: 7, why: "bare numeric β parseFloat fallback" }, | |
| { input: "7Banana", expected: 7, why: "regex `b\\b` fails; parseFloat('7banana')=7. TS quirk: trailing junk allowed" }, | |
| { input: "abc", expected: null, why: "no match, parseFloat NaN β null" }, | |
| { input: "", expected: null, why: "trim empty β early return" }, | |
| { input: " ", expected: null, why: "trim empty β early return" }, | |
| ] | |
| it.each(cases)("'$input' β $expected ($why)", ({ input, expected }) => { | |
| expect(parseParamsBillions(input)).toBe(expected) | |
| }) | |
| }) | |
| describe("string-input branch β TS quirks (positive-only filtering)", () => { | |
| it("'0B' β null (regex matches; amount=0 fails β€0 check)", () => { | |
| expect(parseParamsBillions("0B")).toBe(null) | |
| }) | |
| it("'-5' β null (parseFloat=-5; >0 reject)", () => { | |
| expect(parseParamsBillions("-5")).toBe(null) | |
| }) | |
| it("'-5B' β 5 (regex matches `5b` substring; leading minus silently dropped)", () => { | |
| // TS quirk: `\d+` doesn't include `-`, but the regex isn't anchored at start, | |
| // so it scans past `-` and matches `5b`. amount=5 β returns 5 (NOT -5, NOT null). | |
| expect(parseParamsBillions("-5B")).toBe(5) | |
| }) | |
| }) | |
| describe("string-input branch β model names (Variant A on names)", () => { | |
| // Variant A is callable on any string; using it on model names exhibits | |
| // different behavior from Variant C because A uses `match()` (first match) | |
| // and a regex without leading `\b`. | |
| it("'Llama-3-70B-Instruct' β 70 (first match)", () => { | |
| expect(parseParamsBillions("Llama-3-70B-Instruct")).toBe(70) | |
| }) | |
| it("'Llama-3-70B-Instruct-8K' β 70 (first match wins, NOT last like Variant C)", () => { | |
| expect(parseParamsBillions("Llama-3-70B-Instruct-8K")).toBe(70) | |
| }) | |
| it("'Yi-1.5-34B-32K' β 34 (first match)", () => { | |
| expect(parseParamsBillions("Yi-1.5-34B-32K")).toBe(34) | |
| }) | |
| it("'Mixtral-8x7B' β 7 (no leading \\b β matches `7b` inside `8x7b`)", () => { | |
| // TS quirk: A's regex has no `\b` before `\d+`, so it can start matching | |
| // mid-word. C/E/F all return null here because they DO require leading `\b`. | |
| expect(parseParamsBillions("Mixtral-8x7B")).toBe(7) | |
| }) | |
| }) | |
| }) | |
| // --------------------------------------------------------------------------- | |
| // Group B β Variant B (parseParamsBillionsFromText, eval-detail.tsx) | |
| // --------------------------------------------------------------------------- | |
| describe("Group B β parseParamsBillionsFromText (Variant B, eval-detail.tsx)", () => { | |
| describe("string inputs (mostly converges with Variant A)", () => { | |
| const cases = [ | |
| { input: "7B", expected: 7 }, | |
| { input: "1.5B", expected: 1.5 }, | |
| { input: "1.2T", expected: 1200 }, | |
| { input: "560M", expected: 0.56 }, | |
| { input: "1000K", expected: 0.001 }, | |
| { input: "7 billion", expected: 7 }, | |
| { input: "34.389", expected: 34.389, why: "parseFloat fallback" }, | |
| { input: "1,500B", expected: 1500 }, | |
| { input: "7Banana", expected: 7, why: "parseFloat lenient; same as A" }, | |
| { input: "abc", expected: null }, | |
| { input: "", expected: null }, | |
| { input: " ", expected: null }, | |
| { input: null, expected: null }, | |
| { input: undefined, expected: null }, | |
| ] | |
| it.each(cases)("'$input' β $expected", ({ input, expected }) => { | |
| expect(parseParamsBillionsFromText(input)).toBe(expected) | |
| }) | |
| }) | |
| describe("DIVERGES from Variant A β no β€0 reject", () => { | |
| it("'0B' β 0 (B has no β€0 check; A returns null)", () => { | |
| expect(parseParamsBillionsFromText("0B")).toBe(0) | |
| expect(parseParamsBillions("0B")).toBe(null) | |
| }) | |
| it("'-5' β -5 (B has no >0 check; A returns null)", () => { | |
| expect(parseParamsBillionsFromText("-5")).toBe(-5) | |
| expect(parseParamsBillions("-5")).toBe(null) | |
| }) | |
| it("'-5B' β 5 (matches `5b` substring; same as A)", () => { | |
| expect(parseParamsBillionsFromText("-5B")).toBe(5) | |
| expect(parseParamsBillions("-5B")).toBe(5) | |
| }) | |
| }) | |
| describe("rejects non-string inputs (DIVERGES from Variant A which is polymorphic)", () => { | |
| // Variant B is typed as string|null|undefined; passing a number falls through `if (!value)` | |
| // when value is 0, but otherwise goes through trim/lowercase which throws on non-string. | |
| it("number input is not supported by Variant B (Variant A handles it)", () => { | |
| expect(parseParamsBillions(7)).toBe(7) | |
| // parseParamsBillionsFromText(7) would throw at .trim() β don't call it | |
| }) | |
| }) | |
| }) | |
| // --------------------------------------------------------------------------- | |
| // Group C β Variant C (parseParamsBillionsFromModelName, eval-detail.tsx) | |
| // --------------------------------------------------------------------------- | |
| describe("Group C β parseParamsBillionsFromModelName (Variant C, eval-detail.tsx)", () => { | |
| describe("happy-path model names", () => { | |
| const cases = [ | |
| { input: "Llama-3-70B-Instruct", expected: 70, why: "matchAll β only `70B`; last token; b β 70" }, | |
| { input: "Phi-3.5-mini-3.8B", expected: 3.8, why: "matches `3.8B`" }, | |
| { input: "Qwen2-7B-Instruct", expected: 7, why: "matches `7B`" }, | |
| { input: "560M", expected: 0.56, why: "m β Γ·1000" }, | |
| { input: "1.2T", expected: 1200, why: "t β Γ1000" }, | |
| { input: "2K", expected: 0.000002, why: "k β Γ·1_000_000" }, | |
| ] | |
| it.each(cases)("'$input' β $expected ($why)", ({ input, expected }) => { | |
| expect(parseParamsBillionsFromModelNameC(input)).toBe(expected) | |
| }) | |
| }) | |
| describe("TS quirk β context-window suffix beats parameter count (last-token wins)", () => { | |
| it("'Llama-3-8B-Instruct-8K' β 0.000008 (last token = `8K`, NOT `8B`)", () => { | |
| expect(parseParamsBillionsFromModelNameC("Llama-3-8B-Instruct-8K")).toBe(0.000008) | |
| }) | |
| it("'Llama-3-70B-Instruct-32K' β 0.000032 (last token = `32K`)", () => { | |
| expect(parseParamsBillionsFromModelNameC("Llama-3-70B-Instruct-32K")).toBe(0.000032) | |
| }) | |
| it("'Yi-1.5-34B-32K' β 0.000032 (real production model name; returns context window)", () => { | |
| expect(parseParamsBillionsFromModelNameC("Yi-1.5-34B-32K")).toBe(0.000032) | |
| }) | |
| }) | |
| describe("regex anchoring quirks (leading \\b required)", () => { | |
| it("'Mixtral-8x7B' β null (no `\\b` between `x` and `7` β no match)", () => { | |
| expect(parseParamsBillionsFromModelNameC("Mixtral-8x7B")).toBe(null) | |
| }) | |
| it("'7 billion' β null (single-letter unit only; `billion` doesn't match [tmbk])", () => { | |
| expect(parseParamsBillionsFromModelNameC("7 billion")).toBe(null) | |
| }) | |
| it("'GPT-4' β null (no unit token)", () => { | |
| expect(parseParamsBillionsFromModelNameC("GPT-4")).toBe(null) | |
| }) | |
| }) | |
| describe("falsy inputs", () => { | |
| it.each([ | |
| { input: "", expected: null }, | |
| { input: null, expected: null }, | |
| { input: undefined, expected: null }, | |
| ])("$input β null", ({ input, expected }) => { | |
| expect(parseParamsBillionsFromModelNameC(input)).toBe(expected) | |
| }) | |
| }) | |
| }) | |
| // --------------------------------------------------------------------------- | |
| // Group D β Variant E (parseParamsBillionsFromModelName, model-compare-dialog.tsx) | |
| // --------------------------------------------------------------------------- | |
| describe("Group D (E) β parseParamsBillionsFromModelName (Variant E, model-compare-dialog.tsx)", () => { | |
| describe("converges with C on b|m inputs", () => { | |
| const cases = [ | |
| { input: "Llama-3-70B-Instruct", expected: 70 }, | |
| { input: "Phi-3.5-mini-3.8B", expected: 3.8 }, | |
| { input: "560M", expected: 0.56 }, | |
| ] | |
| it.each(cases)("'$input' β $expected", ({ input, expected }) => { | |
| expect(parseParamsBillionsFromModelNameE(input)).toBe(expected) | |
| }) | |
| }) | |
| describe("DIVERGES from C β rejects t and k", () => { | |
| it("'Llama-3-8B-Instruct-8K' β 8 (E ignores K; last `b|m` token = `8B`; C returns 0.000008)", () => { | |
| expect(parseParamsBillionsFromModelNameE("Llama-3-8B-Instruct-8K")).toBe(8) | |
| expect(parseParamsBillionsFromModelNameC("Llama-3-8B-Instruct-8K")).toBe(0.000008) | |
| }) | |
| it("'Yi-1.5-34B-32K' β 34 (E ignores K; C returns 0.000032)", () => { | |
| expect(parseParamsBillionsFromModelNameE("Yi-1.5-34B-32K")).toBe(34) | |
| expect(parseParamsBillionsFromModelNameC("Yi-1.5-34B-32K")).toBe(0.000032) | |
| }) | |
| it("'1.2T' β null (E rejects t)", () => { | |
| expect(parseParamsBillionsFromModelNameE("1.2T")).toBe(null) | |
| expect(parseParamsBillionsFromModelNameC("1.2T")).toBe(1200) | |
| }) | |
| it("'2K' β null (E rejects k)", () => { | |
| expect(parseParamsBillionsFromModelNameE("2K")).toBe(null) | |
| }) | |
| }) | |
| describe("regex anchoring matches C", () => { | |
| it("'Mixtral-8x7B' β null (same as C β leading \\b fails)", () => { | |
| expect(parseParamsBillionsFromModelNameE("Mixtral-8x7B")).toBe(null) | |
| }) | |
| }) | |
| }) | |
| // --------------------------------------------------------------------------- | |
| // Group E (F) β Variant F (inline regex, app/evals/[id]/page.tsx) | |
| // --------------------------------------------------------------------------- | |
| describe("Group E (F) β inline regex on `name + ' ' + id` (Variant F, app/evals/[id]/page.tsx)", () => { | |
| describe("happy-path (b|B only)", () => { | |
| const cases = [ | |
| { name: "Llama-3-70B-Instruct", id: "meta/llama-3-70b-instruct", expected: 70, why: "first 70B match" }, | |
| { name: "1.5B-instruct", id: "meta/foo-1-5b", expected: 1.5, why: "first 1.5B match" }, | |
| ] | |
| it.each(cases)("['$name' + '$id'] β $expected ($why)", ({ name, id, expected }) => { | |
| expect(parseParamsBillionsInline(name, id)).toBe(expected) | |
| }) | |
| }) | |
| describe("DIVERGES from C/E β first-match (not last-match) ordering", () => { | |
| it("name with both `8B` and `70B` β returns FIRST (8), Variant C returns LAST (70)", () => { | |
| expect(parseParamsBillionsInline("Llama-3-8B-70B-Instruct", "meta/llama-3-8b-70b")).toBe(8) | |
| expect(parseParamsBillionsFromModelNameC("Llama-3-8B-70B-Instruct")).toBe(70) | |
| }) | |
| it("'Llama-3-70B-Instruct-8K' β 70 (8K not matched by [bB]; first 70B wins)", () => { | |
| // Variant F is *more correct* than C here because it ignores `K` entirely. | |
| expect(parseParamsBillionsInline("Llama-3-70B-Instruct-8K", "meta/llama-3-70b-instruct")).toBe(70) | |
| }) | |
| it("'Yi-1.5-34B-32K' β 34 (real production name; F returns correct value, C returns context window)", () => { | |
| expect(parseParamsBillionsInline("Yi-1.5-34B-32K", "01-ai/yi-1-5-34b-32k")).toBe(34) | |
| expect(parseParamsBillionsFromModelNameC("Yi-1.5-34B-32K")).toBe(0.000032) | |
| }) | |
| }) | |
| describe("DIVERGES from A/B/C β only b|B accepted", () => { | |
| it("'560M' β null (F is B-only)", () => { | |
| expect(parseParamsBillionsInline("560M", "openai/foo-560m")).toBe(null) | |
| }) | |
| it("'1.2T' β null (F is B-only)", () => { | |
| expect(parseParamsBillionsInline("1.2T", "openai/foo-1-2t")).toBe(null) | |
| }) | |
| it("'GPT-4' β null (no B token at all)", () => { | |
| expect(parseParamsBillionsInline("GPT-4", "openai/gpt-4")).toBe(null) | |
| }) | |
| }) | |
| describe("regex anchoring matches C/E (leading \\b required)", () => { | |
| it("'Mixtral-8x7B' β null (same as C/E β `\\b` fails between `x` and `7`)", () => { | |
| expect(parseParamsBillionsInline("Mixtral-8x7B", "mistralai/mixtral-8x7b")).toBe(null) | |
| }) | |
| }) | |
| describe("uses concatenated name + id (not just name)", () => { | |
| it("name lacks B but id has it β still matches", () => { | |
| // e.g. raw model id might carry the `7b` token even if display name omits it | |
| expect(parseParamsBillionsInline("Llama 3", "meta/llama-3-7b")).toBe(7) | |
| }) | |
| }) | |
| describe("empty/falsy", () => { | |
| it("'' + '' β null", () => { | |
| expect(parseParamsBillionsInline("", "")).toBe(null) | |
| }) | |
| }) | |
| }) | |
| // --------------------------------------------------------------------------- | |
| // Group F β Cross-variant divergence (DOCUMENTED, NOT FIXED) | |
| // --------------------------------------------------------------------------- | |
| // | |
| // The five variants produce different outputs for the same input. | |
| // In production these don't usually disagree (because the input *type* per | |
| // callsite is consistent: A sees clean numbers, B sees clean decimal strings, | |
| // C/E/F see model-name strings). | |
| describe("Group F β cross-variant divergence (TS quirks, do NOT fix)", () => { | |
| it("'0B' β A rejects (β€0); B/C/E accept; F accepts", () => { | |
| expect(parseParamsBillions("0B")).toBe(null) | |
| expect(parseParamsBillionsFromText("0B")).toBe(0) | |
| expect(parseParamsBillionsFromModelNameC("0B")).toBe(0) | |
| expect(parseParamsBillionsFromModelNameE("0B")).toBe(0) | |
| expect(parseParamsBillionsInline("0B", "")).toBe(0) | |
| }) | |
| it("'-5' (string) β A rejects (>0); B accepts -5; C/E/F have no unit so return null", () => { | |
| expect(parseParamsBillions("-5")).toBe(null) | |
| expect(parseParamsBillionsFromText("-5")).toBe(-5) | |
| expect(parseParamsBillionsFromModelNameC("-5")).toBe(null) | |
| expect(parseParamsBillionsFromModelNameE("-5")).toBe(null) | |
| expect(parseParamsBillionsInline("-5", "")).toBe(null) | |
| }) | |
| it("'7 billion' β A and B parse to 7; C/E/F have no full-word `billion` β null", () => { | |
| expect(parseParamsBillions("7 billion")).toBe(7) | |
| expect(parseParamsBillionsFromText("7 billion")).toBe(7) | |
| expect(parseParamsBillionsFromModelNameC("7 billion")).toBe(null) | |
| expect(parseParamsBillionsFromModelNameE("7 billion")).toBe(null) | |
| expect(parseParamsBillionsInline("7 billion", "")).toBe(null) | |
| }) | |
| it("'1.2T' β A/B/C accept; E/F reject (no `t` unit)", () => { | |
| expect(parseParamsBillions("1.2T")).toBe(1200) | |
| expect(parseParamsBillionsFromText("1.2T")).toBe(1200) | |
| expect(parseParamsBillionsFromModelNameC("1.2T")).toBe(1200) | |
| expect(parseParamsBillionsFromModelNameE("1.2T")).toBe(null) | |
| expect(parseParamsBillionsInline("1.2T", "")).toBe(null) | |
| }) | |
| it("'560M' β A/B/C/E accept; F rejects (no `m` unit)", () => { | |
| expect(parseParamsBillions("560M")).toBe(0.56) | |
| expect(parseParamsBillionsFromText("560M")).toBe(0.56) | |
| expect(parseParamsBillionsFromModelNameC("560M")).toBe(0.56) | |
| expect(parseParamsBillionsFromModelNameE("560M")).toBe(0.56) | |
| expect(parseParamsBillionsInline("560M", "")).toBe(null) | |
| }) | |
| it("'Mixtral-8x7B' β A/B accept (regex has no leading \\b β matches `7b` inside `8x7b`); C/E/F reject", () => { | |
| // Most striking divergence: A and B return 7, all model-name parsers return null. | |
| expect(parseParamsBillions("Mixtral-8x7B")).toBe(7) | |
| expect(parseParamsBillionsFromText("Mixtral-8x7B")).toBe(7) | |
| expect(parseParamsBillionsFromModelNameC("Mixtral-8x7B")).toBe(null) | |
| expect(parseParamsBillionsFromModelNameE("Mixtral-8x7B")).toBe(null) | |
| expect(parseParamsBillionsInline("Mixtral-8x7B", "mistralai/mixtral-8x7b")).toBe(null) | |
| }) | |
| it("'Llama-3-8B-Instruct-8K' β A/B/F return 8/8/8 (first match or [bB]-only); C returns 0.000008; E returns 8", () => { | |
| // A and B both use match() (first match) on a leading-anchor-free regex β first | |
| // match is `8B` β 8. C uses matchAll() (last match) and accepts `K` β `8K` β 0.000008. | |
| // E uses matchAll() but rejects `K` β last `b|m` token = `8B` β 8. F is [bB]-only, | |
| // first match β `8B` β 8. | |
| expect(parseParamsBillions("Llama-3-8B-Instruct-8K")).toBe(8) | |
| expect(parseParamsBillionsFromText("Llama-3-8B-Instruct-8K")).toBe(8) | |
| expect(parseParamsBillionsFromModelNameC("Llama-3-8B-Instruct-8K")).toBe(0.000008) | |
| expect(parseParamsBillionsFromModelNameE("Llama-3-8B-Instruct-8K")).toBe(8) | |
| expect(parseParamsBillionsInline("Llama-3-8B-Instruct-8K", "meta/llama-3-8b-8k")).toBe(8) | |
| }) | |
| it("'Yi-1.5-34B-32K' (real production name) β only Variant C is wrong (returns context window)", () => { | |
| expect(parseParamsBillions("Yi-1.5-34B-32K")).toBe(34) | |
| expect(parseParamsBillionsFromText("Yi-1.5-34B-32K")).toBe(34) | |
| expect(parseParamsBillionsFromModelNameC("Yi-1.5-34B-32K")).toBe(0.000032) | |
| expect(parseParamsBillionsFromModelNameE("Yi-1.5-34B-32K")).toBe(34) | |
| expect(parseParamsBillionsInline("Yi-1.5-34B-32K", "01-ai/yi-1-5-34b-32k")).toBe(34) | |
| }) | |
| it("name with multiple B-tokens β first vs last divergence (F vs C/E)", () => { | |
| // Synthetic: `Llama-3-8B-70B-Instruct` β first is 8B, last is 70B | |
| expect(parseParamsBillionsInline("Llama-3-8B-70B-Instruct", "")).toBe(8) // F: first | |
| expect(parseParamsBillionsFromModelNameC("Llama-3-8B-70B-Instruct")).toBe(70) // C: last | |
| expect(parseParamsBillionsFromModelNameE("Llama-3-8B-70B-Instruct")).toBe(70) // E: last | |
| expect(parseParamsBillions("Llama-3-8B-70B-Instruct")).toBe(8) // A: first via match() | |
| expect(parseParamsBillionsFromText("Llama-3-8B-70B-Instruct")).toBe(8) // B: first via match() | |
| }) | |
| it("clean production-shaped inputs β all variants converge", () => { | |
| // The empirical reason this divergence rarely fires: in production, A/B see | |
| // `additional_details.params_billions` strings like "34.389" (clean decimal), | |
| // while C/E/F see model names like "Llama-3-70B-Instruct" (single B-token, | |
| // no context-window suffix). For these inputs all variants agree. | |
| expect(parseParamsBillions("34.389")).toBe(34.389) | |
| expect(parseParamsBillionsFromText("34.389")).toBe(34.389) | |
| // C/E/F can't handle bare decimals (no unit) but they're not given them | |
| expect(parseParamsBillionsFromModelNameC("Llama-3-70B-Instruct")).toBe(70) | |
| expect(parseParamsBillionsFromModelNameE("Llama-3-70B-Instruct")).toBe(70) | |
| expect(parseParamsBillionsInline("Llama-3-70B-Instruct", "meta/llama-3-70b-instruct")).toBe(70) | |
| }) | |
| }) | |