@oh-my-pi/pi-catalog 16.0.4 → 16.0.6
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 +44 -0
- package/dist/types/compat/openai.d.ts +4 -1
- package/dist/types/discovery/antigravity.d.ts +9 -0
- package/dist/types/identity/dialect.d.ts +1 -1
- package/dist/types/identity/family.d.ts +8 -0
- package/dist/types/provider-models/descriptors.d.ts +1 -1
- package/dist/types/provider-models/openai-compat.d.ts +5 -6
- package/dist/types/types.d.ts +109 -13
- package/dist/types/variant-collapse.d.ts +4 -5
- package/dist/types/wire/gemini-headers.d.ts +16 -1
- package/dist/types/wire/github-copilot.d.ts +2 -0
- package/package.json +4 -3
- package/src/build.ts +3 -1
- package/src/compat/openai.ts +213 -19
- package/src/discovery/antigravity.ts +91 -98
- package/src/discovery/codex.ts +33 -40
- package/src/discovery/cursor.ts +31 -24
- package/src/discovery/gemini.ts +39 -30
- package/src/discovery/openai-compatible.ts +22 -32
- package/src/identity/dialect.ts +4 -1
- package/src/identity/family.ts +21 -1
- package/src/model-cache.ts +8 -6
- package/src/model-thinking.ts +24 -6
- package/src/models.json +544 -376
- package/src/provider-models/google.ts +2 -0
- package/src/provider-models/ollama.ts +11 -2
- package/src/provider-models/openai-compat.ts +47 -46
- package/src/types.ts +190 -43
- package/src/variant-collapse.ts +198 -72
- package/src/wire/gemini-headers.ts +28 -5
- package/src/wire/github-copilot.ts +18 -0
package/src/discovery/cursor.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as http2 from "node:http2";
|
|
2
2
|
import { create, fromBinary, toBinary } from "@bufbuild/protobuf";
|
|
3
|
-
import {
|
|
3
|
+
import { type } from "arktype";
|
|
4
4
|
import { getBundledModels } from "../models";
|
|
5
5
|
import { toModelSpec } from "../provider-models/bundled-references";
|
|
6
6
|
import type { Model, ModelSpec } from "../types";
|
|
@@ -13,27 +13,34 @@ const CURSOR_GET_USABLE_MODELS_PATH = "/agent.v1.AgentService/GetUsableModels";
|
|
|
13
13
|
const DEFAULT_CONTEXT_WINDOW = 200_000;
|
|
14
14
|
const DEFAULT_MAX_TOKENS = 64_000;
|
|
15
15
|
|
|
16
|
-
const OptionalDisplayNameSchema =
|
|
17
|
-
const CursorAliasesSchema =
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
16
|
+
const OptionalDisplayNameSchema = type("unknown").pipe(raw => (typeof raw === "string" ? raw : undefined));
|
|
17
|
+
const CursorAliasesSchema = type("unknown").pipe(raw => {
|
|
18
|
+
if (Array.isArray(raw)) {
|
|
19
|
+
return raw.filter((alias: unknown): alias is string => typeof alias === "string");
|
|
20
|
+
}
|
|
21
|
+
return [];
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const CursorModelDetailsSchema = type({
|
|
25
|
+
modelId: "string",
|
|
26
|
+
displayName: OptionalDisplayNameSchema.default(undefined),
|
|
27
|
+
displayNameShort: OptionalDisplayNameSchema.default(undefined),
|
|
28
|
+
displayModelId: OptionalDisplayNameSchema.default(undefined),
|
|
29
|
+
aliases: CursorAliasesSchema.default(() => []),
|
|
30
|
+
"thinkingDetails?": "unknown",
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const CursorModelsInnerSchema = type("unknown[]");
|
|
34
|
+
const ResilientCursorModelsSchema = type("unknown").pipe(raw => {
|
|
35
|
+
const out = CursorModelsInnerSchema(raw);
|
|
36
|
+
return out instanceof type.errors ? [] : out;
|
|
30
37
|
});
|
|
31
38
|
|
|
32
|
-
const CursorDecodedResponseSchema =
|
|
33
|
-
models:
|
|
39
|
+
const CursorDecodedResponseSchema = type({
|
|
40
|
+
models: ResilientCursorModelsSchema.default(() => []),
|
|
34
41
|
});
|
|
35
42
|
|
|
36
|
-
type CursorModelDetailsValue =
|
|
43
|
+
type CursorModelDetailsValue = typeof CursorModelDetailsSchema.infer;
|
|
37
44
|
|
|
38
45
|
/**
|
|
39
46
|
* Options for fetching dynamic Cursor models from `GetUsableModels`.
|
|
@@ -74,13 +81,13 @@ export async function fetchCursorUsableModels(
|
|
|
74
81
|
return null;
|
|
75
82
|
}
|
|
76
83
|
const decoded = decodeGetUsableModelsResponse(responseBuffer);
|
|
77
|
-
const parsedDecoded = CursorDecodedResponseSchema
|
|
78
|
-
if (
|
|
84
|
+
const parsedDecoded = CursorDecodedResponseSchema(decoded);
|
|
85
|
+
if (parsedDecoded instanceof type.errors) {
|
|
79
86
|
return null;
|
|
80
87
|
}
|
|
81
88
|
|
|
82
89
|
const references = createCursorReferenceMap();
|
|
83
|
-
return normalizeCursorModels(parsedDecoded.
|
|
90
|
+
return normalizeCursorModels(parsedDecoded.models, options.baseUrl, references);
|
|
84
91
|
} catch {
|
|
85
92
|
return null;
|
|
86
93
|
}
|
|
@@ -254,12 +261,12 @@ function normalizeCursorModel(
|
|
|
254
261
|
baseUrlOverride: string | undefined,
|
|
255
262
|
references: Map<string, ModelSpec<"cursor-agent">>,
|
|
256
263
|
): ModelSpec<"cursor-agent"> | null {
|
|
257
|
-
const parsedModel = CursorModelDetailsSchema
|
|
258
|
-
if (
|
|
264
|
+
const parsedModel = CursorModelDetailsSchema(model);
|
|
265
|
+
if (parsedModel instanceof type.errors) {
|
|
259
266
|
return null;
|
|
260
267
|
}
|
|
261
268
|
|
|
262
|
-
const details = parsedModel
|
|
269
|
+
const details = parsedModel;
|
|
263
270
|
const id = details.modelId.trim();
|
|
264
271
|
if (!id) {
|
|
265
272
|
return null;
|
package/src/discovery/gemini.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type } from "arktype";
|
|
2
2
|
import { getBundledModels } from "../models";
|
|
3
3
|
import { toModelSpec } from "../provider-models/bundled-references";
|
|
4
4
|
import type { FetchImpl, Model, ModelSpec } from "../types";
|
|
@@ -7,36 +7,45 @@ const GOOGLE_GENERATIVE_AI_BASE_URL = "https://generativelanguage.googleapis.com
|
|
|
7
7
|
const DEFAULT_PAGE_SIZE = 100;
|
|
8
8
|
const DEFAULT_MAX_PAGES = 25;
|
|
9
9
|
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
inputTokenLimit: z.number().finite().optional().catch(undefined),
|
|
15
|
-
outputTokenLimit: z.number().finite().optional().catch(undefined),
|
|
10
|
+
const resilientString = type("unknown").pipe(val => {
|
|
11
|
+
if (val === undefined) return undefined;
|
|
12
|
+
const out = type("string")(val);
|
|
13
|
+
return out instanceof type.errors ? undefined : out;
|
|
16
14
|
});
|
|
17
15
|
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
parsedItems.push(parsed.data);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
return parsedItems;
|
|
34
|
-
}),
|
|
35
|
-
nextPageToken: z.string().optional().catch(undefined),
|
|
16
|
+
const resilientNumber = type("unknown").pipe(val => {
|
|
17
|
+
if (val === undefined) return undefined;
|
|
18
|
+
const out = type("number")(val);
|
|
19
|
+
return out instanceof type.errors ? undefined : out;
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const geminiModelListItemSchema = type({
|
|
23
|
+
"name?": resilientString,
|
|
24
|
+
"displayName?": resilientString,
|
|
25
|
+
"supportedGenerationMethods?": "string[]",
|
|
26
|
+
"inputTokenLimit?": resilientNumber,
|
|
27
|
+
"outputTokenLimit?": resilientNumber,
|
|
36
28
|
});
|
|
37
29
|
|
|
38
|
-
type GeminiModelListItem =
|
|
30
|
+
type GeminiModelListItem = typeof geminiModelListItemSchema.infer;
|
|
39
31
|
|
|
32
|
+
const modelsSchema = type("unknown[]")
|
|
33
|
+
.pipe(items => {
|
|
34
|
+
const parsedItems: GeminiModelListItem[] = [];
|
|
35
|
+
for (const item of items) {
|
|
36
|
+
const parsed = geminiModelListItemSchema(item);
|
|
37
|
+
if (!(parsed instanceof type.errors)) {
|
|
38
|
+
parsedItems.push(parsed);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return parsedItems;
|
|
42
|
+
})
|
|
43
|
+
.default(() => []);
|
|
44
|
+
|
|
45
|
+
const geminiModelListResponseSchema = type({
|
|
46
|
+
models: modelsSchema,
|
|
47
|
+
"nextPageToken?": resilientString,
|
|
48
|
+
});
|
|
40
49
|
/**
|
|
41
50
|
* Configuration for Google Generative AI model discovery.
|
|
42
51
|
*/
|
|
@@ -103,19 +112,19 @@ export async function fetchGeminiModels(
|
|
|
103
112
|
return null;
|
|
104
113
|
}
|
|
105
114
|
|
|
106
|
-
const parsed = geminiModelListResponseSchema
|
|
107
|
-
if (
|
|
115
|
+
const parsed = geminiModelListResponseSchema(payload);
|
|
116
|
+
if (parsed instanceof type.errors) {
|
|
108
117
|
return null;
|
|
109
118
|
}
|
|
110
119
|
|
|
111
|
-
for (const item of parsed.
|
|
120
|
+
for (const item of parsed.models) {
|
|
112
121
|
const model = normalizeModel(item, baseUrl, bundledById);
|
|
113
122
|
if (model) {
|
|
114
123
|
modelsById.set(model.id, model);
|
|
115
124
|
}
|
|
116
125
|
}
|
|
117
126
|
|
|
118
|
-
const token = normalizePageToken(parsed.
|
|
127
|
+
const token = normalizePageToken(parsed.nextPageToken);
|
|
119
128
|
if (!token) {
|
|
120
129
|
break;
|
|
121
130
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type } from "arktype";
|
|
2
2
|
import type { Api, FetchImpl, ModelSpec, Provider } from "../types";
|
|
3
3
|
|
|
4
4
|
const MODELS_PATH = "/models";
|
|
@@ -32,28 +32,23 @@ export interface OpenAICompatibleModelsEnvelope {
|
|
|
32
32
|
[key: string]: unknown;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
const openAICompatibleModelRecordSchema =
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
})
|
|
42
|
-
.loose();
|
|
35
|
+
const openAICompatibleModelRecordSchema = type({
|
|
36
|
+
id: "string >= 1",
|
|
37
|
+
"name?": "string | null",
|
|
38
|
+
"object?": "unknown",
|
|
39
|
+
"owned_by?": "unknown",
|
|
40
|
+
});
|
|
43
41
|
|
|
44
|
-
const openAICompatibleModelsEnvelopeSchema =
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
})
|
|
51
|
-
.loose();
|
|
42
|
+
const openAICompatibleModelsEnvelopeSchema = type({
|
|
43
|
+
"data?": "unknown",
|
|
44
|
+
"models?": "unknown",
|
|
45
|
+
"result?": "unknown",
|
|
46
|
+
"items?": "unknown",
|
|
47
|
+
});
|
|
52
48
|
|
|
53
|
-
const openAICompatibleModelsPayloadSchema =
|
|
54
|
-
|
|
55
|
-
type ParsedOpenAICompatibleModelRecord = z.infer<typeof openAICompatibleModelRecordSchema>;
|
|
49
|
+
const openAICompatibleModelsPayloadSchema = type("unknown[]").or(openAICompatibleModelsEnvelopeSchema);
|
|
56
50
|
|
|
51
|
+
type ParsedOpenAICompatibleModelRecord = typeof openAICompatibleModelRecordSchema.infer;
|
|
57
52
|
/**
|
|
58
53
|
* Context passed to custom OpenAI-compatible model mappers.
|
|
59
54
|
*/
|
|
@@ -196,22 +191,17 @@ function extractModelEntries(payload: unknown): ParsedOpenAICompatibleModelRecor
|
|
|
196
191
|
}
|
|
197
192
|
|
|
198
193
|
function extractModelEntriesFromNode(node: unknown): ParsedOpenAICompatibleModelRecord[] | null {
|
|
199
|
-
const parsedPayload = openAICompatibleModelsPayloadSchema
|
|
200
|
-
if (
|
|
194
|
+
const parsedPayload = openAICompatibleModelsPayloadSchema(node);
|
|
195
|
+
if (parsedPayload instanceof type.errors) {
|
|
201
196
|
return null;
|
|
202
197
|
}
|
|
203
|
-
if (Array.isArray(parsedPayload
|
|
204
|
-
const parsedEntries = parsedPayload
|
|
205
|
-
.map(entry => openAICompatibleModelRecordSchema
|
|
206
|
-
.flatMap(entry => (entry.
|
|
198
|
+
if (Array.isArray(parsedPayload)) {
|
|
199
|
+
const parsedEntries = parsedPayload
|
|
200
|
+
.map(entry => openAICompatibleModelRecordSchema(entry))
|
|
201
|
+
.flatMap(entry => (entry instanceof type.errors ? [] : [entry]));
|
|
207
202
|
return parsedEntries;
|
|
208
203
|
}
|
|
209
|
-
for (const candidate of [
|
|
210
|
-
parsedPayload.data.data,
|
|
211
|
-
parsedPayload.data.models,
|
|
212
|
-
parsedPayload.data.result,
|
|
213
|
-
parsedPayload.data.items,
|
|
214
|
-
]) {
|
|
204
|
+
for (const candidate of [parsedPayload.data, parsedPayload.models, parsedPayload.result, parsedPayload.items]) {
|
|
215
205
|
if (candidate === undefined) {
|
|
216
206
|
continue;
|
|
217
207
|
}
|
package/src/identity/dialect.ts
CHANGED
|
@@ -11,7 +11,8 @@ export type Dialect =
|
|
|
11
11
|
| "pi"
|
|
12
12
|
| "qwen3"
|
|
13
13
|
| "gemini"
|
|
14
|
-
| "gemma"
|
|
14
|
+
| "gemma"
|
|
15
|
+
| "minimax";
|
|
15
16
|
|
|
16
17
|
export const FALLBACK_DIALECT: Dialect = "xml";
|
|
17
18
|
|
|
@@ -31,6 +32,8 @@ export function preferredDialect(modelId: string): Dialect {
|
|
|
31
32
|
return "qwen3";
|
|
32
33
|
case "deepseek":
|
|
33
34
|
return "deepseek";
|
|
35
|
+
case "minimax":
|
|
36
|
+
return "minimax";
|
|
34
37
|
case "openai":
|
|
35
38
|
case "gpt-oss":
|
|
36
39
|
return "harmony";
|
package/src/identity/family.ts
CHANGED
|
@@ -56,6 +56,19 @@ export function isMimoModelIdOrName(value: string): boolean {
|
|
|
56
56
|
return value.toLowerCase().includes("mimo");
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
const GROK_EFFORT_CAPABLE_PREFIXES = ["grok-3-mini", "grok-4.20-multi-agent", "grok-4.3"] as const;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Grok SKUs that expose the wire `reasoning.effort` dial. Other Grok reasoners
|
|
63
|
+
* (e.g. `grok-build`, `grok-4.20-0309-reasoning`) think natively but reject the
|
|
64
|
+
* param, so callers must omit reasoning effort for them.
|
|
65
|
+
*/
|
|
66
|
+
export function isGrokReasoningEffortCapable(modelId: string): boolean {
|
|
67
|
+
const bare = bareModelId(modelId).trim().toLowerCase();
|
|
68
|
+
if (!bare) return false;
|
|
69
|
+
return GROK_EFFORT_CAPABLE_PREFIXES.some(prefix => bare.startsWith(prefix));
|
|
70
|
+
}
|
|
71
|
+
|
|
59
72
|
/**
|
|
60
73
|
* MiniMax M2-generation family (M2, M2.1, M2.5, M2.7, including `-highspeed`/
|
|
61
74
|
* `-lightning`/`-her`/`-turbo` variants, dotless aliases like `minimax-m21`,
|
|
@@ -73,6 +86,13 @@ export function isMinimaxM2FamilyModelId(modelId: string): boolean {
|
|
|
73
86
|
return /(?:^|[/.-])m2\d*(?:[.-]\d+)?(?:[-.:_]|$)/i.test(lower);
|
|
74
87
|
}
|
|
75
88
|
|
|
89
|
+
/** MiniMax M3 family ids in bundled/default and aggregator namespace forms. */
|
|
90
|
+
export function isMinimaxM3FamilyModelId(modelId: string): boolean {
|
|
91
|
+
const lower = modelId.toLowerCase();
|
|
92
|
+
if (!lower.includes("minimax")) return false;
|
|
93
|
+
return /(?:^|[/._-])(?:minimax[/._-])?m3(?:[-.:_]|$)/i.test(lower);
|
|
94
|
+
}
|
|
95
|
+
|
|
76
96
|
/**
|
|
77
97
|
* OpenAI gpt-oss family (`gpt-oss-20b`, `gpt-oss-120b`, `gpt-oss:120b`,
|
|
78
98
|
* `vendor/gpt-oss-…`). The Harmony reasoning format only accepts
|
|
@@ -139,7 +159,7 @@ export function modelFamilyToken(modelId: string): string {
|
|
|
139
159
|
if (isOpenAIModelId(modelId)) return "openai";
|
|
140
160
|
if (isKimiModelId(modelId)) return "kimi";
|
|
141
161
|
if (isQwenModelId(modelId)) return "qwen";
|
|
142
|
-
if (isMinimaxM2FamilyModelId(modelId)) return "minimax";
|
|
162
|
+
if (isMinimaxM2FamilyModelId(modelId) || isMinimaxM3FamilyModelId(modelId)) return "minimax";
|
|
143
163
|
if (isOpenAIGptOssModelId(modelId)) return "gpt-oss";
|
|
144
164
|
if (isDeepseekModelIdOrName(modelId)) return "deepseek";
|
|
145
165
|
if (isMimoModelIdOrName(modelId)) return "mimo";
|
package/src/model-cache.ts
CHANGED
|
@@ -7,12 +7,14 @@ import { getModelDbPath } from "@oh-my-pi/pi-utils";
|
|
|
7
7
|
import type { Api, Model, ModelSpec } from "./types";
|
|
8
8
|
|
|
9
9
|
// Rows persist ModelSpec JSON (sparse `compat`, never the resolved record);
|
|
10
|
-
// the model manager rebuilds via `buildModel` on load.
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
|
|
10
|
+
// the model manager rebuilds via `buildModel` on load. v7 invalidates rows
|
|
11
|
+
// predating the Antigravity Gemini budget-mode migration (cached specs still
|
|
12
|
+
// carrying `thinking.mode: "google-level"` and the old 3.5-flash effort
|
|
13
|
+
// routing); v6 invalidates rows that may contain the retired unknown-limit
|
|
14
|
+
// sentinels (222222/8888); v5 invalidated rows predating effort-tier variant
|
|
15
|
+
// collapsing (raw `-low`/`-high`/`-thinking` member ids); v4 dropped the
|
|
16
|
+
// pre-efforts ThinkingConfig shape.
|
|
17
|
+
const CACHE_SCHEMA_VERSION = 7;
|
|
16
18
|
|
|
17
19
|
interface CacheRow {
|
|
18
20
|
provider_id: string;
|
package/src/model-thinking.ts
CHANGED
|
@@ -57,6 +57,7 @@ const GEMINI_3_FLASH_EFFORTS: readonly Effort[] = [Effort.Minimal, Effort.Low, E
|
|
|
57
57
|
const GPT_5_2_PLUS_EFFORTS: readonly Effort[] = [Effort.Low, Effort.Medium, Effort.High, Effort.XHigh];
|
|
58
58
|
const GPT_5_1_CODEX_MINI_EFFORTS: readonly Effort[] = [Effort.Medium, Effort.High];
|
|
59
59
|
const LOW_MEDIUM_HIGH_REASONING_EFFORTS: readonly Effort[] = [Effort.Low, Effort.Medium, Effort.High];
|
|
60
|
+
const GLM_52_HIGH_MAX_REASONING_EFFORTS: readonly Effort[] = [Effort.High, Effort.XHigh];
|
|
60
61
|
|
|
61
62
|
type EffortMap = Partial<Record<Effort, string>>;
|
|
62
63
|
|
|
@@ -84,6 +85,9 @@ const ZAI_GLM_52_REASONING_EFFORT_MAP: Readonly<EffortMap> = {
|
|
|
84
85
|
[Effort.High]: "high",
|
|
85
86
|
[Effort.XHigh]: "max",
|
|
86
87
|
};
|
|
88
|
+
const OLLAMA_CLOUD_GLM_52_REASONING_EFFORT_MAP: Readonly<EffortMap> = {
|
|
89
|
+
[Effort.XHigh]: "max",
|
|
90
|
+
};
|
|
87
91
|
|
|
88
92
|
/**
|
|
89
93
|
* Effort → wire-value map for the 5-tier adaptive scale (Opus 4.7+ and
|
|
@@ -221,7 +225,7 @@ export function deriveThinking<TApi extends Api>(spec: ModelSpec<TApi>, compat:
|
|
|
221
225
|
* True when the model reasons natively but rejects the wire `reasoning.effort`
|
|
222
226
|
* param. Scoped to openai-responses* because that's the only API surface where
|
|
223
227
|
* `compat.supportsReasoningEffort: false` means "omit the field entirely"
|
|
224
|
-
* (xAI Grok off the
|
|
228
|
+
* (xAI Grok off the `isGrokReasoningEffortCapable` allowlist: grok-build,
|
|
225
229
|
* grok-4.20-0309-reasoning). openai-completions keeps its thinking config even
|
|
226
230
|
* without effort support — binary thinking formats (zai/qwen) drive reasoning
|
|
227
231
|
* through other request fields.
|
|
@@ -266,11 +270,18 @@ function sameEffortList(left: readonly Effort[], right: readonly Effort[]): bool
|
|
|
266
270
|
return true;
|
|
267
271
|
}
|
|
268
272
|
|
|
273
|
+
function isOpenAICompatReasoningApi(api: Api): boolean {
|
|
274
|
+
return api === "openai-completions" || api === "openrouter";
|
|
275
|
+
}
|
|
276
|
+
|
|
269
277
|
function getModelDefinedEfforts<TApi extends Api>(spec: ModelSpec<TApi>): readonly Effort[] | undefined {
|
|
270
|
-
if (spec.api
|
|
278
|
+
if (isOpenAICompatReasoningApi(spec.api) && isZaiGlm52ReasoningEffortModel(spec)) {
|
|
271
279
|
return DEFAULT_REASONING_EFFORTS_WITH_XHIGH;
|
|
272
280
|
}
|
|
273
|
-
|
|
281
|
+
if (isOllamaCloudGlm52ReasoningEffortModel(spec)) {
|
|
282
|
+
return GLM_52_HIGH_MAX_REASONING_EFFORTS;
|
|
283
|
+
}
|
|
284
|
+
return isOpenAICompatReasoningApi(spec.api) && (isMinimaxM2FamilyModelId(spec.id) || isOpenAIGptOssModelId(spec.id))
|
|
274
285
|
? LOW_MEDIUM_HIGH_REASONING_EFFORTS
|
|
275
286
|
: undefined;
|
|
276
287
|
}
|
|
@@ -280,6 +291,10 @@ function isZaiGlm52ReasoningEffortModel<TApi extends Api>(spec: ModelSpec<TApi>)
|
|
|
280
291
|
return modelMatchesHost(spec, "zai") || modelMatchesHost(spec, "zhipu");
|
|
281
292
|
}
|
|
282
293
|
|
|
294
|
+
function isOllamaCloudGlm52ReasoningEffortModel<TApi extends Api>(spec: ModelSpec<TApi>): boolean {
|
|
295
|
+
return spec.api === "ollama-chat" && spec.provider === "ollama-cloud" && isGlm52ReasoningEffortModelId(spec.id);
|
|
296
|
+
}
|
|
297
|
+
|
|
283
298
|
function readCompatEffortMap(compat: CompatOf<Api>): EffortMap | undefined {
|
|
284
299
|
if (compat === undefined || !("reasoningEffortMap" in compat)) {
|
|
285
300
|
return undefined;
|
|
@@ -298,7 +313,10 @@ function inferDetectedEffortMap<TApi extends Api>(
|
|
|
298
313
|
? ANTHROPIC_ADAPTIVE_EFFORT_MAP_5_TIER
|
|
299
314
|
: ANTHROPIC_ADAPTIVE_EFFORT_MAP_4_TIER;
|
|
300
315
|
}
|
|
301
|
-
if (spec
|
|
316
|
+
if (isOllamaCloudGlm52ReasoningEffortModel(spec)) {
|
|
317
|
+
return OLLAMA_CLOUD_GLM_52_REASONING_EFFORT_MAP;
|
|
318
|
+
}
|
|
319
|
+
if (!isOpenAICompatReasoningApi(spec.api)) {
|
|
302
320
|
return undefined;
|
|
303
321
|
}
|
|
304
322
|
if (spec.provider === "groq" && spec.id === "qwen/qwen3-32b") {
|
|
@@ -437,7 +455,7 @@ function inferFallbackEfforts<TApi extends Api>(spec: ModelSpec<TApi>, compat: C
|
|
|
437
455
|
if (spec.api === "bedrock-converse-stream") {
|
|
438
456
|
return DEFAULT_REASONING_EFFORTS;
|
|
439
457
|
}
|
|
440
|
-
if (spec.api
|
|
458
|
+
if (isOpenAICompatReasoningApi(spec.api)) {
|
|
441
459
|
const resolved = compat as ResolvedOpenAICompat;
|
|
442
460
|
if (resolved.thinkingFormat === "openai" && resolved.supportsReasoningEffort) {
|
|
443
461
|
return DEFAULT_REASONING_EFFORTS_WITH_XHIGH;
|
|
@@ -503,7 +521,7 @@ function isOpenRouterAnthropicAdaptiveReasoningModel<TApi extends Api>(
|
|
|
503
521
|
parsedModel: AnthropicModel,
|
|
504
522
|
spec: ModelSpec<TApi>,
|
|
505
523
|
): boolean {
|
|
506
|
-
if (spec.api
|
|
524
|
+
if (!isOpenAICompatReasoningApi(spec.api)) return false;
|
|
507
525
|
if (!modelMatchesHost(spec, "openrouter")) return false;
|
|
508
526
|
return isFableOrMythos(parsedModel.kind) || (parsedModel.kind === "opus" && semverGte(parsedModel.version, "4.6"));
|
|
509
527
|
}
|