@oh-my-pi/pi-catalog 16.0.5 → 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 +20 -0
- package/dist/types/compat/openai.d.ts +3 -1
- package/dist/types/identity/family.d.ts +6 -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 +94 -17
- package/package.json +4 -3
- package/src/build.ts +3 -1
- package/src/compat/openai.ts +206 -19
- package/src/discovery/antigravity.ts +76 -92
- 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/family.ts +13 -0
- package/src/model-thinking.ts +24 -6
- package/src/models.json +504 -360
- package/src/provider-models/ollama.ts +11 -2
- package/src/provider-models/openai-compat.ts +40 -42
- package/src/types.ts +175 -48
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type } from "arktype";
|
|
2
2
|
import type { ModelSpec } from "../types";
|
|
3
3
|
import { toPositiveNumber } from "../utils";
|
|
4
4
|
import {
|
|
@@ -56,94 +56,78 @@ export interface AntigravityDiscoveryApiResponse {
|
|
|
56
56
|
models?: Record<string, AntigravityDiscoveryApiModel>;
|
|
57
57
|
agentModelSorts?: AntigravityDiscoveryAgentModelSort[];
|
|
58
58
|
}
|
|
59
|
-
const AntigravityDiscoveryApiModelSchema
|
|
60
|
-
.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
),
|
|
132
|
-
agentModelSorts: z.preprocess(
|
|
133
|
-
value => (Array.isArray(value) ? value : undefined),
|
|
134
|
-
z
|
|
135
|
-
.array(z.unknown())
|
|
136
|
-
.transform(sorts =>
|
|
137
|
-
sorts.flatMap(sort => {
|
|
138
|
-
const parsedSort = AntigravityDiscoveryAgentModelSortSchema.safeParse(sort);
|
|
139
|
-
return parsedSort.success ? [parsedSort.data] : [];
|
|
140
|
-
}),
|
|
141
|
-
)
|
|
142
|
-
.optional(),
|
|
143
|
-
),
|
|
144
|
-
})
|
|
145
|
-
.loose();
|
|
146
|
-
|
|
59
|
+
const AntigravityDiscoveryApiModelSchema = type({
|
|
60
|
+
"displayName?": type("unknown").pipe(value => (typeof value === "string" ? value : undefined)),
|
|
61
|
+
"supportsImages?": type("unknown").pipe(value => (typeof value === "boolean" ? value : undefined)),
|
|
62
|
+
"supportsThinking?": type("unknown").pipe(value => (typeof value === "boolean" ? value : undefined)),
|
|
63
|
+
"thinkingBudget?": type("unknown").pipe(value =>
|
|
64
|
+
typeof value === "number" && Number.isFinite(value) ? value : undefined,
|
|
65
|
+
),
|
|
66
|
+
"recommended?": type("unknown").pipe(value => (typeof value === "boolean" ? value : undefined)),
|
|
67
|
+
"maxTokens?": type("unknown").pipe(value =>
|
|
68
|
+
typeof value === "number" && Number.isFinite(value) ? value : undefined,
|
|
69
|
+
),
|
|
70
|
+
"maxOutputTokens?": type("unknown").pipe(value =>
|
|
71
|
+
typeof value === "number" && Number.isFinite(value) ? value : undefined,
|
|
72
|
+
),
|
|
73
|
+
"model?": type("unknown").pipe(value => (typeof value === "string" ? value : undefined)),
|
|
74
|
+
"apiProvider?": type("unknown").pipe(value => (typeof value === "string" ? value : undefined)),
|
|
75
|
+
"modelProvider?": type("unknown").pipe(value => (typeof value === "string" ? value : undefined)),
|
|
76
|
+
"isInternal?": type("unknown").pipe(value => (typeof value === "boolean" ? value : undefined)),
|
|
77
|
+
"supportsVideo?": type("unknown").pipe(value => (typeof value === "boolean" ? value : undefined)),
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const AntigravityDiscoveryAgentModelGroupSchema = type({
|
|
81
|
+
"modelIds?": type("unknown").pipe(value =>
|
|
82
|
+
Array.isArray(value) ? value.filter((modelId): modelId is string => typeof modelId === "string") : undefined,
|
|
83
|
+
),
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const AntigravityDiscoveryAgentModelSortSchema = type({
|
|
87
|
+
"groups?": type("unknown").pipe(value => {
|
|
88
|
+
if (!Array.isArray(value)) return undefined;
|
|
89
|
+
const result: AntigravityDiscoveryAgentModelGroup[] = [];
|
|
90
|
+
for (const group of value) {
|
|
91
|
+
const parsedGroup = AntigravityDiscoveryAgentModelGroupSchema(group);
|
|
92
|
+
if (!(parsedGroup instanceof type.errors)) {
|
|
93
|
+
result.push(parsedGroup);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return result;
|
|
97
|
+
}),
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const AntigravityDiscoveryApiResponseSchema = type({
|
|
101
|
+
"models?": type("unknown").pipe(value => {
|
|
102
|
+
if (typeof value !== "object" || value === null) {
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
const normalized: Record<string, AntigravityDiscoveryApiModel> = {};
|
|
106
|
+
for (const [modelId, modelValue] of Object.entries(value)) {
|
|
107
|
+
if (typeof modelValue !== "object" || modelValue === null) {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const parsedModel = AntigravityDiscoveryApiModelSchema(modelValue);
|
|
111
|
+
if (!(parsedModel instanceof type.errors)) {
|
|
112
|
+
normalized[modelId] = parsedModel;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return normalized;
|
|
116
|
+
}),
|
|
117
|
+
"agentModelSorts?": type("unknown").pipe(value => {
|
|
118
|
+
if (!Array.isArray(value)) {
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
const result: AntigravityDiscoveryAgentModelSort[] = [];
|
|
122
|
+
for (const sort of value) {
|
|
123
|
+
const parsedSort = AntigravityDiscoveryAgentModelSortSchema(sort);
|
|
124
|
+
if (!(parsedSort instanceof type.errors)) {
|
|
125
|
+
result.push(parsedSort);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return result;
|
|
129
|
+
}),
|
|
130
|
+
});
|
|
147
131
|
/**
|
|
148
132
|
* Options for fetching Antigravity discovery models.
|
|
149
133
|
*/
|
|
@@ -257,11 +241,11 @@ export async function fetchAntigravityDiscoveryModels(
|
|
|
257
241
|
}
|
|
258
242
|
|
|
259
243
|
function parseAntigravityDiscoveryResponse(value: unknown): AntigravityDiscoveryApiResponse | null {
|
|
260
|
-
const parsed = AntigravityDiscoveryApiResponseSchema
|
|
261
|
-
if (
|
|
244
|
+
const parsed = AntigravityDiscoveryApiResponseSchema(value);
|
|
245
|
+
if (parsed instanceof type.errors) {
|
|
262
246
|
return null;
|
|
263
247
|
}
|
|
264
|
-
return parsed
|
|
248
|
+
return parsed;
|
|
265
249
|
}
|
|
266
250
|
|
|
267
251
|
function trimTrailingSlashes(value: string): string {
|
package/src/discovery/codex.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type } from "arktype";
|
|
2
2
|
import type { ModelSpec } from "../types";
|
|
3
3
|
import { isRecord } from "../utils";
|
|
4
4
|
import { CODEX_BASE_URL, OPENAI_HEADER_VALUES, OPENAI_HEADERS } from "../wire/codex";
|
|
@@ -9,36 +9,29 @@ const DEFAULT_MAX_TOKENS = 128_000;
|
|
|
9
9
|
const DEFAULT_CODEX_CLIENT_VERSION = "0.99.0";
|
|
10
10
|
const NPM_CODEX_LATEST_URL = "https://registry.npmjs.org/@openai%2Fcodex/latest";
|
|
11
11
|
|
|
12
|
-
const codexReasoningPresetSchema =
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
models: z.array(z.unknown()).optional(),
|
|
36
|
-
data: z.array(z.unknown()).optional(),
|
|
37
|
-
})
|
|
38
|
-
.loose();
|
|
39
|
-
|
|
40
|
-
type CodexModelEntry = z.infer<typeof codexModelEntrySchema>;
|
|
41
|
-
|
|
12
|
+
const codexReasoningPresetSchema = type({
|
|
13
|
+
"effort?": "unknown",
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const codexModelEntrySchema = type({
|
|
17
|
+
"slug?": "unknown",
|
|
18
|
+
"id?": "unknown",
|
|
19
|
+
"display_name?": "unknown",
|
|
20
|
+
"context_window?": "unknown",
|
|
21
|
+
"default_reasoning_level?": "unknown",
|
|
22
|
+
"supported_reasoning_levels?": "unknown",
|
|
23
|
+
"input_modalities?": "unknown",
|
|
24
|
+
"supported_in_api?": "unknown",
|
|
25
|
+
"priority?": "unknown",
|
|
26
|
+
"prefer_websockets?": "unknown",
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const codexModelsResponseSchema = type({
|
|
30
|
+
"models?": "unknown[]",
|
|
31
|
+
"data?": "unknown[]",
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
type CodexModelEntry = typeof codexModelEntrySchema.infer;
|
|
42
35
|
interface NormalizedCodexModel {
|
|
43
36
|
model: ModelSpec<"openai-codex-responses">;
|
|
44
37
|
priority: number;
|
|
@@ -216,12 +209,12 @@ function isAbortError(error: unknown): error is Error {
|
|
|
216
209
|
}
|
|
217
210
|
|
|
218
211
|
function normalizeCodexModels(payload: unknown, baseUrl: string): ModelSpec<"openai-codex-responses">[] | null {
|
|
219
|
-
const parsedResponse = codexModelsResponseSchema
|
|
220
|
-
if (
|
|
212
|
+
const parsedResponse = codexModelsResponseSchema(payload);
|
|
213
|
+
if (parsedResponse instanceof type.errors) {
|
|
221
214
|
return null;
|
|
222
215
|
}
|
|
223
216
|
|
|
224
|
-
const entries = parsedResponse.
|
|
217
|
+
const entries = parsedResponse.models ?? parsedResponse.data ?? [];
|
|
225
218
|
const normalized: NormalizedCodexModel[] = [];
|
|
226
219
|
for (const entry of entries) {
|
|
227
220
|
const model = normalizeCodexModelEntry(entry, baseUrl);
|
|
@@ -241,12 +234,12 @@ function normalizeCodexModels(payload: unknown, baseUrl: string): ModelSpec<"ope
|
|
|
241
234
|
}
|
|
242
235
|
|
|
243
236
|
function normalizeCodexModelEntry(entry: unknown, baseUrl: string): NormalizedCodexModel | null {
|
|
244
|
-
const parsedEntry = codexModelEntrySchema
|
|
245
|
-
if (
|
|
237
|
+
const parsedEntry = codexModelEntrySchema(entry);
|
|
238
|
+
if (parsedEntry instanceof type.errors) {
|
|
246
239
|
return null;
|
|
247
240
|
}
|
|
248
241
|
|
|
249
|
-
const payload: CodexModelEntry = parsedEntry
|
|
242
|
+
const payload: CodexModelEntry = parsedEntry;
|
|
250
243
|
const slug = toNonEmptyString(payload.slug) ?? toNonEmptyString(payload.id);
|
|
251
244
|
if (!slug) {
|
|
252
245
|
return null;
|
|
@@ -295,11 +288,11 @@ function supportsReasoning(defaultReasoningLevel: unknown, supportedReasoningLev
|
|
|
295
288
|
}
|
|
296
289
|
|
|
297
290
|
for (const level of supportedReasoningLevels) {
|
|
298
|
-
const parsedLevel = codexReasoningPresetSchema
|
|
299
|
-
if (
|
|
291
|
+
const parsedLevel = codexReasoningPresetSchema(level);
|
|
292
|
+
if (parsedLevel instanceof type.errors) {
|
|
300
293
|
continue;
|
|
301
294
|
}
|
|
302
|
-
const effort = toNonEmptyString(parsedLevel.
|
|
295
|
+
const effort = toNonEmptyString(parsedLevel.effort)?.toLowerCase();
|
|
303
296
|
if (effort && effort !== "none") {
|
|
304
297
|
return true;
|
|
305
298
|
}
|
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/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`,
|