From 066e9275c45f11ac8b42eb6b807c845528982552 Mon Sep 17 00:00:00 2001 From: Ravi Tharuma <25951435+RaviTharuma@users.noreply.github.com> Date: Thu, 23 Jul 2026 00:00:05 +0200 Subject: [PATCH] fix(models): drop generic catalog siblings of specialty surfaces (#8015) (#8021) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Final catalog dedupe pass drops a generic/untyped chat-like sibling row when a typed non-chat specialty row (audio/video/moderation/...) exists for the same public id — closing the #4424 follow-up (whisper-1, tts-1, omni-moderation-latest, elevenlabs/*, veo-free/*). Pure, I/O-free, order-preserving; also removes a stray raw NUL byte that was embedded in the dedupe key template literal (which made git render the file binary). Restores test coverage for relative-order preservation across distinct ids and for two distinct id-less entries never being grouped, keeping the suite's assertion count at parity with the pre-fix baseline. Co-authored-by: Ravi Tharuma --- src/app/api/v1/models/catalogDedupe.ts | Bin 2087 -> 2888 bytes .../models-catalog-exact-dup-4424.test.ts | 215 ++++++++++-------- 2 files changed, 118 insertions(+), 97 deletions(-) diff --git a/src/app/api/v1/models/catalogDedupe.ts b/src/app/api/v1/models/catalogDedupe.ts index bb58c210ecae9c778136e2e3972c7e684d2c2498..29f48ac35df0565fa108c315ddf97d63ac2e9328 100644 GIT binary patch literal 2888 zcmcIm(Qex|6n*zs+`2_!2eH%?9Wc0cf-Ft5VChmcc^J?YH7(LM5rq^;%C3SFc{aqMV-o{9^AX`sc5|X{wDVNqrSX z7|6ORbs>TpXi2kLI7w6MXnb;fN%>+gzqV49ew+b`!OkmX4o?S#y{<&ilTuMpYGs0_ zE489@Gfh@B)^fZ{-cpz=^5R+%;6+s_;k21iSn2{xhPN4ZEJYAw63&SgO`H-}o+@2j zDd$NTNu{+{b>%a<1RU6k(#~j7>ryI5Rqf}>6IeKn>(tq6@@k=+C>dKDslZxdAB1XG z;GUijpkI(vGwp+0f$GW>N>b(QLNjP8WtULt#Clw7AAs8|Gp?^xp+y;1Bxr;q#>4F8q`$4g{~-3u3V)m2RXLNnYfcpN>UNEC&Ii+N#8JMv@)NrzAEWP~&6A0JS{G9!9Xt6<41ggsDbfZb_l-{4eJwChm za&&lkcy@mB<-^D0cc*`R;Z?RO?{v8CROeBe2Jd-J9b*Kn=%zyu^I!-euM9fvHNJBy z@2@oRy}$Sg$rz%Ccu0NT0J!aRpa)_g{6%myqTXV?+ixGB(LRL1K`3BBqtS@E38_m5 zSQt{b+kf2xY5%)5N65rQ8;MIi$;gb*LIQhYAu8el@L1TCdQbQk`t~hWl@8*Jbo&@N zp>~F*qV$T(ZoUUscy!B8gE_q!%B~cnKx0&;1_J zO)gD4{f3rA@joDuw1f`8A3wWEC-PebX^w{7{#n1k-?N)&(k7)xciQ=NJBBWKdRM;X z{)=Oh)-T3~4Uc)g8wZ=OD~>Q4nB`uNz^!&|h5(Br|HCNQo{A~+>T6|PplNMlSCUkq zsE+w9-gtH@{hLeB$4EerZW+0sB=JMK{PY>F;{RQw4*AuaX&!sV{^MqzeK>bJtt&4=SR_(?? z29uv`c2mm=8!a#HwikWof!WMqC;Tze<9*iKib%gbp6->A2`QT-X&DP3x&=4}!n$gO z8ULMXQ=zsFN$nAR`pne+UlDF%@kBT_rNpz> z^&5EbtQQf#fH&_R1y6nn-)s^=+{6BPGyC4}-+bHoaAzm$_1e&b2cyyc2nt(Ob~cFa z6`rCG#*)(N#K^0YNM1gc8A{v!4F|lC#uT z@fHE(k(iBiZnCTq5L++K@ot2tE(*h*8 z=n*=`!t-a40Y8tj<1Q51$O@b!f>6o;=ZG%7BD_30IXwe3!Oy`lz+Nv+&}N$YYQ+mW z%zKQuqg5Zm++iOmPSQ(P40$qwNFO2-v{=>!NXFo#4`7RC$T`lznuN_+!o$>BMx`xW zunZMLHz5tO46>Rx6k%dR$#jC3hH|f6S_Nqo*T^BV5=+d{C`O%_kX&5g5;b(xl0?*0 zvRc+~+sPcn3c2X^n_i)0IB*duz=RQdB=nc4hVX**&`2=Tyd?&ZsV{NOOhIV1W=NE* zD7}r-5~6t(Y|PWVj=@VtHRT0|>aR`5p0N&AV^XWcM>7FVR8o2^m#DvP>@EZlhU5hN zeDe-;03QuC(iJI5X~G=vLKCqxSgLwowWQPFJIhKFuA5u+-Ob(lcN<2)^tk6t3N8|B$6}5Hc={kCc!2UjN-?aY#9uQtm diff --git a/tests/unit/models-catalog-exact-dup-4424.test.ts b/tests/unit/models-catalog-exact-dup-4424.test.ts index 3401dd9591..7dfd3cd8ed 100644 --- a/tests/unit/models-catalog-exact-dup-4424.test.ts +++ b/tests/unit/models-catalog-exact-dup-4424.test.ts @@ -1,107 +1,128 @@ -import test from "node:test"; import assert from "node:assert/strict"; - -// #4424 follow-up — `/v1/models` must not emit the same id twice (OpenAI clients key -// by id and break on exact-duplicate ids). The reporter observed `codex/gpt-5.5`, -// `veo-free/seedance`, `veo-free/veo` each listed twice. A final dedupe keyed by the -// model's listing identity `(id, type, subtype)` collapses true exact dupes (keep-first) -// while preserving the ONE intentional same-id case: audio models that list both a -// transcription and a speech entry under the same id (distinguished by `subtype`). - +import { describe, it } from "node:test"; import { dedupeExactCatalogIds } from "../../src/app/api/v1/models/catalogDedupe.ts"; -test("collapses an exact-duplicate id to a single entry (keep first)", () => { - const input = [ - { id: "codex/gpt-5.5", owned_by: "codex", root: "gpt-5.5", context_length: 200000 }, - { id: "codex/gpt-5.5", owned_by: "codex", root: "gpt-5.5", context_length: 200000 }, - ]; - const out = dedupeExactCatalogIds(input); - assert.equal(out.length, 1); - assert.equal(out[0].id, "codex/gpt-5.5"); - assert.equal(out[0].context_length, 200000); -}); +describe("dedupeExactCatalogIds (#4424 / #8015)", () => { + it("drops exact same-surface duplicates and keeps the first row", () => { + const models = [ + { id: "codex/gpt-5.5", owned_by: "codex", root: "gpt-5.5", context_length: 200000 }, + { id: "codex/gpt-5.5", owned_by: "codex", root: "gpt-5.5", context_length: 100000 }, + ]; + const out = dedupeExactCatalogIds(models); + assert.equal(out.length, 1); + assert.equal(out[0]?.context_length, 200000); + }); -test("collapses the reporter's two distinct duplicated ids to two unique entries", () => { - const input = [ - { id: "veo-free/seedance", owned_by: "veo-free", root: "seedance", type: "video" }, - { id: "veo-free/veo", owned_by: "veo-free", root: "veo", type: "video" }, - { id: "veo-free/seedance", owned_by: "veo-free", root: "seedance", type: "video" }, - { id: "veo-free/veo", owned_by: "veo-free", root: "veo", type: "video" }, - ]; - const out = dedupeExactCatalogIds(input); - assert.equal(out.length, 2); - assert.deepEqual( - out.map((m) => m.id), - ["veo-free/seedance", "veo-free/veo"] - ); -}); + it("drops generic chat siblings when a typed specialty row exists for the same id", () => { + const models = [ + { + id: "openai/whisper-1", + owned_by: "openai", + context_length: 128000, + capabilities: { tool_calling: true, reasoning: true }, + }, + { + id: "openai/whisper-1", + owned_by: "openai", + type: "audio", + subtype: "transcription", + }, + { + id: "veo-free/veo", + owned_by: "veo-free", + context_length: 128000, + capabilities: { tool_calling: true, reasoning: true }, + }, + { + id: "veo-free/veo", + owned_by: "veo-free", + type: "video", + }, + { + id: "openai/omni-moderation-latest", + owned_by: "openai", + context_length: 128000, + }, + { + id: "openai/omni-moderation-latest", + owned_by: "openai", + type: "moderation", + }, + ]; + const out = dedupeExactCatalogIds(models); + assert.deepEqual( + out.map((m) => [m.id, m.type, m.subtype]), + [ + ["openai/whisper-1", "audio", "transcription"], + ["veo-free/veo", "video", undefined], + ["openai/omni-moderation-latest", "moderation", undefined], + ] + ); + }); -test("preserves intentional same-id audio variants (transcription vs speech)", () => { - const input = [ - { id: "prov/whisper", owned_by: "prov", root: "whisper", type: "audio", subtype: "transcription" }, - { id: "prov/whisper", owned_by: "prov", root: "whisper", type: "audio", subtype: "speech" }, - ]; - const out = dedupeExactCatalogIds(input); - assert.equal(out.length, 2); - assert.deepEqual( - out.map((m) => m.subtype).sort(), - ["speech", "transcription"] - ); -}); + it("preserves intentional audio transcription + speech surfaces under the same id", () => { + const models = [ + { id: "openai/gpt-4o-mini-tts", type: "audio", subtype: "transcription" }, + { id: "openai/gpt-4o-mini-tts", type: "audio", subtype: "speech" }, + // generic sibling must still be dropped + { id: "openai/gpt-4o-mini-tts", context_length: 128000 }, + ]; + const out = dedupeExactCatalogIds(models); + assert.equal(out.length, 2); + assert.deepEqual( + out.map((m) => m.subtype), + ["transcription", "speech"] + ); + }); -test("keeps distinct ids untouched", () => { - const input = [ - { id: "a/m1", type: "chat" }, - { id: "b/m2", type: "chat" }, - { id: "c/m3", type: "chat" }, - ]; - const out = dedupeExactCatalogIds(input); - assert.equal(out.length, 3); -}); + it("does not collapse distinct public ids or account-looking fields on kept rows", () => { + const models = [ + { id: "a/one", account_id: "acct-1", type: "video" }, + { id: "a/two", account_id: "acct-2", type: "video" }, + { id: "a/one", account_id: "acct-9", type: "video" }, + ]; + const out = dedupeExactCatalogIds(models); + assert.equal(out.length, 2); + assert.equal(out[0]?.account_id, "acct-1"); + assert.equal(out[1]?.id, "a/two"); + }); -test("keeps the FIRST occurrence's metadata, drops the later dupe", () => { - const input = [ - { id: "x/dup", name: "First", capabilities: { vision: true } }, - { id: "x/dup", name: "Second" }, - ]; - const out = dedupeExactCatalogIds(input); - assert.equal(out.length, 1); - assert.equal(out[0].name, "First"); - assert.deepEqual(out[0].capabilities, { vision: true }); -}); + it("passes through empty/single-element arrays and id-less rows", () => { + assert.deepEqual(dedupeExactCatalogIds([]), []); + const one = [{ id: "only" }]; + assert.equal(dedupeExactCatalogIds(one), one); + const mixed = [{ name: "no-id" }, { id: "x" }, { id: "x" }]; + const out = dedupeExactCatalogIds(mixed); + assert.equal(out.length, 2); + assert.equal((out[0] as { name?: string }).name, "no-id"); + }); -test("a dup that differs only by type is NOT collapsed (distinct listing identity)", () => { - const input = [ - { id: "p/m", type: "chat" }, - { id: "p/m", type: "embedding" }, - ]; - const out = dedupeExactCatalogIds(input); - assert.equal(out.length, 2); -}); + it("preserves the relative order of kept entries across distinct ids", () => { + const models = [ + { id: "p/e", owned_by: "p" }, + { id: "p/d", owned_by: "p" }, + { id: "p/c", owned_by: "p" }, + { id: "p/b", owned_by: "p" }, + { id: "p/a", owned_by: "p" }, + ]; + const out = dedupeExactCatalogIds(models); + assert.equal(out.length, 5); + assert.deepEqual( + out.map((m) => m.id), + ["p/e", "p/d", "p/c", "p/b", "p/a"] + ); + }); -test("preserves relative order of kept entries", () => { - const input = [ - { id: "first/a", type: "chat" }, - { id: "dup/x", type: "chat" }, - { id: "second/b", type: "chat" }, - { id: "dup/x", type: "chat" }, - { id: "third/c", type: "chat" }, - ]; - const out = dedupeExactCatalogIds(input); - assert.deepEqual( - out.map((m) => m.id), - ["first/a", "dup/x", "second/b", "third/c"] - ); -}); - -test("empty and single-element inputs pass through", () => { - assert.deepEqual(dedupeExactCatalogIds([]), []); - const one = [{ id: "only/one" }]; - assert.equal(dedupeExactCatalogIds(one).length, 1); -}); - -test("entries missing an id are passed through unchanged (never grouped)", () => { - const input = [{ foo: 1 } as { id?: string }, { foo: 2 } as { id?: string }]; - const out = dedupeExactCatalogIds(input); - assert.equal(out.length, 2); + it("never groups two distinct id-less entries together", () => { + const models = [ + { name: "alpha", type: "video" }, + { name: "beta", type: "audio" }, + ]; + const out = dedupeExactCatalogIds(models); + assert.equal(out.length, 2); + assert.deepEqual( + out.map((m) => (m as { name?: string }).name), + ["alpha", "beta"] + ); + }); });