@oh-my-pi/pi-catalog 15.11.7 → 15.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/types/variant-collapse.d.ts +9 -0
- package/package.json +3 -3
- package/src/model-manager.ts +1 -1
- package/src/models.json +2 -2
- package/src/variant-collapse.ts +103 -10
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [15.11.8] - 2026-06-12
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Fixed Antigravity `gemini-3.1-pro --thinking high` failing with `Cloud Code Assist API error (400): Request contains an invalid argument.` — the upstream `gemini-3.1-pro-high` deployment rejects every `streamGenerateContent` request on both CCA endpoints while discovery still advertises it. High effort now routes to `gemini-pro-agent` (the same "Gemini 3.1 Pro (High)" model, verified accepting the identical request body), and the model-cache fingerprint version was bumped (`merge-v2` → `merge-v3`) so existing fresh caches refetch discovery and pick up the corrected routing immediately.
|
|
10
|
+
|
|
5
11
|
## [15.11.7] - 2026-06-12
|
|
6
12
|
### Added
|
|
7
13
|
|
|
@@ -21,6 +21,15 @@ export interface EffortVariantFamily {
|
|
|
21
21
|
* when it equals the logical id).
|
|
22
22
|
*/
|
|
23
23
|
members: readonly string[];
|
|
24
|
+
/**
|
|
25
|
+
* Wire ids upstream no longer serves (e.g. a deployment killed while
|
|
26
|
+
* discovery still advertises it). Fresh collapsing never routes to them,
|
|
27
|
+
* and stale collapsed snapshots (bundled catalog, cache rows,
|
|
28
|
+
* previous-generation fallbacks) get routing/`requestModelId` entries that
|
|
29
|
+
* target them re-pointed through `routing`. Keep retired ids in `members`
|
|
30
|
+
* so the raw upstream spec is still consumed and aliased.
|
|
31
|
+
*/
|
|
32
|
+
retiredMembers?: readonly string[];
|
|
24
33
|
/**
|
|
25
34
|
* Per-effort upstream wire id; `"off"` applies when thinking is disabled.
|
|
26
35
|
* Entries whose target member is absent from the input are dropped — those
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-catalog",
|
|
4
|
-
"version": "15.
|
|
4
|
+
"version": "15.12.0",
|
|
5
5
|
"description": "Model catalog for omp: bundled model database, provider discovery descriptors, model identity, classification, and equivalence",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -34,11 +34,11 @@
|
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@bufbuild/protobuf": "^2.12.0",
|
|
37
|
-
"@oh-my-pi/pi-utils": "15.
|
|
37
|
+
"@oh-my-pi/pi-utils": "15.12.0",
|
|
38
38
|
"zod": "4.4.3"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@oh-my-pi/pi-ai": "15.
|
|
41
|
+
"@oh-my-pi/pi-ai": "15.12.0",
|
|
42
42
|
"@types/bun": "^1.3.14"
|
|
43
43
|
},
|
|
44
44
|
"engines": {
|
package/src/model-manager.ts
CHANGED
|
@@ -301,7 +301,7 @@ function retainModelIds<TApi extends Api>(
|
|
|
301
301
|
* arms calling `resolveProviderModels` with the same `staticModels` array)
|
|
302
302
|
* skip the JSON+hash work after the first call.
|
|
303
303
|
*/
|
|
304
|
-
const MODEL_CACHE_FINGERPRINT_VERSION = "merge-
|
|
304
|
+
const MODEL_CACHE_FINGERPRINT_VERSION = "merge-v3";
|
|
305
305
|
const kStaticFingerprint = Symbol("model-manager.staticFingerprint");
|
|
306
306
|
type ModelArrayWithFingerprint = readonly Model<Api>[] & { [kStaticFingerprint]?: string };
|
|
307
307
|
function fingerprintStatic<TApi extends Api>(
|
package/src/models.json
CHANGED
|
@@ -16574,7 +16574,7 @@
|
|
|
16574
16574
|
"effortRouting": {
|
|
16575
16575
|
"off": "gemini-3.1-pro-low",
|
|
16576
16576
|
"low": "gemini-3.1-pro-low",
|
|
16577
|
-
"high": "gemini-
|
|
16577
|
+
"high": "gemini-pro-agent"
|
|
16578
16578
|
},
|
|
16579
16579
|
"suppressWhenOff": true
|
|
16580
16580
|
},
|
|
@@ -54417,7 +54417,7 @@
|
|
|
54417
54417
|
"cacheRead": 0.135,
|
|
54418
54418
|
"cacheWrite": 0
|
|
54419
54419
|
},
|
|
54420
|
-
"contextWindow":
|
|
54420
|
+
"contextWindow": 131072,
|
|
54421
54421
|
"maxTokens": 16384,
|
|
54422
54422
|
"thinking": {
|
|
54423
54423
|
"mode": "effort",
|
package/src/variant-collapse.ts
CHANGED
|
@@ -57,6 +57,15 @@ export interface EffortVariantFamily {
|
|
|
57
57
|
* when it equals the logical id).
|
|
58
58
|
*/
|
|
59
59
|
members: readonly string[];
|
|
60
|
+
/**
|
|
61
|
+
* Wire ids upstream no longer serves (e.g. a deployment killed while
|
|
62
|
+
* discovery still advertises it). Fresh collapsing never routes to them,
|
|
63
|
+
* and stale collapsed snapshots (bundled catalog, cache rows,
|
|
64
|
+
* previous-generation fallbacks) get routing/`requestModelId` entries that
|
|
65
|
+
* target them re-pointed through `routing`. Keep retired ids in `members`
|
|
66
|
+
* so the raw upstream spec is still consumed and aliased.
|
|
67
|
+
*/
|
|
68
|
+
retiredMembers?: readonly string[];
|
|
60
69
|
/**
|
|
61
70
|
* Per-effort upstream wire id; `"off"` applies when thinking is disabled.
|
|
62
71
|
* Entries whose target member is absent from the input are dropped — those
|
|
@@ -123,12 +132,18 @@ export const ANTIGRAVITY_VARIANT_COLLAPSE_TABLE: VariantCollapseTable = {
|
|
|
123
132
|
{
|
|
124
133
|
id: "gemini-3.1-pro",
|
|
125
134
|
name: "Gemini 3.1 Pro",
|
|
126
|
-
// `gemini-pro-agent`
|
|
127
|
-
|
|
135
|
+
// High routes to `gemini-pro-agent` — the upstream `gemini-3.1-pro-high`
|
|
136
|
+
// deployment returns INVALID_ARGUMENT on every streamGenerateContent
|
|
137
|
+
// request (both CCA endpoints) while discovery still lists it;
|
|
138
|
+
// `gemini-pro-agent` is the same model ("Gemini 3.1 Pro (High)", same
|
|
139
|
+
// thinking budget/caps) and accepts the identical request body.
|
|
140
|
+
// `gemini-3.1-pro-high` stays a member so the dead raw id is consumed.
|
|
141
|
+
members: ["gemini-3.1-pro-low", "gemini-pro-agent", "gemini-3.1-pro-high"],
|
|
142
|
+
retiredMembers: ["gemini-3.1-pro-high"],
|
|
128
143
|
routing: {
|
|
129
144
|
off: "gemini-3.1-pro-low",
|
|
130
145
|
[Effort.Low]: "gemini-3.1-pro-low",
|
|
131
|
-
[Effort.High]: "gemini-
|
|
146
|
+
[Effort.High]: "gemini-pro-agent",
|
|
132
147
|
},
|
|
133
148
|
thinking: { mode: "google-level", efforts: GEMINI_3_PRO_FAMILY_EFFORTS },
|
|
134
149
|
suppressWhenOff: true,
|
|
@@ -282,6 +297,69 @@ export function isVariantCollapsedSpec(spec: VariantSpecLike): boolean {
|
|
|
282
297
|
return table !== undefined && getAliasIndex(table).familyIds.has(spec.id);
|
|
283
298
|
}
|
|
284
299
|
|
|
300
|
+
/**
|
|
301
|
+
* Re-point a stale collapsed spec whose `requestModelId` or routing still
|
|
302
|
+
* targets a retired wire id. Collapsed snapshots (bundled catalog, cache
|
|
303
|
+
* rows, previous-generation fallbacks) pass through collapsing untouched, so
|
|
304
|
+
* a hand-table routing fix would otherwise never reach them. Only retired
|
|
305
|
+
* targets are rewritten — presence-filtered routing decisions from live
|
|
306
|
+
* discovery stay authoritative for everything else. Per retired entry the
|
|
307
|
+
* table's route for that effort wins, then the off/first-live-member wire id,
|
|
308
|
+
* then the route is dropped (falls back to `requestModelId ?? id`). Returns
|
|
309
|
+
* `spec` by reference when nothing targets a retired id.
|
|
310
|
+
*/
|
|
311
|
+
function reconcileRetiredRouting<TSpec extends VariantSpecLike>(
|
|
312
|
+
spec: TSpec,
|
|
313
|
+
family: EffortVariantFamily,
|
|
314
|
+
retired: ReadonlySet<string>,
|
|
315
|
+
): TSpec {
|
|
316
|
+
const routing = spec.thinking?.effortRouting;
|
|
317
|
+
const requestRetired = spec.requestModelId !== undefined && retired.has(spec.requestModelId);
|
|
318
|
+
let routingRetired = false;
|
|
319
|
+
if (routing !== undefined) {
|
|
320
|
+
for (const key in routing) {
|
|
321
|
+
const target = routing[key as Effort | "off"];
|
|
322
|
+
if (target !== undefined && retired.has(target)) {
|
|
323
|
+
routingRetired = true;
|
|
324
|
+
break;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
if (!requestRetired && !routingRetired) return spec;
|
|
329
|
+
|
|
330
|
+
const offTarget = family.routing.off;
|
|
331
|
+
const fallbackWireId =
|
|
332
|
+
offTarget !== undefined && !retired.has(offTarget) ? offTarget : family.members.find(id => !retired.has(id));
|
|
333
|
+
const next: TSpec = { ...spec };
|
|
334
|
+
if (routingRetired && routing !== undefined) {
|
|
335
|
+
const nextRouting: Partial<Record<Effort | "off", string>> = {};
|
|
336
|
+
for (const key in routing) {
|
|
337
|
+
const effortKey = key as Effort | "off";
|
|
338
|
+
const target = routing[effortKey];
|
|
339
|
+
if (target === undefined) continue;
|
|
340
|
+
if (!retired.has(target)) {
|
|
341
|
+
nextRouting[effortKey] = target;
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
const tableTarget = family.routing[effortKey];
|
|
345
|
+
if (tableTarget !== undefined && !retired.has(tableTarget)) {
|
|
346
|
+
nextRouting[effortKey] = tableTarget;
|
|
347
|
+
} else if (fallbackWireId !== undefined) {
|
|
348
|
+
nextRouting[effortKey] = fallbackWireId;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
next.thinking = { ...(spec.thinking as ThinkingConfig), effortRouting: nextRouting };
|
|
352
|
+
}
|
|
353
|
+
if (requestRetired) {
|
|
354
|
+
if (fallbackWireId !== undefined && fallbackWireId !== spec.id) {
|
|
355
|
+
next.requestModelId = fallbackWireId;
|
|
356
|
+
} else {
|
|
357
|
+
delete next.requestModelId;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return next;
|
|
361
|
+
}
|
|
362
|
+
|
|
285
363
|
/**
|
|
286
364
|
* Collapse every family in `table` found in `specs`. Non-member specs pass
|
|
287
365
|
* through verbatim (by reference), order preserved; the collapsed spec
|
|
@@ -302,13 +380,26 @@ export function collapseEffortVariants<TSpec extends VariantSpecLike>(
|
|
|
302
380
|
const familyIdBySpecId = new Map<string, string>();
|
|
303
381
|
|
|
304
382
|
for (const family of table.families) {
|
|
383
|
+
const retired =
|
|
384
|
+
family.retiredMembers !== undefined && family.retiredMembers.length > 0
|
|
385
|
+
? new Set(family.retiredMembers)
|
|
386
|
+
: undefined;
|
|
305
387
|
const existing = byId.get(family.id);
|
|
306
388
|
const existingCollapsed =
|
|
307
389
|
existing !== undefined &&
|
|
308
390
|
(existing.requestModelId !== undefined || existing.thinking?.effortRouting !== undefined);
|
|
391
|
+
const reconciled =
|
|
392
|
+
existing !== undefined && existingCollapsed && retired !== undefined
|
|
393
|
+
? reconcileRetiredRouting(existing, family, retired)
|
|
394
|
+
: existing;
|
|
309
395
|
const rawPresent = family.members.filter(id => byId.has(id) && !(id === family.id && existingCollapsed));
|
|
310
396
|
if (rawPresent.length === 0) {
|
|
311
397
|
// Inert (no members) or already collapsed (pass-through) — idempotence.
|
|
398
|
+
// A stale collapsed entry still gets retired routing re-pointed.
|
|
399
|
+
if (reconciled !== undefined && reconciled !== existing) {
|
|
400
|
+
familyIdBySpecId.set(family.id, family.id);
|
|
401
|
+
replacement.set(family.id, reconciled);
|
|
402
|
+
}
|
|
312
403
|
continue;
|
|
313
404
|
}
|
|
314
405
|
|
|
@@ -317,8 +408,8 @@ export function collapseEffortVariants<TSpec extends VariantSpecLike>(
|
|
|
317
408
|
|
|
318
409
|
if (existingCollapsed) {
|
|
319
410
|
// Mixed input: the collapsed entry (live truth) wins; stale raw
|
|
320
|
-
// members are deduped away.
|
|
321
|
-
replacement.set(family.id,
|
|
411
|
+
// members are deduped away. Retired targets are re-pointed first.
|
|
412
|
+
replacement.set(family.id, reconciled as TSpec);
|
|
322
413
|
continue;
|
|
323
414
|
}
|
|
324
415
|
|
|
@@ -329,7 +420,7 @@ export function collapseEffortVariants<TSpec extends VariantSpecLike>(
|
|
|
329
420
|
let hasEffortRoute = false;
|
|
330
421
|
for (const effortKey in family.routing) {
|
|
331
422
|
const target = family.routing[effortKey as Effort | "off"];
|
|
332
|
-
if (target !== undefined && presentSet.has(target)) {
|
|
423
|
+
if (target !== undefined && presentSet.has(target) && !retired?.has(target)) {
|
|
333
424
|
routing[effortKey as Effort | "off"] = target;
|
|
334
425
|
hasRouting = true;
|
|
335
426
|
if (effortKey !== "off") hasEffortRoute = true;
|
|
@@ -356,12 +447,14 @@ export function collapseEffortVariants<TSpec extends VariantSpecLike>(
|
|
|
356
447
|
contextWindow: Math.max(...memberSpecs.map(spec => spec.contextWindow)),
|
|
357
448
|
maxTokens: Math.max(...memberSpecs.map(spec => spec.maxTokens)),
|
|
358
449
|
};
|
|
359
|
-
// The default wire id is the priority member; omit when it
|
|
360
|
-
// logical id (bare/thinking pairs) — `resolveWireModelId`
|
|
361
|
-
|
|
450
|
+
// The default wire id is the highest-priority live member; omit when it
|
|
451
|
+
// equals the logical id (bare/thinking pairs) — `resolveWireModelId`
|
|
452
|
+
// falls back. Retired members never become the default.
|
|
453
|
+
const defaultWireId = rawPresent.find(id => !retired?.has(id)) ?? rawPresent[0];
|
|
454
|
+
if (defaultWireId === family.id) {
|
|
362
455
|
delete collapsed.requestModelId;
|
|
363
456
|
} else {
|
|
364
|
-
collapsed.requestModelId =
|
|
457
|
+
collapsed.requestModelId = defaultWireId as string;
|
|
365
458
|
}
|
|
366
459
|
if (reasoning) {
|
|
367
460
|
collapsed.thinking = thinking;
|