@dbx-tools/model 0.9.32 → 0.9.37
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 +3 -3
- package/exports.ts +1 -0
- package/index.ts +12 -8
- package/lib/index.d.ts +12 -8
- package/lib/index.js +8 -5
- package/lib/scripts/generate-metadata.d.ts +1 -0
- package/lib/scripts/generate-metadata.js +65 -0
- package/lib/src/_metadata-contract.d.ts +12 -6
- package/lib/src/_metadata-contract.js +3 -1
- package/lib/src/_metadata-generator.d.ts +3 -1
- package/lib/src/_metadata-generator.js +148 -1
- package/lib/src/_ranking.js +39 -12
- package/lib/src/_selection.d.ts +1 -17
- package/lib/src/_selection.js +1 -1
- package/lib/src/classify.d.ts +1 -17
- package/lib/src/classify.js +1 -1
- package/lib/src/generated/model-reasoning.json +147 -0
- package/lib/src/invoke.d.ts +8 -0
- package/lib/src/invoke.js +13 -1
- package/lib/src/metadata-cache.d.ts +84 -0
- package/lib/src/metadata-cache.js +234 -0
- package/lib/src/metadata.d.ts +52 -17
- package/lib/src/metadata.js +251 -18
- package/lib/src/model-catalog.d.ts +1 -19
- package/lib/src/model-catalog.js +9 -2
- package/lib/src/model-client.d.ts +11 -19
- package/lib/src/model-client.js +42 -24
- package/lib/src/reasoning-translation.d.ts +105 -0
- package/lib/src/reasoning-translation.js +444 -0
- package/lib/src/resolve.d.ts +2 -4
- package/lib/src/resolve.js +2 -2
- package/lib/src/serving-wire.d.ts +37 -0
- package/lib/src/serving-wire.js +211 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +110 -17
- package/src/_metadata-contract.ts +17 -7
- package/src/_metadata-generator.ts +160 -0
- package/src/_ranking.ts +40 -11
- package/src/_selection.ts +4 -19
- package/src/classify.ts +2 -18
- package/src/generated/model-reasoning.json +147 -0
- package/src/invoke.ts +16 -0
- package/src/metadata-cache.ts +322 -0
- package/src/metadata.ts +334 -43
- package/src/model-catalog.ts +8 -25
- package/src/model-client.ts +67 -44
- package/src/reasoning-translation.ts +495 -0
- package/src/resolve.ts +10 -11
- package/src/serving-wire.ts +231 -0
- package/lib/src/python.d.ts +0 -2
- package/lib/src/python.js +0 -3
- package/src/python.ts +0 -2
package/src/metadata.ts
CHANGED
|
@@ -1,66 +1,80 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Build-generated Databricks model retirement, capability,
|
|
2
|
+
* Build-generated Databricks model retirement, capability, rate-limit, and
|
|
3
|
+
* reasoning-effort metadata, plus a daily write-through cache for refreshed
|
|
4
|
+
* docs and error-learned reasoning ladders.
|
|
3
5
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
6
|
+
* Committed snapshots seed every process. {@link refreshModelMetadata} pulls
|
|
7
|
+
* documentation through {@link createMetadataCache} (cacache memoize + disk) and
|
|
8
|
+
* rebuilds the in-memory index. Error-learned efforts use the same store via
|
|
9
|
+
* {@link rememberReasoningLevels}. Disk writes are skipped in Databricks Apps.
|
|
7
10
|
*
|
|
8
11
|
* @module
|
|
9
12
|
*/
|
|
10
13
|
import * as functionUtils from "@dbx-tools/shared-core/function-utils";
|
|
11
|
-
import
|
|
14
|
+
import * as log from "@dbx-tools/shared-core/log";
|
|
15
|
+
import type {
|
|
16
|
+
ModelCapabilities,
|
|
17
|
+
ModelMetadata,
|
|
18
|
+
ModelRateLimits,
|
|
19
|
+
ModelStatus,
|
|
20
|
+
ServingEndpointSummary,
|
|
21
|
+
} from "@dbx-tools/shared-model/contracts";
|
|
12
22
|
|
|
13
|
-
import capabilitiesSnapshotJson from "./generated/model-capabilities.json" with { type: "json" };
|
|
14
|
-
import rateLimitsSnapshotJson from "./generated/model-rate-limits.json" with { type: "json" };
|
|
15
|
-
import retiredModelsSnapshotJson from "./generated/retired-models.json" with { type: "json" };
|
|
16
23
|
import {
|
|
17
24
|
MODEL_METADATA_TTL_MS,
|
|
18
25
|
MODEL_RATE_LIMITS_URL,
|
|
19
26
|
OPENAI_RESPONSES_MODELS_URL,
|
|
27
|
+
QUERY_REASON_MODELS_URL,
|
|
20
28
|
RETIRED_MODELS_URL,
|
|
21
29
|
WEB_SEARCH_MODELS_URL,
|
|
22
30
|
type ModelCapabilitiesSnapshot,
|
|
23
31
|
type ModelCapabilityCatalogue,
|
|
24
|
-
type ModelRateLimits,
|
|
25
32
|
type ModelRateLimitsSnapshot,
|
|
33
|
+
type ReasoningModelsSnapshot,
|
|
26
34
|
type RetiredModelsSnapshot,
|
|
27
35
|
} from "./_metadata-contract.ts";
|
|
36
|
+
import {
|
|
37
|
+
parseModelCapabilities,
|
|
38
|
+
parseModelRateLimits,
|
|
39
|
+
parseReasoningModels,
|
|
40
|
+
parseRetiredModels,
|
|
41
|
+
} from "./_metadata-generator.ts";
|
|
42
|
+
import {
|
|
43
|
+
createMetadataCache,
|
|
44
|
+
mergePreferFreshList,
|
|
45
|
+
mergePreferFreshRecord,
|
|
46
|
+
type MetadataCache,
|
|
47
|
+
} from "./metadata-cache.ts";
|
|
48
|
+
import capabilitiesSnapshotJson from "./generated/model-capabilities.json" with { type: "json" };
|
|
49
|
+
import rateLimitsSnapshotJson from "./generated/model-rate-limits.json" with { type: "json" };
|
|
50
|
+
import reasoningSnapshotJson from "./generated/model-reasoning.json" with { type: "json" };
|
|
51
|
+
import retiredModelsSnapshotJson from "./generated/retired-models.json" with { type: "json" };
|
|
28
52
|
import { modelSearchQuery } from "./policy.ts";
|
|
53
|
+
import {
|
|
54
|
+
defaultReasoningLevels,
|
|
55
|
+
documentedReasoningLevels,
|
|
56
|
+
parseReasoning,
|
|
57
|
+
parseReasoningLevels,
|
|
58
|
+
type ReasoningLevel,
|
|
59
|
+
type ReasoningModelCatalogue,
|
|
60
|
+
uniqueReasoningLevels,
|
|
61
|
+
} from "./reasoning-translation.ts";
|
|
29
62
|
|
|
30
63
|
export {
|
|
31
64
|
MODEL_METADATA_TTL_MS,
|
|
32
65
|
MODEL_RATE_LIMITS_URL,
|
|
33
66
|
OPENAI_RESPONSES_MODELS_URL,
|
|
67
|
+
QUERY_REASON_MODELS_URL,
|
|
34
68
|
RETIRED_MODELS_URL,
|
|
35
69
|
WEB_SEARCH_MODELS_URL,
|
|
36
70
|
};
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
ModelCapabilityCatalogue,
|
|
40
|
-
ModelRateLimitCatalogue,
|
|
41
|
-
ModelRateLimits,
|
|
42
|
-
ModelRateLimitsSnapshot,
|
|
43
|
-
RetiredModelsSnapshot,
|
|
44
|
-
} from "./_metadata-contract.ts";
|
|
71
|
+
|
|
72
|
+
const logger = log.logger("model/metadata");
|
|
45
73
|
|
|
46
74
|
const retiredModelsSnapshot = retiredModelsSnapshotJson as RetiredModelsSnapshot;
|
|
47
75
|
const capabilitiesSnapshot = capabilitiesSnapshotJson as ModelCapabilitiesSnapshot;
|
|
48
76
|
const rateLimitsSnapshot = rateLimitsSnapshotJson as ModelRateLimitsSnapshot;
|
|
49
|
-
|
|
50
|
-
/** Documented capabilities resolved for one model identity. */
|
|
51
|
-
export interface ModelCapabilities {
|
|
52
|
-
readonly responses: boolean;
|
|
53
|
-
readonly imageInput: boolean;
|
|
54
|
-
readonly applyPatch: boolean;
|
|
55
|
-
readonly webSearch: boolean;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/** Combined build-generated metadata resolved for one model identity. */
|
|
59
|
-
export interface ModelMetadata {
|
|
60
|
-
readonly status: ModelStatus;
|
|
61
|
-
readonly capabilities: ModelCapabilities;
|
|
62
|
-
readonly rateLimits?: ModelRateLimits;
|
|
63
|
-
}
|
|
77
|
+
const reasoningSnapshot = reasoningSnapshotJson as ReasoningModelsSnapshot;
|
|
64
78
|
|
|
65
79
|
interface MetadataIndex {
|
|
66
80
|
readonly retiredNames: readonly string[];
|
|
@@ -69,25 +83,261 @@ interface MetadataIndex {
|
|
|
69
83
|
readonly [Key in keyof ModelCapabilityCatalogue]: ReadonlySet<string>;
|
|
70
84
|
};
|
|
71
85
|
readonly rateLimits: ReadonlyMap<string, ModelRateLimits>;
|
|
86
|
+
readonly reasoning: ReasoningModelCatalogue;
|
|
87
|
+
readonly learnedReasoning: ReasoningModelCatalogue;
|
|
72
88
|
}
|
|
73
89
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
90
|
+
type CapabilitySets = ModelCapabilityCatalogue;
|
|
91
|
+
|
|
92
|
+
const USER_AGENT = "dbx-tools-model-metadata/1";
|
|
93
|
+
|
|
94
|
+
const retiredCache = createMetadataCache<readonly string[]>({
|
|
95
|
+
key: "retired-models",
|
|
96
|
+
fallback: retiredModelsSnapshot.models,
|
|
97
|
+
load: async () => parseRetiredModels(await loadPage(RETIRED_MODELS_URL), nowSeconds()).models,
|
|
98
|
+
merge: mergePreferFreshList,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const capabilitiesCache = createMetadataCache<CapabilitySets>({
|
|
102
|
+
key: "model-capabilities",
|
|
103
|
+
fallback: capabilitiesSnapshot.capabilities,
|
|
104
|
+
load: async () => {
|
|
105
|
+
const [responsesHtml, webSearchHtml] = await Promise.all([
|
|
106
|
+
loadPage(OPENAI_RESPONSES_MODELS_URL),
|
|
107
|
+
loadPage(WEB_SEARCH_MODELS_URL),
|
|
108
|
+
]);
|
|
109
|
+
return parseModelCapabilities(responsesHtml, webSearchHtml, nowSeconds()).capabilities;
|
|
82
110
|
},
|
|
83
|
-
|
|
84
|
-
|
|
111
|
+
merge: (input) => ({
|
|
112
|
+
responses: mergePreferFreshList({
|
|
113
|
+
fresh: input.fresh?.responses,
|
|
114
|
+
previous: input.previous?.responses,
|
|
115
|
+
fallback: input.fallback.responses,
|
|
116
|
+
}),
|
|
117
|
+
imageInput: mergePreferFreshList({
|
|
118
|
+
fresh: input.fresh?.imageInput,
|
|
119
|
+
previous: input.previous?.imageInput,
|
|
120
|
+
fallback: input.fallback.imageInput,
|
|
121
|
+
}),
|
|
122
|
+
applyPatch: mergePreferFreshList({
|
|
123
|
+
fresh: input.fresh?.applyPatch,
|
|
124
|
+
previous: input.previous?.applyPatch,
|
|
125
|
+
fallback: input.fallback.applyPatch,
|
|
126
|
+
}),
|
|
127
|
+
webSearch: mergePreferFreshList({
|
|
128
|
+
fresh: input.fresh?.webSearch,
|
|
129
|
+
previous: input.previous?.webSearch,
|
|
130
|
+
fallback: input.fallback.webSearch,
|
|
131
|
+
}),
|
|
132
|
+
}),
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const rateLimitsCache = createMetadataCache<Readonly<Record<string, ModelRateLimits>>>({
|
|
136
|
+
key: "model-rate-limits",
|
|
137
|
+
fallback: rateLimitsSnapshot.catalogue.models,
|
|
138
|
+
load: async () =>
|
|
139
|
+
parseModelRateLimits(await loadPage(MODEL_RATE_LIMITS_URL), nowSeconds()).catalogue.models,
|
|
140
|
+
merge: mergePreferFreshRecord,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
const reasoningDocsCache = createMetadataCache<Readonly<Record<string, readonly string[]>>>({
|
|
144
|
+
key: "model-reasoning",
|
|
145
|
+
fallback: reasoningSnapshot.catalogue.models,
|
|
146
|
+
load: async () =>
|
|
147
|
+
parseReasoningModels(await loadPage(QUERY_REASON_MODELS_URL), nowSeconds()).catalogue.models,
|
|
148
|
+
merge: mergePreferFreshRecord,
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
const learnedReasoningCache = createMetadataCache<Readonly<Record<string, readonly ReasoningLevel[]>>>({
|
|
152
|
+
key: "learned-reasoning",
|
|
153
|
+
fallback: {},
|
|
154
|
+
merge: mergePreferFreshRecord,
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
let liveIndex: MetadataIndex | undefined;
|
|
158
|
+
|
|
159
|
+
const metadataIndex = functionUtils.memoize<MetadataIndex>(() => liveIndex ?? buildIndexFromCommitted());
|
|
160
|
+
|
|
161
|
+
function buildIndexFromCommitted(): MetadataIndex {
|
|
162
|
+
return buildIndex({
|
|
163
|
+
retiredNames: retiredModelsSnapshot.models,
|
|
164
|
+
capabilities: capabilitiesSnapshot.capabilities,
|
|
165
|
+
rateLimits: rateLimitsSnapshot.catalogue.models,
|
|
166
|
+
reasoning: reasoningSnapshot.catalogue.models,
|
|
167
|
+
learnedReasoning: {},
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function buildIndex(input: {
|
|
172
|
+
readonly retiredNames: readonly string[];
|
|
173
|
+
readonly capabilities: CapabilitySets;
|
|
174
|
+
readonly rateLimits: Readonly<Record<string, ModelRateLimits>>;
|
|
175
|
+
readonly reasoning: Readonly<Record<string, readonly string[]>>;
|
|
176
|
+
readonly learnedReasoning: Readonly<Record<string, readonly ReasoningLevel[]>>;
|
|
177
|
+
}): MetadataIndex {
|
|
178
|
+
return {
|
|
179
|
+
retiredNames: Object.freeze([...input.retiredNames]),
|
|
180
|
+
retiredKeys: new Set(input.retiredNames.map(retiredModelKey).filter(Boolean)),
|
|
181
|
+
capabilities: {
|
|
182
|
+
responses: new Set(input.capabilities.responses),
|
|
183
|
+
imageInput: new Set(input.capabilities.imageInput),
|
|
184
|
+
applyPatch: new Set(input.capabilities.applyPatch),
|
|
185
|
+
webSearch: new Set(input.capabilities.webSearch),
|
|
186
|
+
},
|
|
187
|
+
rateLimits: new Map(Object.entries(input.rateLimits)),
|
|
188
|
+
reasoning: freezeReasoningCatalogue(input.reasoning),
|
|
189
|
+
learnedReasoning: freezeLearnedCatalogue(input.learnedReasoning),
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function freezeReasoningCatalogue(
|
|
194
|
+
models: Readonly<Record<string, readonly string[]>>,
|
|
195
|
+
): ReasoningModelCatalogue {
|
|
196
|
+
return Object.freeze(
|
|
197
|
+
Object.fromEntries(
|
|
198
|
+
Object.entries(models).map(([key, levels]) => [
|
|
199
|
+
key,
|
|
200
|
+
Object.freeze(
|
|
201
|
+
uniqueReasoningLevels(levels.flatMap((level) => parseReasoning(level) ?? [])),
|
|
202
|
+
),
|
|
203
|
+
]),
|
|
204
|
+
),
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function freezeLearnedCatalogue(
|
|
209
|
+
models: Readonly<Record<string, readonly ReasoningLevel[]>>,
|
|
210
|
+
): ReasoningModelCatalogue {
|
|
211
|
+
return Object.freeze(
|
|
212
|
+
Object.fromEntries(
|
|
213
|
+
Object.entries(models).map(([key, levels]) => [
|
|
214
|
+
key,
|
|
215
|
+
Object.freeze(uniqueReasoningLevels(levels)),
|
|
216
|
+
]),
|
|
217
|
+
),
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
let refreshInflight: Promise<MetadataIndex> | undefined;
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Hydrate or refresh documentation + learned caches into the process index.
|
|
225
|
+
*
|
|
226
|
+
* Safe to call on gateway startup. Concurrent callers share one refresh.
|
|
227
|
+
*/
|
|
228
|
+
export async function refreshModelMetadata(): Promise<MetadataIndex> {
|
|
229
|
+
if (refreshInflight) return refreshInflight;
|
|
230
|
+
refreshInflight = (async () => {
|
|
231
|
+
const [retiredNames, capabilities, rateLimits, reasoning, learnedReasoning] = await Promise.all([
|
|
232
|
+
retiredCache.get(),
|
|
233
|
+
capabilitiesCache.get(),
|
|
234
|
+
rateLimitsCache.get(),
|
|
235
|
+
reasoningDocsCache.get(),
|
|
236
|
+
learnedReasoningCache.get(),
|
|
237
|
+
]);
|
|
238
|
+
liveIndex = buildIndex({
|
|
239
|
+
retiredNames,
|
|
240
|
+
capabilities,
|
|
241
|
+
rateLimits,
|
|
242
|
+
reasoning,
|
|
243
|
+
learnedReasoning,
|
|
244
|
+
});
|
|
245
|
+
return liveIndex;
|
|
246
|
+
})().finally(() => {
|
|
247
|
+
refreshInflight = undefined;
|
|
248
|
+
});
|
|
249
|
+
return refreshInflight;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
void hydrateModelMetadata().catch((error) => {
|
|
253
|
+
logger.warn("metadata cache hydrate failed; using committed snapshots", { error });
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
/** Load cacache / memory entries without forcing a network refresh. */
|
|
257
|
+
export async function hydrateModelMetadata(): Promise<MetadataIndex> {
|
|
258
|
+
const [retiredNames, capabilities, rateLimits, reasoning, learnedReasoning] = await Promise.all([
|
|
259
|
+
peekOrFallback(retiredCache, retiredModelsSnapshot.models),
|
|
260
|
+
peekOrFallback(capabilitiesCache, capabilitiesSnapshot.capabilities),
|
|
261
|
+
peekOrFallback(rateLimitsCache, rateLimitsSnapshot.catalogue.models),
|
|
262
|
+
peekOrFallback(reasoningDocsCache, reasoningSnapshot.catalogue.models),
|
|
263
|
+
peekOrFallback(learnedReasoningCache, {}),
|
|
264
|
+
]);
|
|
265
|
+
liveIndex = buildIndex({
|
|
266
|
+
retiredNames,
|
|
267
|
+
capabilities,
|
|
268
|
+
rateLimits,
|
|
269
|
+
reasoning,
|
|
270
|
+
learnedReasoning,
|
|
271
|
+
});
|
|
272
|
+
return liveIndex;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
async function peekOrFallback<T>(cache: MetadataCache<T>, fallback: T): Promise<T> {
|
|
276
|
+
const peeked = await cache.peek();
|
|
277
|
+
if (peeked !== undefined) return peeked;
|
|
278
|
+
return cache.get().catch(() => fallback);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/** Absolute cacache root used by the documentation / learned caches. */
|
|
282
|
+
export function modelMetadataCachePath(): string {
|
|
283
|
+
return retiredCache.path;
|
|
284
|
+
}
|
|
85
285
|
|
|
86
286
|
/** Return the committed retired-model names. The array is cached and immutable. */
|
|
87
287
|
export function retiredModelNames(): readonly string[] {
|
|
88
288
|
return metadataIndex().retiredNames;
|
|
89
289
|
}
|
|
90
290
|
|
|
291
|
+
/** Return the committed reasoning-effort catalogue. Cached per process. */
|
|
292
|
+
export function reasoningModelCatalogue(): ReasoningModelCatalogue {
|
|
293
|
+
return metadataIndex().reasoning;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/** Return error-learned reasoning ladders persisted in the metadata cache. */
|
|
297
|
+
export function learnedReasoningCatalogue(): ReasoningModelCatalogue {
|
|
298
|
+
return metadataIndex().learnedReasoning;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Store levels learned from an upstream error and persist them write-through.
|
|
303
|
+
*
|
|
304
|
+
* Empty lists are ignored so a failed parse cannot wipe defaults. Error bodies
|
|
305
|
+
* themselves are never stored - only the parsed ladder.
|
|
306
|
+
*/
|
|
307
|
+
export async function rememberReasoningLevels(
|
|
308
|
+
model: string,
|
|
309
|
+
levels: readonly ReasoningLevel[],
|
|
310
|
+
): Promise<ReasoningLevel[]> {
|
|
311
|
+
const unique = uniqueReasoningLevels(levels);
|
|
312
|
+
if (unique.length === 0) return modelReasoningLevelsFor(model);
|
|
313
|
+
const key = modelKey(model);
|
|
314
|
+
if (!key) return modelReasoningLevelsFor(model);
|
|
315
|
+
const catalogue = await learnedReasoningCache.update((current) => ({
|
|
316
|
+
...current,
|
|
317
|
+
[key]: unique,
|
|
318
|
+
}));
|
|
319
|
+
const current = metadataIndex();
|
|
320
|
+
liveIndex = {
|
|
321
|
+
...current,
|
|
322
|
+
learnedReasoning: freezeLearnedCatalogue(catalogue),
|
|
323
|
+
};
|
|
324
|
+
return [...unique];
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Parse an upstream error body and persist the supported ladder for `model`.
|
|
329
|
+
*
|
|
330
|
+
* Accepts the same nested Databricks envelopes as {@link parseReasoningLevels}.
|
|
331
|
+
* Returns the levels that were stored (or the current lookup when parse yields
|
|
332
|
+
* nothing). The raw error body is never written to disk.
|
|
333
|
+
*/
|
|
334
|
+
export async function learnReasoningLevelsFromError(
|
|
335
|
+
model: string,
|
|
336
|
+
body: unknown,
|
|
337
|
+
): Promise<ReasoningLevel[]> {
|
|
338
|
+
return rememberReasoningLevels(model, parseReasoningLevels(body));
|
|
339
|
+
}
|
|
340
|
+
|
|
91
341
|
/** Resolve whether any supplied identity is listed in the retirement snapshot. */
|
|
92
342
|
export function modelStatusFor(
|
|
93
343
|
...models: readonly (string | ServingEndpointSummary)[]
|
|
@@ -131,6 +381,30 @@ export function modelRateLimitsFor(
|
|
|
131
381
|
return undefined;
|
|
132
382
|
}
|
|
133
383
|
|
|
384
|
+
/**
|
|
385
|
+
* Resolve reasoning levels: learned from errors, then documentation, then
|
|
386
|
+
* family defaults.
|
|
387
|
+
*
|
|
388
|
+
* Does not parse error bodies; callers should use {@link parseReasoningLevels}
|
|
389
|
+
* and then {@link rememberReasoningLevels}.
|
|
390
|
+
*/
|
|
391
|
+
export function modelReasoningLevelsFor(
|
|
392
|
+
model: string | ServingEndpointSummary,
|
|
393
|
+
): ReasoningLevel[] {
|
|
394
|
+
const index = metadataIndex();
|
|
395
|
+
for (const identity of modelIdentities(model)) {
|
|
396
|
+
const learned = documentedReasoningLevels(identity, index.learnedReasoning);
|
|
397
|
+
if (learned?.length) return learned;
|
|
398
|
+
}
|
|
399
|
+
for (const identity of modelIdentities(model)) {
|
|
400
|
+
const documented = documentedReasoningLevels(identity, index.reasoning);
|
|
401
|
+
if (documented?.length) return documented;
|
|
402
|
+
}
|
|
403
|
+
const primary =
|
|
404
|
+
typeof model === "string" ? model : (model.modelServiceName ?? model.name);
|
|
405
|
+
return defaultReasoningLevels(primary);
|
|
406
|
+
}
|
|
407
|
+
|
|
134
408
|
/** Resolve retirement, capability, and rate-limit metadata together. */
|
|
135
409
|
export function modelMetadataFor(model: string | ServingEndpointSummary): ModelMetadata {
|
|
136
410
|
const rateLimits = modelRateLimitsFor(model);
|
|
@@ -141,16 +415,18 @@ export function modelMetadataFor(model: string | ServingEndpointSummary): ModelM
|
|
|
141
415
|
};
|
|
142
416
|
}
|
|
143
417
|
|
|
144
|
-
/** Return generation timestamps for the
|
|
418
|
+
/** Return generation timestamps for the committed metadata snapshots. */
|
|
145
419
|
export function modelMetadataGeneratedAt(): Readonly<{
|
|
146
420
|
retiredModels: number;
|
|
147
421
|
capabilities: number;
|
|
148
422
|
rateLimits: number;
|
|
423
|
+
reasoning: number;
|
|
149
424
|
}> {
|
|
150
425
|
return {
|
|
151
426
|
retiredModels: retiredModelsSnapshot.generatedAt,
|
|
152
427
|
capabilities: capabilitiesSnapshot.generatedAt,
|
|
153
428
|
rateLimits: rateLimitsSnapshot.generatedAt,
|
|
429
|
+
reasoning: reasoningSnapshot.generatedAt,
|
|
154
430
|
};
|
|
155
431
|
}
|
|
156
432
|
|
|
@@ -187,3 +463,18 @@ function retiredModelKey(value: string): string {
|
|
|
187
463
|
while (tokens[0] && RETIRED_MODEL_PREFIXES.has(tokens[0])) tokens.shift();
|
|
188
464
|
return tokens.join("-");
|
|
189
465
|
}
|
|
466
|
+
|
|
467
|
+
async function loadPage(url: string): Promise<string> {
|
|
468
|
+
const response = await fetch(url, {
|
|
469
|
+
headers: { "user-agent": USER_AGENT },
|
|
470
|
+
signal: AbortSignal.timeout(15_000),
|
|
471
|
+
});
|
|
472
|
+
if (!response.ok) {
|
|
473
|
+
throw new Error(`Databricks documentation returned HTTP ${response.status}: ${url}`);
|
|
474
|
+
}
|
|
475
|
+
return response.text();
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function nowSeconds(): number {
|
|
479
|
+
return Math.floor(Date.now() / 1000);
|
|
480
|
+
}
|
package/src/model-catalog.ts
CHANGED
|
@@ -25,7 +25,13 @@ import { createHash } from "node:crypto";
|
|
|
25
25
|
import { CacheManager } from "@databricks/appkit";
|
|
26
26
|
import { appkit } from "@dbx-tools/appkit";
|
|
27
27
|
import { errorUtils, log } from "@dbx-tools/shared-core";
|
|
28
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
model,
|
|
30
|
+
type ResolvedModel,
|
|
31
|
+
type ResolveModelOptions,
|
|
32
|
+
type ScoredEndpoint,
|
|
33
|
+
type ServingEndpointSummary,
|
|
34
|
+
} from "@dbx-tools/shared-model";
|
|
29
35
|
|
|
30
36
|
import { classifyEndpointClasses, normalizeEndpoints, rankEndpoints } from "./_ranking.ts";
|
|
31
37
|
|
|
@@ -140,9 +146,7 @@ async function fetchEndpoints(client: WorkspaceClientLike): Promise<ServingEndpo
|
|
|
140
146
|
* classification of the whole set. Endpoints the classifier doesn't recognize
|
|
141
147
|
* (custom, unscored, non-LLM) are left without a class.
|
|
142
148
|
*/
|
|
143
|
-
function stampModelClasses(
|
|
144
|
-
summaries: readonly ServingEndpointSummary[],
|
|
145
|
-
): ServingEndpointSummary[] {
|
|
149
|
+
function stampModelClasses(summaries: readonly ServingEndpointSummary[]): ServingEndpointSummary[] {
|
|
146
150
|
const classOf = classifyEndpointClasses(summaries);
|
|
147
151
|
return summaries.map((summary) => {
|
|
148
152
|
const cls = classOf.get(summary.name);
|
|
@@ -218,27 +222,6 @@ export async function clearServingEndpointsCache(
|
|
|
218
222
|
* callers can fall back to the original input (Databricks will then return a
|
|
219
223
|
* clean 404).
|
|
220
224
|
*/
|
|
221
|
-
export interface ResolvedModel {
|
|
222
|
-
modelId: string;
|
|
223
|
-
matched: boolean;
|
|
224
|
-
score?: number;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/** Options accepted by {@link resolveModelId} / {@link searchServingEndpoints}. */
|
|
228
|
-
export interface ResolveModelOptions {
|
|
229
|
-
/** Fuzzy distance threshold (0 = exact, 1 = anything). Default `0.4`. */
|
|
230
|
-
threshold?: number;
|
|
231
|
-
/** Require a model verified for a complete function-tool round-trip. */
|
|
232
|
-
requiresTools?: boolean;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/** A serving endpoint paired with its fuzzy-match distance for a query. */
|
|
236
|
-
export interface ScoredEndpoint {
|
|
237
|
-
endpoint: ServingEndpointSummary;
|
|
238
|
-
/** Fuzzy distance: `0` is exact, `1` is no match. */
|
|
239
|
-
score: number;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
225
|
/**
|
|
243
226
|
* Fuzzy-rank endpoints by how closely their `name` matches `input`, best
|
|
244
227
|
* (lowest score) first, keeping only those within `threshold`:
|