@dbx-tools/model 0.9.166 → 0.9.173
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/README.md +4 -1
- package/index.ts +4 -4
- package/lib/index.d.ts +4 -4
- package/lib/index.js +4 -4
- package/lib/scripts/generate-metadata.js +15 -3
- package/lib/src/_metadata-generator.d.ts +5 -1
- package/lib/src/_metadata-generator.js +168 -1
- package/lib/src/_ranking.js +9 -3
- package/lib/src/generated/model-capabilities.json +5 -3
- package/lib/src/generated/model-rate-limits.json +76 -71
- package/lib/src/generated/model-reasoning.json +24 -1
- package/lib/src/generated/modelparams-reasoning.json +477 -0
- package/lib/src/generated/models-dev-capabilities.json +3622 -0
- package/lib/src/generated/retired-models.json +3 -1
- package/lib/src/metadata-contract.d.ts +27 -0
- package/lib/src/metadata-contract.js +5 -1
- package/lib/src/metadata.d.ts +17 -9
- package/lib/src/metadata.js +147 -27
- package/lib/src/reasoning-translation.d.ts +8 -6
- package/lib/src/reasoning-translation.js +17 -13
- package/package.json +9 -8
- package/src/_metadata-generator.ts +181 -0
- package/src/_ranking.ts +12 -2
- package/src/generated/model-capabilities.json +5 -3
- package/src/generated/model-rate-limits.json +76 -71
- package/src/generated/model-reasoning.json +24 -1
- package/src/generated/modelparams-reasoning.json +477 -0
- package/src/generated/models-dev-capabilities.json +3622 -0
- package/src/generated/retired-models.json +3 -1
- package/src/metadata-contract.ts +33 -0
- package/src/metadata.ts +204 -33
- package/src/reasoning-translation.ts +16 -12
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generatedAt":
|
|
2
|
+
"generatedAt": 1793283170,
|
|
3
3
|
"models": [
|
|
4
4
|
"Anthropic Claude 3.7 Sonnet",
|
|
5
5
|
"Anthropic Claude Sonnet 4",
|
|
6
6
|
"DBRX",
|
|
7
7
|
"DBRX Instruct",
|
|
8
|
+
"DeepSeek V4 Flash (0731)",
|
|
8
9
|
"DeepSeek V4 Pro (0813)",
|
|
10
|
+
"DeepSeek V4.1 Flash",
|
|
9
11
|
"Gemini 2.5 Flash",
|
|
10
12
|
"Gemini 2.5 Pro",
|
|
11
13
|
"Gemini 3 Pro",
|
package/src/metadata-contract.ts
CHANGED
|
@@ -23,6 +23,12 @@ export const MODEL_RATE_LIMITS_URL =
|
|
|
23
23
|
export const QUERY_REASON_MODELS_URL =
|
|
24
24
|
"https://docs.databricks.com/aws/en/machine-learning/model-serving/query-reason-models";
|
|
25
25
|
|
|
26
|
+
/** models.dev provider catalogue URL. */
|
|
27
|
+
export const MODELS_DEV_API_URL = "https://models.dev/api.json";
|
|
28
|
+
|
|
29
|
+
/** modelparams.dev normalized model-parameter catalogue URL. */
|
|
30
|
+
export const MODELPARAMS_DEV_MODELS_URL = "https://modelparams.dev/api/v1/models.json";
|
|
31
|
+
|
|
26
32
|
/** Build-generated retired-model snapshot. */
|
|
27
33
|
export interface RetiredModelsSnapshot {
|
|
28
34
|
readonly generatedAt: number;
|
|
@@ -64,3 +70,30 @@ export interface ReasoningModelsSnapshot {
|
|
|
64
70
|
readonly generatedAt: number;
|
|
65
71
|
readonly catalogue: ReasoningModelCatalogue;
|
|
66
72
|
}
|
|
73
|
+
|
|
74
|
+
/** Portable capability sets normalized from models.dev. */
|
|
75
|
+
export interface ModelsDevCapabilityCatalogue {
|
|
76
|
+
readonly imageInput: readonly string[];
|
|
77
|
+
readonly reasoning: readonly string[];
|
|
78
|
+
readonly tools: readonly string[];
|
|
79
|
+
readonly structuredOutput: readonly string[];
|
|
80
|
+
readonly temperatureSupported: readonly string[];
|
|
81
|
+
readonly temperatureUnsupported: readonly string[];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Build-generated models.dev capability and reasoning snapshot. */
|
|
85
|
+
export interface ModelsDevSnapshot {
|
|
86
|
+
readonly generatedAt: number;
|
|
87
|
+
readonly capabilities: ModelsDevCapabilityCatalogue;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Reasoning support and discrete effort levels normalized from modelparams.dev. */
|
|
91
|
+
export interface ModelParamsCatalogue extends ReasoningModelCatalogue {
|
|
92
|
+
readonly reasoning: readonly string[];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Build-generated modelparams.dev reasoning snapshot. */
|
|
96
|
+
export interface ModelParamsSnapshot {
|
|
97
|
+
readonly generatedAt: number;
|
|
98
|
+
readonly catalogue: ModelParamsCatalogue;
|
|
99
|
+
}
|
package/src/metadata.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Build-generated Databricks model
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Build-generated Databricks and public model capability metadata, plus a
|
|
3
|
+
* daily write-through cache for refreshed sources and error-learned reasoning
|
|
4
|
+
* ladders.
|
|
5
5
|
*
|
|
6
6
|
* Committed snapshots seed every process. {@link refreshModelMetadata} pulls
|
|
7
|
-
* documentation through the shared
|
|
8
|
-
* rebuilds the in-memory index.
|
|
9
|
-
*
|
|
10
|
-
* shared model project cache.
|
|
7
|
+
* Databricks documentation, models.dev, and modelparams.dev through the shared
|
|
8
|
+
* memory-first {@link createMetadataCache} and rebuilds the in-memory index.
|
|
9
|
+
* Error-learned efforts use the same store via {@link rememberReasoningLevels}.
|
|
10
|
+
* Known-good values are written behind to the shared model project cache.
|
|
11
11
|
*
|
|
12
12
|
* @module
|
|
13
13
|
*/
|
|
@@ -20,12 +20,16 @@ import type {
|
|
|
20
20
|
} from "@dbx-tools/shared-model";
|
|
21
21
|
|
|
22
22
|
import {
|
|
23
|
+
parseModelParams,
|
|
23
24
|
parseModelCapabilities,
|
|
24
25
|
parseModelRateLimits,
|
|
26
|
+
parseModelsDev,
|
|
25
27
|
parseReasoningModels,
|
|
26
28
|
parseRetiredModels,
|
|
27
29
|
} from "./_metadata-generator.ts";
|
|
28
30
|
import capabilitiesSnapshotJson from "./generated/model-capabilities.json" with { type: "json" };
|
|
31
|
+
import modelParamsSnapshotJson from "./generated/modelparams-reasoning.json" with { type: "json" };
|
|
32
|
+
import modelsDevSnapshotJson from "./generated/models-dev-capabilities.json" with { type: "json" };
|
|
29
33
|
import rateLimitsSnapshotJson from "./generated/model-rate-limits.json" with { type: "json" };
|
|
30
34
|
import reasoningSnapshotJson from "./generated/model-reasoning.json" with { type: "json" };
|
|
31
35
|
import {
|
|
@@ -35,17 +39,29 @@ import {
|
|
|
35
39
|
type MetadataCache,
|
|
36
40
|
} from "./metadata-cache.ts";
|
|
37
41
|
import {
|
|
42
|
+
MODELPARAMS_DEV_MODELS_URL,
|
|
43
|
+
MODELS_DEV_API_URL,
|
|
38
44
|
MODEL_RATE_LIMITS_URL,
|
|
39
45
|
OPENAI_RESPONSES_MODELS_URL,
|
|
40
46
|
QUERY_REASON_MODELS_URL,
|
|
41
47
|
RETIRED_MODELS_URL,
|
|
42
48
|
WEB_SEARCH_MODELS_URL,
|
|
49
|
+
type ModelParamsCatalogue,
|
|
50
|
+
type ModelParamsSnapshot,
|
|
43
51
|
type ModelCapabilitiesSnapshot,
|
|
44
52
|
type ModelCapabilityCatalogue,
|
|
45
53
|
type ModelRateLimitsSnapshot,
|
|
54
|
+
type ModelsDevCapabilityCatalogue,
|
|
55
|
+
type ModelsDevSnapshot,
|
|
46
56
|
type ReasoningModelsSnapshot,
|
|
47
57
|
} from "./metadata-contract.ts";
|
|
48
|
-
import {
|
|
58
|
+
import {
|
|
59
|
+
inheritsNativeWebSearch,
|
|
60
|
+
isFoundationModelIdentity,
|
|
61
|
+
modelReasoningEfforts,
|
|
62
|
+
modelSearchQuery,
|
|
63
|
+
modelSupportsTools,
|
|
64
|
+
} from "./policy.ts";
|
|
49
65
|
import {
|
|
50
66
|
defaultReasoningLevels,
|
|
51
67
|
documentedReasoningLevels,
|
|
@@ -63,19 +79,25 @@ import {
|
|
|
63
79
|
} from "./retirement.ts";
|
|
64
80
|
|
|
65
81
|
const capabilitiesSnapshot = capabilitiesSnapshotJson as ModelCapabilitiesSnapshot;
|
|
82
|
+
const modelParamsSnapshot = modelParamsSnapshotJson as ModelParamsSnapshot;
|
|
83
|
+
const modelsDevSnapshot = modelsDevSnapshotJson as ModelsDevSnapshot;
|
|
66
84
|
const rateLimitsSnapshot = rateLimitsSnapshotJson as ModelRateLimitsSnapshot;
|
|
67
85
|
const reasoningSnapshot = reasoningSnapshotJson as ReasoningModelsSnapshot;
|
|
68
86
|
|
|
69
87
|
interface MetadataIndex {
|
|
70
88
|
readonly capabilities: {
|
|
71
|
-
readonly [Key in keyof
|
|
89
|
+
readonly [Key in keyof ModelCapabilities]: ReadonlySet<string>;
|
|
72
90
|
};
|
|
73
91
|
readonly rateLimits: ReadonlyMap<string, ModelRateLimits>;
|
|
74
92
|
readonly reasoning: ReasoningModelCatalogue;
|
|
93
|
+
readonly reasoningDocs: ReasoningModelCatalogue;
|
|
94
|
+
readonly modelParamsReasoning: ReasoningModelCatalogue;
|
|
75
95
|
readonly learnedReasoning: ReasoningModelCatalogue;
|
|
96
|
+
readonly temperatureUnsupported: ReadonlySet<string>;
|
|
76
97
|
}
|
|
77
98
|
|
|
78
99
|
type CapabilitySets = ModelCapabilityCatalogue;
|
|
100
|
+
type ModelsDevData = ModelsDevCapabilityCatalogue;
|
|
79
101
|
|
|
80
102
|
const USER_AGENT = "dbx-tools-model-metadata/1";
|
|
81
103
|
|
|
@@ -136,6 +158,35 @@ const reasoningDocsCache = createMetadataCache<Readonly<Record<string, readonly
|
|
|
136
158
|
merge: mergePreferFreshRecord,
|
|
137
159
|
});
|
|
138
160
|
|
|
161
|
+
const modelsDevCache = createMetadataCache<ModelsDevData>({
|
|
162
|
+
key: "models-dev-capabilities",
|
|
163
|
+
fallback: modelsDevSnapshot.capabilities,
|
|
164
|
+
load: async () => {
|
|
165
|
+
const snapshot = parseModelsDev(await loadJson(MODELS_DEV_API_URL), nowSeconds());
|
|
166
|
+
return snapshot.capabilities;
|
|
167
|
+
},
|
|
168
|
+
merge: mergeModelsDevData,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
const modelParamsCache = createMetadataCache<ModelParamsCatalogue>({
|
|
172
|
+
key: "modelparams-reasoning",
|
|
173
|
+
fallback: modelParamsSnapshot.catalogue,
|
|
174
|
+
load: async () =>
|
|
175
|
+
parseModelParams(await loadJson(MODELPARAMS_DEV_MODELS_URL), nowSeconds()).catalogue,
|
|
176
|
+
merge: (input) => ({
|
|
177
|
+
reasoning: mergePreferFreshList({
|
|
178
|
+
fresh: input.fresh?.reasoning,
|
|
179
|
+
previous: input.previous?.reasoning,
|
|
180
|
+
fallback: input.fallback.reasoning,
|
|
181
|
+
}),
|
|
182
|
+
models: mergePreferFreshRecord({
|
|
183
|
+
fresh: input.fresh?.models,
|
|
184
|
+
previous: input.previous?.models,
|
|
185
|
+
fallback: input.fallback.models,
|
|
186
|
+
}),
|
|
187
|
+
}),
|
|
188
|
+
});
|
|
189
|
+
|
|
139
190
|
const learnedReasoningCache = createMetadataCache<
|
|
140
191
|
Readonly<Record<string, readonly ReasoningLevel[]>>
|
|
141
192
|
>({
|
|
@@ -146,12 +197,18 @@ const learnedReasoningCache = createMetadataCache<
|
|
|
146
197
|
|
|
147
198
|
let liveIndex: MetadataIndex | undefined;
|
|
148
199
|
|
|
149
|
-
const
|
|
200
|
+
const committedMetadataIndex = memoize<MetadataIndex>(buildIndexFromCommitted);
|
|
201
|
+
|
|
202
|
+
function metadataIndex(): MetadataIndex {
|
|
203
|
+
return liveIndex ?? committedMetadataIndex();
|
|
204
|
+
}
|
|
150
205
|
|
|
151
206
|
function buildIndexFromCommitted(): MetadataIndex {
|
|
152
207
|
return buildIndex({
|
|
153
208
|
retiredNames: COMMITTED_RETIRED_MODELS,
|
|
154
209
|
capabilities: capabilitiesSnapshot.capabilities,
|
|
210
|
+
modelsDev: modelsDevSnapshot.capabilities,
|
|
211
|
+
modelParams: modelParamsSnapshot.catalogue,
|
|
155
212
|
rateLimits: rateLimitsSnapshot.catalogue.models,
|
|
156
213
|
reasoning: reasoningSnapshot.catalogue.models,
|
|
157
214
|
learnedReasoning: {},
|
|
@@ -161,21 +218,41 @@ function buildIndexFromCommitted(): MetadataIndex {
|
|
|
161
218
|
function buildIndex(input: {
|
|
162
219
|
readonly retiredNames: readonly string[];
|
|
163
220
|
readonly capabilities: CapabilitySets;
|
|
221
|
+
readonly modelsDev: ModelsDevData;
|
|
222
|
+
readonly modelParams: ModelParamsCatalogue;
|
|
164
223
|
readonly rateLimits: Readonly<Record<string, ModelRateLimits>>;
|
|
165
224
|
readonly reasoning: Readonly<Record<string, readonly string[]>>;
|
|
166
225
|
readonly learnedReasoning: Readonly<Record<string, readonly ReasoningLevel[]>>;
|
|
167
226
|
}): MetadataIndex {
|
|
168
227
|
replaceRetiredModelNames(input.retiredNames);
|
|
228
|
+
const reasoningDocs = freezeReasoningCatalogue(input.reasoning);
|
|
229
|
+
const modelParamsReasoning = freezeReasoningCatalogue(input.modelParams.models);
|
|
169
230
|
return {
|
|
170
231
|
capabilities: {
|
|
171
232
|
responses: new Set(input.capabilities.responses),
|
|
172
|
-
imageInput: new Set(
|
|
233
|
+
imageInput: new Set([
|
|
234
|
+
...input.capabilities.imageInput,
|
|
235
|
+
...input.modelsDev.imageInput,
|
|
236
|
+
]),
|
|
173
237
|
applyPatch: new Set(input.capabilities.applyPatch),
|
|
174
238
|
webSearch: new Set(input.capabilities.webSearch),
|
|
239
|
+
reasoning: new Set([
|
|
240
|
+
...input.modelsDev.reasoning,
|
|
241
|
+
...input.modelParams.reasoning,
|
|
242
|
+
]),
|
|
243
|
+
tools: new Set(input.modelsDev.tools),
|
|
244
|
+
structuredOutput: new Set(input.modelsDev.structuredOutput),
|
|
245
|
+
temperature: new Set(input.modelsDev.temperatureSupported),
|
|
175
246
|
},
|
|
176
247
|
rateLimits: new Map(Object.entries(input.rateLimits)),
|
|
177
|
-
reasoning:
|
|
248
|
+
reasoning: Object.freeze({
|
|
249
|
+
...modelParamsReasoning,
|
|
250
|
+
...reasoningDocs,
|
|
251
|
+
}),
|
|
252
|
+
reasoningDocs,
|
|
253
|
+
modelParamsReasoning,
|
|
178
254
|
learnedReasoning: freezeLearnedCatalogue(input.learnedReasoning),
|
|
255
|
+
temperatureUnsupported: new Set(input.modelsDev.temperatureUnsupported),
|
|
179
256
|
};
|
|
180
257
|
}
|
|
181
258
|
|
|
@@ -217,18 +294,28 @@ let refreshInflight: Promise<MetadataIndex> | undefined;
|
|
|
217
294
|
export async function refreshModelMetadata(): Promise<MetadataIndex> {
|
|
218
295
|
if (refreshInflight) return refreshInflight;
|
|
219
296
|
refreshInflight = (async () => {
|
|
220
|
-
const [
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
297
|
+
const [
|
|
298
|
+
retiredNames,
|
|
299
|
+
capabilities,
|
|
300
|
+
modelsDev,
|
|
301
|
+
modelParams,
|
|
302
|
+
rateLimits,
|
|
303
|
+
reasoning,
|
|
304
|
+
learnedReasoning,
|
|
305
|
+
] = await Promise.all([
|
|
306
|
+
retiredCache.get(),
|
|
307
|
+
capabilitiesCache.get(),
|
|
308
|
+
modelsDevCache.get(),
|
|
309
|
+
modelParamsCache.get(),
|
|
310
|
+
rateLimitsCache.get(),
|
|
311
|
+
reasoningDocsCache.get(),
|
|
312
|
+
learnedReasoningCache.get(),
|
|
313
|
+
]);
|
|
229
314
|
liveIndex = buildIndex({
|
|
230
315
|
retiredNames,
|
|
231
316
|
capabilities,
|
|
317
|
+
modelsDev,
|
|
318
|
+
modelParams,
|
|
232
319
|
rateLimits,
|
|
233
320
|
reasoning,
|
|
234
321
|
learnedReasoning,
|
|
@@ -242,9 +329,19 @@ export async function refreshModelMetadata(): Promise<MetadataIndex> {
|
|
|
242
329
|
|
|
243
330
|
/** Load shared cache entries without forcing a network refresh. */
|
|
244
331
|
export async function hydrateModelMetadata(): Promise<MetadataIndex> {
|
|
245
|
-
const [
|
|
332
|
+
const [
|
|
333
|
+
retiredNames,
|
|
334
|
+
capabilities,
|
|
335
|
+
modelsDev,
|
|
336
|
+
modelParams,
|
|
337
|
+
rateLimits,
|
|
338
|
+
reasoning,
|
|
339
|
+
learnedReasoning,
|
|
340
|
+
] = await Promise.all([
|
|
246
341
|
peekOrFallback(retiredCache, COMMITTED_RETIRED_MODELS),
|
|
247
342
|
peekOrFallback(capabilitiesCache, capabilitiesSnapshot.capabilities),
|
|
343
|
+
peekOrFallback(modelsDevCache, modelsDevSnapshot.capabilities),
|
|
344
|
+
peekOrFallback(modelParamsCache, modelParamsSnapshot.catalogue),
|
|
248
345
|
peekOrFallback(rateLimitsCache, rateLimitsSnapshot.catalogue.models),
|
|
249
346
|
peekOrFallback(reasoningDocsCache, reasoningSnapshot.catalogue.models),
|
|
250
347
|
peekOrFallback(learnedReasoningCache, {}),
|
|
@@ -252,6 +349,8 @@ export async function hydrateModelMetadata(): Promise<MetadataIndex> {
|
|
|
252
349
|
liveIndex = buildIndex({
|
|
253
350
|
retiredNames,
|
|
254
351
|
capabilities,
|
|
352
|
+
modelsDev,
|
|
353
|
+
modelParams,
|
|
255
354
|
rateLimits,
|
|
256
355
|
reasoning,
|
|
257
356
|
learnedReasoning,
|
|
@@ -305,17 +404,21 @@ export async function rememberReasoningLevels(
|
|
|
305
404
|
* Parse an upstream error body and persist the supported ladder for `model`.
|
|
306
405
|
*
|
|
307
406
|
* Accepts the same nested Databricks envelopes as {@link parseReasoningLevels}.
|
|
308
|
-
* Returns
|
|
309
|
-
*
|
|
407
|
+
* Returns a published ladder without parsing or storing error-derived levels
|
|
408
|
+
* when one exists. Otherwise returns the levels that were stored, or the
|
|
409
|
+
* current fallback when parsing yields nothing. The raw error body is never
|
|
410
|
+
* cached.
|
|
310
411
|
*/
|
|
311
412
|
export async function learnReasoningLevelsFromError(
|
|
312
413
|
model: string,
|
|
313
414
|
body: unknown,
|
|
314
415
|
): Promise<ReasoningLevel[]> {
|
|
416
|
+
const published = publishedReasoningLevelsFor(model, metadataIndex());
|
|
417
|
+
if (published) return published;
|
|
315
418
|
return rememberReasoningLevels(model, parseReasoningLevels(body));
|
|
316
419
|
}
|
|
317
420
|
|
|
318
|
-
/** Resolve
|
|
421
|
+
/** Resolve published model capabilities from a name or endpoint summary. */
|
|
319
422
|
export function modelCapabilitiesFor(model: string | ServingEndpointSummary): ModelCapabilities {
|
|
320
423
|
const capabilities = metadataIndex().capabilities;
|
|
321
424
|
const identities = modelIdentities(model, false);
|
|
@@ -332,9 +435,27 @@ export function modelCapabilitiesFor(model: string | ServingEndpointSummary): Mo
|
|
|
332
435
|
isFoundationModelIdentity(identity) &&
|
|
333
436
|
inheritsNativeWebSearch(identity, capabilities.webSearch),
|
|
334
437
|
),
|
|
438
|
+
reasoning:
|
|
439
|
+
keys.some((key) => capabilities.reasoning.has(key)) ||
|
|
440
|
+
identities.some((identity) => modelReasoningEfforts(identity).length > 0),
|
|
441
|
+
tools:
|
|
442
|
+
keys.some((key) => capabilities.tools.has(key)) || identities.some(modelSupportsTools),
|
|
443
|
+
structuredOutput: keys.some((key) => capabilities.structuredOutput.has(key)),
|
|
444
|
+
temperature: modelTemperatureSupportFor(model) === true,
|
|
335
445
|
};
|
|
336
446
|
}
|
|
337
447
|
|
|
448
|
+
/** Resolve explicit Databricks temperature support, or undefined when unpublished. */
|
|
449
|
+
export function modelTemperatureSupportFor(
|
|
450
|
+
model: string | ServingEndpointSummary,
|
|
451
|
+
): boolean | undefined {
|
|
452
|
+
const index = metadataIndex();
|
|
453
|
+
const keys = modelIdentities(model).flatMap((identity) => modelKey(identity) ?? []);
|
|
454
|
+
if (keys.some((key) => index.temperatureUnsupported.has(key))) return false;
|
|
455
|
+
if (keys.some((key) => index.capabilities.temperature.has(key))) return true;
|
|
456
|
+
return undefined;
|
|
457
|
+
}
|
|
458
|
+
|
|
338
459
|
/** Resolve published ITPM, OTPM, and QPH limits from a model identity. */
|
|
339
460
|
export function modelRateLimitsFor(
|
|
340
461
|
model: string | ServingEndpointSummary,
|
|
@@ -350,26 +471,40 @@ export function modelRateLimitsFor(
|
|
|
350
471
|
}
|
|
351
472
|
|
|
352
473
|
/**
|
|
353
|
-
* Resolve reasoning levels:
|
|
354
|
-
* family defaults.
|
|
474
|
+
* Resolve reasoning levels: Databricks documentation, modelparams.dev,
|
|
475
|
+
* learned errors, then family defaults.
|
|
355
476
|
*
|
|
356
|
-
* Does not parse error bodies; callers should use
|
|
357
|
-
*
|
|
477
|
+
* Does not parse error bodies; callers handling an upstream error should use
|
|
478
|
+
* {@link learnReasoningLevelsFromError} so published levels keep precedence.
|
|
358
479
|
*/
|
|
359
480
|
export function modelReasoningLevelsFor(model: string | ServingEndpointSummary): ReasoningLevel[] {
|
|
360
481
|
const index = metadataIndex();
|
|
482
|
+
const published = publishedReasoningLevelsFor(model, index);
|
|
483
|
+
if (published) return published;
|
|
361
484
|
for (const identity of modelIdentities(model)) {
|
|
362
485
|
const learned = documentedReasoningLevels(identity, index.learnedReasoning);
|
|
363
486
|
if (learned?.length) return learned;
|
|
364
487
|
}
|
|
365
|
-
for (const identity of modelIdentities(model)) {
|
|
366
|
-
const documented = documentedReasoningLevels(identity, index.reasoning);
|
|
367
|
-
if (documented?.length) return documented;
|
|
368
|
-
}
|
|
369
488
|
const primary = typeof model === "string" ? model : (model.modelServiceName ?? model.name);
|
|
370
489
|
return defaultReasoningLevels(primary);
|
|
371
490
|
}
|
|
372
491
|
|
|
492
|
+
function publishedReasoningLevelsFor(
|
|
493
|
+
model: string | ServingEndpointSummary,
|
|
494
|
+
index: MetadataIndex,
|
|
495
|
+
): ReasoningLevel[] | undefined {
|
|
496
|
+
const identities = modelIdentities(model);
|
|
497
|
+
for (const identity of identities) {
|
|
498
|
+
const documented = documentedReasoningLevels(identity, index.reasoningDocs);
|
|
499
|
+
if (documented?.length) return documented;
|
|
500
|
+
}
|
|
501
|
+
for (const identity of identities) {
|
|
502
|
+
const modelParams = documentedReasoningLevels(identity, index.modelParamsReasoning);
|
|
503
|
+
if (modelParams?.length) return modelParams;
|
|
504
|
+
}
|
|
505
|
+
return undefined;
|
|
506
|
+
}
|
|
507
|
+
|
|
373
508
|
/** Resolve retirement, capability, and rate-limit metadata together. */
|
|
374
509
|
export function modelMetadataFor(model: string | ServingEndpointSummary): ModelMetadata {
|
|
375
510
|
const rateLimits = modelRateLimitsFor(model);
|
|
@@ -384,12 +519,16 @@ export function modelMetadataFor(model: string | ServingEndpointSummary): ModelM
|
|
|
384
519
|
export function modelMetadataGeneratedAt(): Readonly<{
|
|
385
520
|
retiredModels: number;
|
|
386
521
|
capabilities: number;
|
|
522
|
+
modelsDev: number;
|
|
523
|
+
modelParams: number;
|
|
387
524
|
rateLimits: number;
|
|
388
525
|
reasoning: number;
|
|
389
526
|
}> {
|
|
390
527
|
return {
|
|
391
528
|
retiredModels: RETIRED_MODELS_GENERATED_AT,
|
|
392
529
|
capabilities: capabilitiesSnapshot.generatedAt,
|
|
530
|
+
modelsDev: modelsDevSnapshot.generatedAt,
|
|
531
|
+
modelParams: modelParamsSnapshot.generatedAt,
|
|
393
532
|
rateLimits: rateLimitsSnapshot.generatedAt,
|
|
394
533
|
reasoning: reasoningSnapshot.generatedAt,
|
|
395
534
|
};
|
|
@@ -423,6 +562,38 @@ async function loadPage(url: string): Promise<string> {
|
|
|
423
562
|
return response.text();
|
|
424
563
|
}
|
|
425
564
|
|
|
565
|
+
async function loadJson(url: string): Promise<unknown> {
|
|
566
|
+
const response = await fetch(url, {
|
|
567
|
+
headers: { "user-agent": USER_AGENT },
|
|
568
|
+
signal: AbortSignal.timeout(15_000),
|
|
569
|
+
});
|
|
570
|
+
if (!response.ok) {
|
|
571
|
+
throw new Error(`Model metadata source returned HTTP ${response.status}: ${url}`);
|
|
572
|
+
}
|
|
573
|
+
return response.json();
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
function mergeModelsDevData(input: {
|
|
577
|
+
readonly fresh: ModelsDevData | undefined;
|
|
578
|
+
readonly previous: ModelsDevData | undefined;
|
|
579
|
+
readonly fallback: ModelsDevData;
|
|
580
|
+
}): ModelsDevData {
|
|
581
|
+
const capability = (key: keyof ModelsDevCapabilityCatalogue) =>
|
|
582
|
+
mergePreferFreshList({
|
|
583
|
+
fresh: input.fresh?.[key],
|
|
584
|
+
previous: input.previous?.[key],
|
|
585
|
+
fallback: input.fallback[key],
|
|
586
|
+
});
|
|
587
|
+
return {
|
|
588
|
+
imageInput: capability("imageInput"),
|
|
589
|
+
reasoning: capability("reasoning"),
|
|
590
|
+
tools: capability("tools"),
|
|
591
|
+
structuredOutput: capability("structuredOutput"),
|
|
592
|
+
temperatureSupported: capability("temperatureSupported"),
|
|
593
|
+
temperatureUnsupported: capability("temperatureUnsupported"),
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
|
|
426
597
|
function nowSeconds(): number {
|
|
427
598
|
return Math.floor(Date.now() / 1000);
|
|
428
599
|
}
|
|
@@ -273,23 +273,24 @@ const DISABLED_REASONING_TOKENS = new Set(["none", "off", "disable", "disabled"]
|
|
|
273
273
|
/**
|
|
274
274
|
* Remap a requested effort onto the nearest supported {@link ReasoningLevel}.
|
|
275
275
|
*
|
|
276
|
-
* `none` / `off` (and disable aliases)
|
|
277
|
-
*
|
|
278
|
-
* {@link ReasoningLevel.Medium}. Other
|
|
279
|
-
* level at or below the request, otherwise
|
|
276
|
+
* `none` / `off` (and disable aliases), null, and unrecognized explicit values
|
|
277
|
+
* are not treated as a low budget: when the model requires reasoning, they
|
|
278
|
+
* snap to the supported level closest to {@link ReasoningLevel.Medium}. Other
|
|
279
|
+
* requests prefer the highest allowed level at or below the request, otherwise
|
|
280
|
+
* the lowest allowed level.
|
|
280
281
|
*
|
|
281
|
-
* Returns `undefined` when
|
|
282
|
+
* Returns `undefined` only when `levels` is empty.
|
|
282
283
|
*/
|
|
283
284
|
export function remapReasoning(
|
|
284
285
|
requested: unknown,
|
|
285
286
|
levels: readonly ReasoningLevel[],
|
|
286
287
|
): ReasoningLevel | undefined {
|
|
287
288
|
if (levels.length === 0) return undefined;
|
|
288
|
-
if (isDisabledReasoningToken(requested)) {
|
|
289
|
+
if (requested == null || isDisabledReasoningToken(requested)) {
|
|
289
290
|
return nearestReasoning(ReasoningLevel.Medium, levels);
|
|
290
291
|
}
|
|
291
292
|
const parsed = parseReasoning(requested);
|
|
292
|
-
if (!parsed) return
|
|
293
|
+
if (!parsed) return nearestReasoning(ReasoningLevel.Medium, levels);
|
|
293
294
|
if (levels.includes(parsed)) return parsed;
|
|
294
295
|
const requestedIndex = REASONING_LEVELS.indexOf(parsed);
|
|
295
296
|
let best: ReasoningLevel | undefined;
|
|
@@ -349,7 +350,8 @@ export interface AdaptedRequestReasoning {
|
|
|
349
350
|
* Rewrite `reasoning_effort` / `reasoning.effort` onto a supported ladder.
|
|
350
351
|
*
|
|
351
352
|
* Model-agnostic: callers supply the allowed levels (learned, documented, or
|
|
352
|
-
* defaults).
|
|
353
|
+
* defaults). Explicit null or unrecognized values use the medium-centered
|
|
354
|
+
* fallback. Bodies without reasoning fields are returned unchanged.
|
|
353
355
|
*/
|
|
354
356
|
export function adaptRequestReasoning(
|
|
355
357
|
body: Readonly<Record<string, unknown>>,
|
|
@@ -357,11 +359,13 @@ export function adaptRequestReasoning(
|
|
|
357
359
|
): AdaptedRequestReasoning {
|
|
358
360
|
if (levels.length === 0) return { body, changed: false };
|
|
359
361
|
|
|
362
|
+
const hasTopLevel = Object.hasOwn(body, "reasoning_effort");
|
|
360
363
|
const topLevel = body.reasoning_effort;
|
|
361
364
|
const reasoning = isRecord(body.reasoning) ? body.reasoning : undefined;
|
|
365
|
+
const hasNested = reasoning ? Object.hasOwn(reasoning, "effort") : false;
|
|
362
366
|
const nested = reasoning?.effort;
|
|
363
|
-
|
|
364
|
-
|
|
367
|
+
if (!hasTopLevel && !hasNested) return { body, changed: false };
|
|
368
|
+
const requested = hasTopLevel ? topLevel : nested;
|
|
365
369
|
|
|
366
370
|
const remapped = remapReasoning(requested, levels);
|
|
367
371
|
if (!remapped) return { body, changed: false };
|
|
@@ -370,13 +374,13 @@ export function adaptRequestReasoning(
|
|
|
370
374
|
let changed = false;
|
|
371
375
|
let next: Record<string, unknown> = { ...body };
|
|
372
376
|
|
|
373
|
-
if (
|
|
377
|
+
if (hasTopLevel) {
|
|
374
378
|
if (String(topLevel) !== wire) {
|
|
375
379
|
next = { ...next, reasoning_effort: wire };
|
|
376
380
|
changed = true;
|
|
377
381
|
}
|
|
378
382
|
}
|
|
379
|
-
if (reasoning &&
|
|
383
|
+
if (reasoning && hasNested) {
|
|
380
384
|
if (String(nested) !== wire) {
|
|
381
385
|
next = { ...next, reasoning: { ...reasoning, effort: wire } };
|
|
382
386
|
changed = true;
|