@oh-my-pi/pi-catalog 17.0.7 → 17.0.9
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 +37 -0
- package/dist/types/model-cache.d.ts +2 -0
- package/dist/types/provider-models/special.d.ts +21 -2
- package/dist/types/variant-collapse.d.ts +2 -2
- package/package.json +3 -3
- package/src/discovery/codex.ts +19 -15
- package/src/model-cache.ts +23 -3
- package/src/model-manager.ts +20 -8
- package/src/models.json +1408 -2340
- package/src/provider-models/openai-compat.ts +9 -0
- package/src/provider-models/special.ts +58 -8
- package/src/variant-collapse.ts +32 -11
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,43 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [17.0.9] - 2026-07-23
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- Renamed `codex-auto-review` model to `GPT-5.3 Codex Spark` with updated pricing and capabilities
|
|
10
|
+
- Removed image input support from GPT-5.3 Codex Spark (text-only)
|
|
11
|
+
- Reduced GPT-5.3 Codex Spark context window from 272K to 128K tokens
|
|
12
|
+
- Changed GPT-5.3 Codex Spark thinking efforts from `["minimal", "low", "medium", "high", "xhigh"]` to `["low", "medium", "high", "xhigh"]`
|
|
13
|
+
- Updated pricing for multiple AI models across providers (costs adjusted in models.json)
|
|
14
|
+
- Reduced max output tokens for an unspecified model from 16384 to 8192
|
|
15
|
+
- Added image input support to Venice AI text model
|
|
16
|
+
|
|
17
|
+
## [17.0.8] - 2026-07-22
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- Added support for several new models across multiple providers, including MiniMax M3, Gemini 3.5 Flash Lite, Gemini 3.6 Flash (with thinking support), Hy3, Doubao-Seed-Character, LongCat 2.0, Laguna S 2.1 (free and paid tiers), Qwen 3.6 35B A3B, SWE-1.6 Slow (devin agent catalog), and XiaomiMiMo/MiMo-V2.5.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- Updated Grok 4.5 API type to "openai-responses" and updated "o3-mini" to support thinking capabilities with the "kimi" thinking format.
|
|
26
|
+
- Renamed OpenRouter-specific models and routers to include "OpenRouter" in their names (e.g., "OpenRouter Auto Router (Beta)", "OpenRouter Body Builder (beta)", and "OpenRouter Pareto Code Router").
|
|
27
|
+
- Updated context window sizes, costs, and token limits for numerous models.
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- Fixed an issue where GPT-5.6 Codex SKUs lost usable context window capacity due to dynamic discovery values overwriting bundled limits.
|
|
32
|
+
- Fixed OpenAI Codex discovery dropping account-listed ChatGPT-only models (such as GPT-5.3 Codex Spark) when they are unavailable through the public API.
|
|
33
|
+
- Fixed Codex catalog discovery hiding models when multiple OAuth accounts are configured by independently fetching and merging catalogs from all accounts.
|
|
34
|
+
- Fixed cached models reusing a bundled request model (such as GitHub Copilot long-context variants) being incorrectly flagged as unrestorable and dropped after a restart.
|
|
35
|
+
- Fixed LM Studio discovery reporting a model's theoretical maximum context length instead of the actual loaded context window size of the running instance.
|
|
36
|
+
|
|
37
|
+
### Removed
|
|
38
|
+
|
|
39
|
+
- Removed several deprecated model families from the devin catalog, including Claude Fable 5, Claude Opus 4.6/4.7, Claude Sonnet 4.6/5, DeepSeek V4 Pro, Gemini 3.1 Pro, Gemini 3.5 Flash, GLM-5.2, SWE-1.6, and Nemotron 3 Ultra.
|
|
40
|
+
- Removed GPT-5 through GPT-5.3 Codex variants and GPT-5.4 nano from the openai-codex catalog.
|
|
41
|
+
|
|
5
42
|
## [17.0.6] - 2026-07-20
|
|
6
43
|
|
|
7
44
|
### Added
|
|
@@ -8,6 +8,8 @@ interface CacheEntry<TApi extends Api = Api> {
|
|
|
8
8
|
headerOmittedModelIds: readonly string[];
|
|
9
9
|
/** Header-bearing model ids that cannot be rebuilt from the static source. */
|
|
10
10
|
unrestorableHeaderModelIds: readonly string[];
|
|
11
|
+
/** Whether unrestorable markers predate request-model header matching. */
|
|
12
|
+
legacyHeaderRestoreMarkers: boolean;
|
|
11
13
|
/**
|
|
12
14
|
* Hash of the static catalog slice that was merged into `models` when this
|
|
13
15
|
* row was written. `resolveProviderModels` compares against the current
|
|
@@ -1,9 +1,28 @@
|
|
|
1
1
|
import type { DevinModelDiscoveryOptions } from "../discovery/devin.js";
|
|
2
2
|
import type { ModelManagerOptions } from "../model-manager.js";
|
|
3
3
|
import type { FetchImpl } from "../types.js";
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
/** One Codex OAuth account to fetch a catalog for. */
|
|
5
|
+
export interface OpenAICodexAccount {
|
|
6
|
+
/** OAuth access token used for `Authorization: Bearer ...`. */
|
|
7
|
+
accessToken: string;
|
|
8
|
+
/** ChatGPT account id sent as the `chatgpt-account-id` header. */
|
|
6
9
|
accountId?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface OpenAICodexModelManagerConfig {
|
|
12
|
+
/**
|
|
13
|
+
* Resolves every configured Codex OAuth account at discovery time. Codex
|
|
14
|
+
* discovery is account-scoped — a model can be available to one account and
|
|
15
|
+
* absent from another — so each account's `/models` endpoint is fetched
|
|
16
|
+
* independently and the results unioned by id. Without this, discovery would
|
|
17
|
+
* surface only the account it happened to resolve and, being authoritative,
|
|
18
|
+
* prune every model the other accounts expose (#6265).
|
|
19
|
+
*
|
|
20
|
+
* Returns `null` to abort discovery entirely (e.g. an account's credential
|
|
21
|
+
* failed to refresh): a partial account set would be cached as the complete
|
|
22
|
+
* authoritative catalog and hide the missing account's models, so the caller
|
|
23
|
+
* keeps the previous/bundled catalog instead.
|
|
24
|
+
*/
|
|
25
|
+
resolveAccounts?: () => Promise<readonly OpenAICodexAccount[] | null>;
|
|
7
26
|
clientVersion?: string;
|
|
8
27
|
fetch?: FetchImpl;
|
|
9
28
|
}
|
|
@@ -53,9 +53,9 @@ export interface EffortVariantFamily {
|
|
|
53
53
|
export interface VariantCollapseTable {
|
|
54
54
|
families: readonly EffortVariantFamily[];
|
|
55
55
|
}
|
|
56
|
-
/** `google-antigravity`
|
|
56
|
+
/** `google-antigravity` Gemini families, using each generation's native transport. */
|
|
57
57
|
export declare const ANTIGRAVITY_VARIANT_COLLAPSE_TABLE: VariantCollapseTable;
|
|
58
|
-
/** `google-gemini-cli`
|
|
58
|
+
/** `google-gemini-cli` Gemini families on the official CLI's level transport. */
|
|
59
59
|
export declare const GEMINI_CLI_VARIANT_COLLAPSE_TABLE: VariantCollapseTable;
|
|
60
60
|
export declare const DEVIN_VARIANT_COLLAPSE_TABLE: VariantCollapseTable;
|
|
61
61
|
/** Provider id → hand collapse table. The CCA providers diverge on thinking transport. */
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-catalog",
|
|
4
|
-
"version": "17.0.
|
|
4
|
+
"version": "17.0.9",
|
|
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,12 +34,12 @@
|
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@bufbuild/protobuf": "^2.12.1",
|
|
37
|
-
"@oh-my-pi/pi-utils": "17.0.
|
|
37
|
+
"@oh-my-pi/pi-utils": "17.0.9",
|
|
38
38
|
"arktype": "2.2.3",
|
|
39
39
|
"zod": "^4"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@oh-my-pi/pi-ai": "17.0.
|
|
42
|
+
"@oh-my-pi/pi-ai": "17.0.9",
|
|
43
43
|
"@types/bun": "^1.3.14"
|
|
44
44
|
},
|
|
45
45
|
"engines": {
|
package/src/discovery/codex.ts
CHANGED
|
@@ -8,11 +8,11 @@ const DEFAULT_MODEL_LIST_PATHS = ["/codex/models", "/models"] as const;
|
|
|
8
8
|
const DEFAULT_CONTEXT_WINDOW = 272_000;
|
|
9
9
|
const DEFAULT_MAX_TOKENS = 128_000;
|
|
10
10
|
/**
|
|
11
|
-
* GPT-5.6 luna/sol/terra hard context capacity. Codex
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
11
|
+
* GPT-5.6 luna/sol/terra hard context capacity. OpenAI's Codex model registry
|
|
12
|
+
* declares context_window = max_context_window = 372000 (#5705), but Codex
|
|
13
|
+
* discovery under-reports it — omitting the field for some accounts and
|
|
14
|
+
* actively returning 272000 for others (#6259). Applied as a floor for these
|
|
15
|
+
* SKUs so the reported/absent value never regresses the real window.
|
|
16
16
|
*/
|
|
17
17
|
const GPT_5_6_CONTEXT_WINDOW = 372_000;
|
|
18
18
|
const CODEX_REMOTE_COMPACTION = {
|
|
@@ -33,7 +33,7 @@ const codexModelEntrySchema = type({
|
|
|
33
33
|
"default_reasoning_level?": "unknown",
|
|
34
34
|
"supported_reasoning_levels?": "unknown",
|
|
35
35
|
"input_modalities?": "unknown",
|
|
36
|
-
"
|
|
36
|
+
"visibility?": "unknown",
|
|
37
37
|
"priority?": "unknown",
|
|
38
38
|
"prefer_websockets?": "unknown",
|
|
39
39
|
"use_responses_lite?": "unknown",
|
|
@@ -217,20 +217,24 @@ function normalizeCodexModelEntry(entry: unknown, baseUrl: string): NormalizedCo
|
|
|
217
217
|
return null;
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
-
const
|
|
221
|
-
if (
|
|
220
|
+
const visibility = toNonEmptyString(payload.visibility)?.toLowerCase();
|
|
221
|
+
if (visibility === "hide" || visibility === "hidden") {
|
|
222
222
|
return null;
|
|
223
223
|
}
|
|
224
224
|
|
|
225
225
|
const name = toNonEmptyString(payload.display_name) ?? slug;
|
|
226
|
-
//
|
|
227
|
-
//
|
|
226
|
+
// GPT-5.6 luna/sol/terra have a 372000 hard window, but Codex discovery
|
|
227
|
+
// under-reports it: for some accounts the field is omitted, for others it is
|
|
228
|
+
// actively returned as 272000 (#6259). Treat GPT_5_6_CONTEXT_WINDOW as a
|
|
229
|
+
// floor for these SKUs so neither the omission nor the active under-report
|
|
230
|
+
// regresses the real capacity; other models honor the reported value with
|
|
231
|
+
// the generic 272000 fallback.
|
|
228
232
|
const parsed = parseKnownModel(slug);
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
233
|
+
const isGpt56 = parsed.family === "openai" && semverEqual(parsed.version, "5.6");
|
|
234
|
+
const reportedContextWindow = toPositiveInt(payload.context_window);
|
|
235
|
+
const contextWindow = isGpt56
|
|
236
|
+
? Math.max(GPT_5_6_CONTEXT_WINDOW, reportedContextWindow ?? 0)
|
|
237
|
+
: (reportedContextWindow ?? DEFAULT_CONTEXT_WINDOW);
|
|
234
238
|
const maxTokens = Math.min(DEFAULT_MAX_TOKENS, contextWindow);
|
|
235
239
|
const reasoning = supportsReasoning(payload.default_reasoning_level, payload.supported_reasoning_levels);
|
|
236
240
|
const input = normalizeInputModalities(payload.input_modalities);
|
package/src/model-cache.ts
CHANGED
|
@@ -21,6 +21,7 @@ import type { Api, Model, ModelSpec } from "./types";
|
|
|
21
21
|
// effort-tier variant collapsing (raw `-low`/`-high`/`-thinking` member ids);
|
|
22
22
|
// v4 dropped the pre-efforts ThinkingConfig shape.
|
|
23
23
|
const CACHE_SCHEMA_VERSION = 10;
|
|
24
|
+
const HEADER_RESTORE_VERSION = 1;
|
|
24
25
|
|
|
25
26
|
interface CacheRow {
|
|
26
27
|
provider_id: string;
|
|
@@ -31,6 +32,7 @@ interface CacheRow {
|
|
|
31
32
|
models: string;
|
|
32
33
|
header_omitted_model_ids: string;
|
|
33
34
|
unrestorable_header_model_ids: string;
|
|
35
|
+
header_restore_version: number;
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
interface TableInfoRow {
|
|
@@ -46,6 +48,8 @@ interface CacheEntry<TApi extends Api = Api> {
|
|
|
46
48
|
headerOmittedModelIds: readonly string[];
|
|
47
49
|
/** Header-bearing model ids that cannot be rebuilt from the static source. */
|
|
48
50
|
unrestorableHeaderModelIds: readonly string[];
|
|
51
|
+
/** Whether unrestorable markers predate request-model header matching. */
|
|
52
|
+
legacyHeaderRestoreMarkers: boolean;
|
|
49
53
|
/**
|
|
50
54
|
* Hash of the static catalog slice that was merged into `models` when this
|
|
51
55
|
* row was written. `resolveProviderModels` compares against the current
|
|
@@ -77,6 +81,7 @@ function openDb(resolvedPath: string): Database {
|
|
|
77
81
|
static_fingerprint TEXT NOT NULL DEFAULT '',
|
|
78
82
|
header_omitted_model_ids TEXT NOT NULL DEFAULT '[]',
|
|
79
83
|
unrestorable_header_model_ids TEXT NOT NULL DEFAULT '[]',
|
|
84
|
+
header_restore_version INTEGER NOT NULL DEFAULT 0,
|
|
80
85
|
models TEXT NOT NULL
|
|
81
86
|
)
|
|
82
87
|
`);
|
|
@@ -121,6 +126,12 @@ function migrateCacheSchema(db: Database): void {
|
|
|
121
126
|
if (!columns.some(column => column.name === "unrestorable_header_model_ids")) {
|
|
122
127
|
db.run("ALTER TABLE model_cache ADD COLUMN unrestorable_header_model_ids TEXT NOT NULL DEFAULT '[]'");
|
|
123
128
|
}
|
|
129
|
+
if (!columns.some(column => column.name === "header_restore_version")) {
|
|
130
|
+
// Existing v10 rows get 0, distinguishing markers produced by the
|
|
131
|
+
// old id-only header matcher from rows written after request-model
|
|
132
|
+
// header matching was introduced.
|
|
133
|
+
db.run("ALTER TABLE model_cache ADD COLUMN header_restore_version INTEGER NOT NULL DEFAULT 0");
|
|
134
|
+
}
|
|
124
135
|
} finally {
|
|
125
136
|
stmt.finalize();
|
|
126
137
|
}
|
|
@@ -164,6 +175,7 @@ export function readModelCache<TApi extends Api>(
|
|
|
164
175
|
updatedAt: row.updated_at,
|
|
165
176
|
headerOmittedModelIds,
|
|
166
177
|
unrestorableHeaderModelIds,
|
|
178
|
+
legacyHeaderRestoreMarkers: row.header_restore_version < HEADER_RESTORE_VERSION,
|
|
167
179
|
staticFingerprint: row.static_fingerprint ?? "",
|
|
168
180
|
};
|
|
169
181
|
} finally {
|
|
@@ -226,7 +238,13 @@ export function writeModelCache<TApi extends Api>(
|
|
|
226
238
|
for (const model of models) {
|
|
227
239
|
if (hasModelHeaders(model)) {
|
|
228
240
|
headerOmittedModelIds.push(model.id);
|
|
229
|
-
|
|
241
|
+
// Synthesized variants (e.g. Copilot `-1m`) have no same-id static
|
|
242
|
+
// entry; their headers come from the `requestModelId` base. Match
|
|
243
|
+
// against that source too, else they are wrongly flagged
|
|
244
|
+
// unrestorable and dropped on the next offline read (#6037, #6284).
|
|
245
|
+
const staticHeaderSource =
|
|
246
|
+
staticById.get(model.id) ?? (model.requestModelId ? staticById.get(model.requestModelId) : undefined);
|
|
247
|
+
if (!headersEqual(model.headers, staticHeaderSource?.headers)) {
|
|
230
248
|
unrestorableHeaderModelIds.push(model.id);
|
|
231
249
|
}
|
|
232
250
|
}
|
|
@@ -235,8 +253,9 @@ export function writeModelCache<TApi extends Api>(
|
|
|
235
253
|
db.run(
|
|
236
254
|
`INSERT OR REPLACE INTO model_cache (
|
|
237
255
|
provider_id, version, updated_at, authoritative, static_fingerprint,
|
|
238
|
-
header_omitted_model_ids, unrestorable_header_model_ids,
|
|
239
|
-
|
|
256
|
+
header_omitted_model_ids, unrestorable_header_model_ids,
|
|
257
|
+
header_restore_version, models
|
|
258
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
240
259
|
[
|
|
241
260
|
providerId,
|
|
242
261
|
CACHE_SCHEMA_VERSION,
|
|
@@ -245,6 +264,7 @@ export function writeModelCache<TApi extends Api>(
|
|
|
245
264
|
staticFingerprint,
|
|
246
265
|
JSON.stringify(headerOmittedModelIds),
|
|
247
266
|
JSON.stringify(unrestorableHeaderModelIds),
|
|
267
|
+
HEADER_RESTORE_VERSION,
|
|
248
268
|
JSON.stringify(cachedModels),
|
|
249
269
|
],
|
|
250
270
|
);
|
package/src/model-manager.ts
CHANGED
|
@@ -108,15 +108,21 @@ interface CachedHeaderRestoreResult<TApi extends Api> {
|
|
|
108
108
|
/**
|
|
109
109
|
* Restore cache-omitted headers from the current static source.
|
|
110
110
|
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
111
|
+
* A same-id static match is trusted only when the row did not flag the model
|
|
112
|
+
* unrestorable (its live headers matched static when cached). Request-model
|
|
113
|
+
* fallback also honors that marker for current rows. Only legacy rows written
|
|
114
|
+
* before request-model header matching may bypass it: their id-only writer
|
|
115
|
+
* necessarily marked every synthesized variant unrestorable (#6037, #6284).
|
|
116
|
+
* Header-bearing models without a trusted source cannot be reconstructed
|
|
117
|
+
* safely without persisting arbitrary credential values; callers must refetch
|
|
118
|
+
* them online or omit them rather than return a broken model.
|
|
114
119
|
*/
|
|
115
120
|
function restoreCachedModelHeaders<TApi extends Api>(
|
|
116
121
|
cachedModels: readonly ModelSpec<TApi>[],
|
|
117
122
|
staticModels: readonly Model<TApi>[],
|
|
118
123
|
headerOmittedModelIds: readonly string[],
|
|
119
124
|
unrestorableHeaderModelIds: readonly string[],
|
|
125
|
+
legacyHeaderRestoreMarkers: boolean,
|
|
120
126
|
): CachedHeaderRestoreResult<TApi> {
|
|
121
127
|
const models = passModelList<TApi>(cachedModels);
|
|
122
128
|
if (headerOmittedModelIds.length === 0) {
|
|
@@ -128,11 +134,15 @@ function restoreCachedModelHeaders<TApi extends Api>(
|
|
|
128
134
|
const unresolvedModelIds = new Set<string>();
|
|
129
135
|
const restored = models.map(model => {
|
|
130
136
|
if (!omittedIds.has(model.id)) return model;
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
const staticModel =
|
|
137
|
+
const unrestorable = unrestorableIds.has(model.id);
|
|
138
|
+
// Current unrestorable markers prove that neither same-id nor request-model
|
|
139
|
+
// static headers matched the live model. Only the old id-only writer's
|
|
140
|
+
// markers may recover a synthesized variant through `requestModelId`.
|
|
141
|
+
const staticModel = unrestorable
|
|
142
|
+
? legacyHeaderRestoreMarkers && model.requestModelId
|
|
143
|
+
? staticById.get(model.requestModelId)
|
|
144
|
+
: undefined
|
|
145
|
+
: (staticById.get(model.id) ?? (model.requestModelId ? staticById.get(model.requestModelId) : undefined));
|
|
136
146
|
if (!staticModel?.headers) {
|
|
137
147
|
unresolvedModelIds.add(model.id);
|
|
138
148
|
return model;
|
|
@@ -165,6 +175,7 @@ export async function resolveProviderModels<TApi extends Api = Api, TModelsDevPa
|
|
|
165
175
|
staticModels,
|
|
166
176
|
cache?.headerOmittedModelIds ?? [],
|
|
167
177
|
cache?.unrestorableHeaderModelIds ?? [],
|
|
178
|
+
cache?.legacyHeaderRestoreMarkers ?? false,
|
|
168
179
|
);
|
|
169
180
|
const usableCachedModels = restoredCache.models.filter(model => !restoredCache.unresolvedModelIds.has(model.id));
|
|
170
181
|
const cacheHasUnresolvedHeaders = restoredCache.unresolvedModelIds.size > 0;
|
|
@@ -245,6 +256,7 @@ export async function resolveProviderModels<TApi extends Api = Api, TModelsDevPa
|
|
|
245
256
|
staticModels,
|
|
246
257
|
latestCache?.headerOmittedModelIds ?? cache?.headerOmittedModelIds ?? [],
|
|
247
258
|
latestCache?.unrestorableHeaderModelIds ?? cache?.unrestorableHeaderModelIds ?? [],
|
|
259
|
+
latestCache?.legacyHeaderRestoreMarkers ?? cache?.legacyHeaderRestoreMarkers ?? false,
|
|
248
260
|
);
|
|
249
261
|
const latestUsableCacheModels = latestRestoredCache.models.filter(
|
|
250
262
|
model => !latestRestoredCache.unresolvedModelIds.has(model.id),
|