@fabricorg/experiments-evals 0.1.0 → 0.2.0
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 +5 -2
- package/dist/databricks.cjs +176 -33
- package/dist/databricks.cjs.map +1 -1
- package/dist/databricks.d.cts +271 -13
- package/dist/databricks.d.ts +271 -13
- package/dist/databricks.js +172 -34
- package/dist/databricks.js.map +1 -1
- package/package.json +17 -5
package/README.md
CHANGED
|
@@ -10,6 +10,9 @@ import { DatabricksModelClient } from '@fabricorg/experiments-evals/databricks';
|
|
|
10
10
|
```
|
|
11
11
|
|
|
12
12
|
Databricks is an explicit adapter subpath; it is not exported from the portable
|
|
13
|
-
package root.
|
|
13
|
+
package root. The subpath also exports a read-only `DatabricksMlflowClient` for
|
|
14
|
+
linking native managed-MLflow run metrics into Fabric release policy. MLflow
|
|
15
|
+
artifacts, datasets, traces, scorers, feedback, and lineage remain in
|
|
16
|
+
Databricks.
|
|
14
17
|
|
|
15
|
-
See [
|
|
18
|
+
See [Databricks-native AI quality](https://experiments.fabric.pro/docs/ai-quality/databricks-native/).
|
package/dist/databricks.cjs
CHANGED
|
@@ -1,25 +1,79 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var zod = require('zod');
|
|
4
|
+
|
|
5
|
+
// src/databricks-auth.ts
|
|
6
|
+
var DatabricksBearerProvider = class {
|
|
7
|
+
host;
|
|
8
|
+
auth;
|
|
9
|
+
fetchImpl;
|
|
10
|
+
timeoutMs;
|
|
11
|
+
oauthToken = null;
|
|
12
|
+
constructor(config) {
|
|
13
|
+
this.host = normalizeDatabricksHost(config.host);
|
|
14
|
+
this.auth = config.auth;
|
|
15
|
+
this.fetchImpl = config.fetchImpl ?? fetch;
|
|
16
|
+
this.timeoutMs = config.timeoutMs ?? 6e4;
|
|
17
|
+
if (!Number.isFinite(this.timeoutMs) || this.timeoutMs <= 0) {
|
|
18
|
+
throw new Error("Databricks timeoutMs must be positive");
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
async token() {
|
|
22
|
+
if (this.auth.kind === "pat") return this.auth.token;
|
|
23
|
+
if (this.oauthToken && this.oauthToken.expiresAt > Date.now() + 6e4) {
|
|
24
|
+
return this.oauthToken.value;
|
|
25
|
+
}
|
|
26
|
+
const response = await this.fetchImpl(`${this.host}/oidc/v1/token`, {
|
|
27
|
+
method: "POST",
|
|
28
|
+
headers: {
|
|
29
|
+
authorization: `Basic ${toBase64(`${this.auth.clientId}:${this.auth.clientSecret}`)}`,
|
|
30
|
+
"content-type": "application/x-www-form-urlencoded"
|
|
31
|
+
},
|
|
32
|
+
body: new URLSearchParams({ grant_type: "client_credentials", scope: "all-apis" }),
|
|
33
|
+
signal: AbortSignal.timeout(this.timeoutMs)
|
|
34
|
+
});
|
|
35
|
+
if (!response.ok) throw new Error(`Databricks OAuth exchange failed (${response.status})`);
|
|
36
|
+
const body = await response.json();
|
|
37
|
+
if (!body.access_token) throw new Error("Databricks OAuth exchange returned no access token");
|
|
38
|
+
this.oauthToken = {
|
|
39
|
+
value: body.access_token,
|
|
40
|
+
expiresAt: Date.now() + (body.expires_in ?? 3600) * 1e3
|
|
41
|
+
};
|
|
42
|
+
return this.oauthToken.value;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
function normalizeDatabricksHost(host) {
|
|
46
|
+
return host.startsWith("http://") || host.startsWith("https://") ? host.replace(/\/$/, "") : `https://${host.replace(/\/$/, "")}`;
|
|
47
|
+
}
|
|
48
|
+
function toBase64(value) {
|
|
49
|
+
if (typeof Buffer !== "undefined") return Buffer.from(value).toString("base64");
|
|
50
|
+
return btoa(value);
|
|
51
|
+
}
|
|
52
|
+
|
|
3
53
|
// src/databricks-model-client.ts
|
|
4
54
|
var DatabricksModelClient = class {
|
|
5
55
|
endpoint;
|
|
6
56
|
/** Exposed for wiring assertions (deps-construction tests). */
|
|
7
57
|
authMode;
|
|
8
58
|
host;
|
|
9
|
-
auth;
|
|
10
59
|
fetchImpl;
|
|
11
60
|
timeoutMs;
|
|
12
|
-
|
|
61
|
+
bearer;
|
|
13
62
|
constructor(config) {
|
|
14
|
-
this.host =
|
|
63
|
+
this.host = normalizeDatabricksHost(config.host);
|
|
15
64
|
this.endpoint = config.endpoint;
|
|
16
|
-
this.auth = config.auth;
|
|
17
65
|
this.authMode = config.auth.kind;
|
|
18
66
|
this.fetchImpl = config.fetchImpl ?? fetch;
|
|
19
67
|
this.timeoutMs = config.timeoutMs ?? 6e4;
|
|
20
68
|
if (!Number.isFinite(this.timeoutMs) || this.timeoutMs <= 0) {
|
|
21
69
|
throw new Error("Databricks Model Serving timeoutMs must be positive");
|
|
22
70
|
}
|
|
71
|
+
this.bearer = new DatabricksBearerProvider({
|
|
72
|
+
host: this.host,
|
|
73
|
+
auth: config.auth,
|
|
74
|
+
fetchImpl: this.fetchImpl,
|
|
75
|
+
timeoutMs: this.timeoutMs
|
|
76
|
+
});
|
|
23
77
|
}
|
|
24
78
|
async complete(messages, options) {
|
|
25
79
|
if (!Number.isInteger(options.maxOutputTokens) || options.maxOutputTokens <= 0) {
|
|
@@ -33,7 +87,7 @@ var DatabricksModelClient = class {
|
|
|
33
87
|
{
|
|
34
88
|
method: "POST",
|
|
35
89
|
headers: {
|
|
36
|
-
authorization: `Bearer ${await this.
|
|
90
|
+
authorization: `Bearer ${await this.bearer.token()}`,
|
|
37
91
|
"content-type": "application/json"
|
|
38
92
|
},
|
|
39
93
|
body: JSON.stringify({
|
|
@@ -60,29 +114,6 @@ var DatabricksModelClient = class {
|
|
|
60
114
|
}
|
|
61
115
|
};
|
|
62
116
|
}
|
|
63
|
-
async resolveToken() {
|
|
64
|
-
if (this.auth.kind === "pat") return this.auth.token;
|
|
65
|
-
if (this.oauthToken && this.oauthToken.expiresAt > Date.now() + 6e4) {
|
|
66
|
-
return this.oauthToken.value;
|
|
67
|
-
}
|
|
68
|
-
const response = await this.fetchImpl(`${this.host}/oidc/v1/token`, {
|
|
69
|
-
method: "POST",
|
|
70
|
-
headers: {
|
|
71
|
-
authorization: `Basic ${toBase64(`${this.auth.clientId}:${this.auth.clientSecret}`)}`,
|
|
72
|
-
"content-type": "application/x-www-form-urlencoded"
|
|
73
|
-
},
|
|
74
|
-
body: new URLSearchParams({ grant_type: "client_credentials", scope: "all-apis" }),
|
|
75
|
-
signal: AbortSignal.timeout(this.timeoutMs)
|
|
76
|
-
});
|
|
77
|
-
if (!response.ok) throw new Error(`Databricks OAuth exchange failed (${response.status})`);
|
|
78
|
-
const body = await response.json();
|
|
79
|
-
if (!body.access_token) throw new Error("Databricks OAuth exchange returned no access token");
|
|
80
|
-
this.oauthToken = {
|
|
81
|
-
value: body.access_token,
|
|
82
|
-
expiresAt: Date.now() + (body.expires_in ?? 3600) * 1e3
|
|
83
|
-
};
|
|
84
|
-
return this.oauthToken.value;
|
|
85
|
-
}
|
|
86
117
|
};
|
|
87
118
|
function parseServingResponse(payload) {
|
|
88
119
|
if (!isRecord(payload)) throw new Error("Model Serving returned an invalid response object");
|
|
@@ -107,14 +138,126 @@ function isRecord(value) {
|
|
|
107
138
|
function isTokenCount(value) {
|
|
108
139
|
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
109
140
|
}
|
|
110
|
-
|
|
111
|
-
|
|
141
|
+
var MlflowMetric = zod.z.object({
|
|
142
|
+
key: zod.z.string().min(1),
|
|
143
|
+
value: zod.z.number(),
|
|
144
|
+
timestamp: zod.z.number().optional(),
|
|
145
|
+
step: zod.z.number().optional()
|
|
146
|
+
});
|
|
147
|
+
var MlflowRunResponse = zod.z.object({
|
|
148
|
+
run: zod.z.object({
|
|
149
|
+
info: zod.z.object({
|
|
150
|
+
run_id: zod.z.string().min(1),
|
|
151
|
+
experiment_id: zod.z.string().min(1),
|
|
152
|
+
status: zod.z.string().min(1),
|
|
153
|
+
start_time: zod.z.number().optional(),
|
|
154
|
+
end_time: zod.z.number().optional(),
|
|
155
|
+
artifact_uri: zod.z.string().optional(),
|
|
156
|
+
run_name: zod.z.string().optional()
|
|
157
|
+
}),
|
|
158
|
+
data: zod.z.object({
|
|
159
|
+
metrics: zod.z.array(MlflowMetric).default([]),
|
|
160
|
+
params: zod.z.array(zod.z.object({ key: zod.z.string(), value: zod.z.string() })).default([]),
|
|
161
|
+
tags: zod.z.array(zod.z.object({ key: zod.z.string(), value: zod.z.string() })).default([])
|
|
162
|
+
}).default({ metrics: [], params: [], tags: [] })
|
|
163
|
+
})
|
|
164
|
+
});
|
|
165
|
+
var DatabricksMlflowClient = class {
|
|
166
|
+
host;
|
|
167
|
+
authMode;
|
|
168
|
+
fetchImpl;
|
|
169
|
+
timeoutMs;
|
|
170
|
+
bearer;
|
|
171
|
+
constructor(config) {
|
|
172
|
+
this.host = normalizeDatabricksHost(config.host);
|
|
173
|
+
this.authMode = config.auth.kind;
|
|
174
|
+
this.fetchImpl = config.fetchImpl ?? fetch;
|
|
175
|
+
this.timeoutMs = config.timeoutMs ?? 6e4;
|
|
176
|
+
if (!Number.isFinite(this.timeoutMs) || this.timeoutMs <= 0) {
|
|
177
|
+
throw new Error("Databricks MLflow timeoutMs must be positive");
|
|
178
|
+
}
|
|
179
|
+
this.bearer = new DatabricksBearerProvider({
|
|
180
|
+
host: this.host,
|
|
181
|
+
auth: config.auth,
|
|
182
|
+
fetchImpl: this.fetchImpl,
|
|
183
|
+
timeoutMs: this.timeoutMs
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
async getRun(runId) {
|
|
187
|
+
if (!runId.trim()) throw new Error("MLflow runId is required");
|
|
188
|
+
const url = new URL(`${this.host}/api/2.0/mlflow/runs/get`);
|
|
189
|
+
url.searchParams.set("run_id", runId);
|
|
190
|
+
const response = await this.fetchImpl(url, {
|
|
191
|
+
headers: { authorization: `Bearer ${await this.bearer.token()}` },
|
|
192
|
+
signal: AbortSignal.timeout(this.timeoutMs)
|
|
193
|
+
});
|
|
194
|
+
if (!response.ok) {
|
|
195
|
+
const body = await response.text();
|
|
196
|
+
throw new Error(
|
|
197
|
+
`Databricks MLflow run lookup failed (${response.status}): ${body.slice(0, 500)}`
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
const parsed = MlflowRunResponse.safeParse(await response.json());
|
|
201
|
+
if (!parsed.success) {
|
|
202
|
+
throw new Error(
|
|
203
|
+
`Databricks MLflow returned an invalid run: ${parsed.error.issues[0]?.message ?? "schema mismatch"}`
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
return parsed.data.run;
|
|
207
|
+
}
|
|
208
|
+
runUrl(run) {
|
|
209
|
+
return `${this.host}/ml/experiments/${encodeURIComponent(run.info.experiment_id)}/runs/${encodeURIComponent(run.info.run_id)}`;
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
function parseMlflowMetricGates(value) {
|
|
213
|
+
const entries = value.split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
214
|
+
if (entries.length === 0) throw new Error("At least one MLflow metric gate is required");
|
|
215
|
+
return entries.map((entry) => {
|
|
216
|
+
const match = /^(.+?)(>=|<=|==|!=|>|<)(-?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?)$/i.exec(entry);
|
|
217
|
+
if (!match) throw new Error(`Invalid MLflow metric gate '${entry}'`);
|
|
218
|
+
const metric = match[1].trim();
|
|
219
|
+
if (!metric) throw new Error(`Invalid MLflow metric gate '${entry}'`);
|
|
220
|
+
return {
|
|
221
|
+
metric,
|
|
222
|
+
operator: match[2],
|
|
223
|
+
threshold: Number(match[3])
|
|
224
|
+
};
|
|
225
|
+
});
|
|
112
226
|
}
|
|
113
|
-
function
|
|
114
|
-
|
|
115
|
-
return
|
|
227
|
+
function evaluateMlflowMetricGates(run, gates) {
|
|
228
|
+
const metrics = new Map(run.data.metrics.map((metric) => [metric.key, metric.value]));
|
|
229
|
+
return gates.map((gate) => {
|
|
230
|
+
const actual = metrics.get(gate.metric) ?? null;
|
|
231
|
+
return {
|
|
232
|
+
...gate,
|
|
233
|
+
actual,
|
|
234
|
+
passed: actual === null ? false : compare(actual, gate.operator, gate.threshold),
|
|
235
|
+
...actual === null ? { reason: "missing-metric" } : {}
|
|
236
|
+
};
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
function compare(actual, operator, threshold) {
|
|
240
|
+
switch (operator) {
|
|
241
|
+
case ">=":
|
|
242
|
+
return actual >= threshold;
|
|
243
|
+
case ">":
|
|
244
|
+
return actual > threshold;
|
|
245
|
+
case "<=":
|
|
246
|
+
return actual <= threshold;
|
|
247
|
+
case "<":
|
|
248
|
+
return actual < threshold;
|
|
249
|
+
case "==":
|
|
250
|
+
return actual === threshold;
|
|
251
|
+
case "!=":
|
|
252
|
+
return actual !== threshold;
|
|
253
|
+
}
|
|
116
254
|
}
|
|
117
255
|
|
|
256
|
+
exports.DatabricksBearerProvider = DatabricksBearerProvider;
|
|
257
|
+
exports.DatabricksMlflowClient = DatabricksMlflowClient;
|
|
118
258
|
exports.DatabricksModelClient = DatabricksModelClient;
|
|
259
|
+
exports.evaluateMlflowMetricGates = evaluateMlflowMetricGates;
|
|
260
|
+
exports.normalizeDatabricksHost = normalizeDatabricksHost;
|
|
261
|
+
exports.parseMlflowMetricGates = parseMlflowMetricGates;
|
|
119
262
|
//# sourceMappingURL=databricks.cjs.map
|
|
120
263
|
//# sourceMappingURL=databricks.cjs.map
|
package/dist/databricks.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/databricks-model-client.ts"],"names":[],"mappings":"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","file":"databricks.cjs","sourcesContent":["import type {\n ChatMessage,\n ModelClient,\n ModelCompletionOptions,\n ModelResponse,\n} from './model-client.js';\n\n/**\n * Transport-agnostic Databricks Model Serving chat client\n * (`/serving-endpoints/{name}/invocations`, OpenAI-compatible chat payload).\n *\n * Auth mirrors the warehouse wiring: a PAT (`token`) wins when provided,\n * otherwise M2M OAuth client credentials are exchanged at\n * `/oidc/v1/token` and cached per instance until shortly before expiry.\n *\n * The `fetchImpl` injection keeps this package free of HTTP-client deps and\n * lets tests assert the exact request shape. Both the control plane\n * (`apps/api/src/server/model-client.ts`) and the Temporal worker\n * (`agents/harness-deploy`) wrap this class with their own env resolution.\n */\n\nexport type DatabricksAuth =\n | { kind: 'pat'; token: string }\n | { kind: 'oauth-m2m'; clientId: string; clientSecret: string };\n\nexport interface DatabricksModelClientConfig {\n /** Workspace host — bare (`dbc-x.cloud.databricks.com`) or with scheme. */\n host: string;\n /** Serving endpoint name; recorded in EvalScore.modelEndpoint. */\n endpoint: string;\n auth: DatabricksAuth;\n /** Injected for tests; defaults to global fetch. */\n fetchImpl?: typeof fetch;\n /** Abort Model Serving and OAuth calls after this duration. Default: 60s. */\n timeoutMs?: number;\n}\n\nexport class DatabricksModelClient implements ModelClient {\n readonly endpoint: string;\n /** Exposed for wiring assertions (deps-construction tests). */\n readonly authMode: DatabricksAuth['kind'];\n private readonly host: string;\n private readonly auth: DatabricksAuth;\n private readonly fetchImpl: typeof fetch;\n private readonly timeoutMs: number;\n private oauthToken: { value: string; expiresAt: number } | null = null;\n\n constructor(config: DatabricksModelClientConfig) {\n this.host = normalizeHost(config.host);\n this.endpoint = config.endpoint;\n this.auth = config.auth;\n this.authMode = config.auth.kind;\n this.fetchImpl = config.fetchImpl ?? fetch;\n this.timeoutMs = config.timeoutMs ?? 60_000;\n if (!Number.isFinite(this.timeoutMs) || this.timeoutMs <= 0) {\n throw new Error('Databricks Model Serving timeoutMs must be positive');\n }\n }\n\n async complete(\n messages: readonly ChatMessage[],\n options: ModelCompletionOptions,\n ): Promise<ModelResponse> {\n if (!Number.isInteger(options.maxOutputTokens) || options.maxOutputTokens <= 0) {\n throw new Error('maxOutputTokens must be a positive integer');\n }\n if (!Number.isFinite(options.temperature) || options.temperature < 0) {\n throw new Error('temperature must be a non-negative finite number');\n }\n const response = await this.fetchImpl(\n `${this.host}/serving-endpoints/${encodeURIComponent(this.endpoint)}/invocations`,\n {\n method: 'POST',\n headers: {\n authorization: `Bearer ${await this.resolveToken()}`,\n 'content-type': 'application/json',\n },\n body: JSON.stringify({\n messages: messages.map((m) => ({ role: m.role, content: m.content })),\n temperature: options.temperature,\n max_tokens: options.maxOutputTokens,\n }),\n signal: AbortSignal.timeout(this.timeoutMs),\n },\n );\n if (!response.ok) {\n const body = await response.text();\n throw new Error(\n `Model Serving invocation failed (${response.status}): ${body.slice(0, 500)}`,\n );\n }\n const payload = (await response.json()) as unknown;\n const parsed = parseServingResponse(payload);\n return {\n text: parsed.text,\n tokens: {\n inputTokens: parsed.inputTokens,\n outputTokens: parsed.outputTokens,\n },\n };\n }\n\n private async resolveToken(): Promise<string> {\n if (this.auth.kind === 'pat') return this.auth.token;\n if (this.oauthToken && this.oauthToken.expiresAt > Date.now() + 60_000) {\n return this.oauthToken.value;\n }\n const response = await this.fetchImpl(`${this.host}/oidc/v1/token`, {\n method: 'POST',\n headers: {\n authorization: `Basic ${toBase64(`${this.auth.clientId}:${this.auth.clientSecret}`)}`,\n 'content-type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({ grant_type: 'client_credentials', scope: 'all-apis' }),\n signal: AbortSignal.timeout(this.timeoutMs),\n });\n if (!response.ok) throw new Error(`Databricks OAuth exchange failed (${response.status})`);\n const body = (await response.json()) as { access_token?: string; expires_in?: number };\n if (!body.access_token) throw new Error('Databricks OAuth exchange returned no access token');\n this.oauthToken = {\n value: body.access_token,\n expiresAt: Date.now() + (body.expires_in ?? 3600) * 1000,\n };\n return this.oauthToken.value;\n }\n}\n\nfunction parseServingResponse(payload: unknown): {\n text: string;\n inputTokens: number;\n outputTokens: number;\n} {\n if (!isRecord(payload)) throw new Error('Model Serving returned an invalid response object');\n const choices = payload.choices;\n const first = Array.isArray(choices) ? choices[0] : undefined;\n const message = isRecord(first) && isRecord(first.message) ? first.message : undefined;\n const text = message?.content;\n if (typeof text !== 'string' || text.trim() === '') {\n throw new Error('Model Serving returned no completion text');\n }\n const usage = isRecord(payload.usage) ? payload.usage : undefined;\n const inputTokens = usage?.prompt_tokens;\n const outputTokens = usage?.completion_tokens;\n if (!isTokenCount(inputTokens) || !isTokenCount(outputTokens)) {\n throw new Error('Model Serving returned invalid token usage');\n }\n return { text, inputTokens, outputTokens };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction isTokenCount(value: unknown): value is number {\n return typeof value === 'number' && Number.isInteger(value) && value >= 0;\n}\n\nfunction normalizeHost(host: string): string {\n return host.startsWith('http://') || host.startsWith('https://')\n ? host.replace(/\\/$/, '')\n : `https://${host.replace(/\\/$/, '')}`;\n}\n\n/** Node has Buffer; edge/browser runtimes have btoa. Credentials are ASCII. */\nfunction toBase64(value: string): string {\n if (typeof Buffer !== 'undefined') return Buffer.from(value).toString('base64');\n return btoa(value);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/databricks-auth.ts","../src/databricks-model-client.ts","../src/databricks-mlflow-client.ts"],"names":["z"],"mappings":"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","file":"databricks.cjs","sourcesContent":["/** Authentication accepted by Databricks workspace APIs. */\nexport type DatabricksAuth =\n | { kind: 'pat'; token: string }\n | { kind: 'oauth-m2m'; clientId: string; clientSecret: string };\n\n/**\n * Small, dependency-free bearer provider shared by the Model Serving and\n * MLflow adapters. OAuth tokens are cached until one minute before expiry.\n */\nexport class DatabricksBearerProvider {\n private readonly host: string;\n private readonly auth: DatabricksAuth;\n private readonly fetchImpl: typeof fetch;\n private readonly timeoutMs: number;\n private oauthToken: { value: string; expiresAt: number } | null = null;\n\n constructor(config: {\n host: string;\n auth: DatabricksAuth;\n fetchImpl?: typeof fetch;\n timeoutMs?: number;\n }) {\n this.host = normalizeDatabricksHost(config.host);\n this.auth = config.auth;\n this.fetchImpl = config.fetchImpl ?? fetch;\n this.timeoutMs = config.timeoutMs ?? 60_000;\n if (!Number.isFinite(this.timeoutMs) || this.timeoutMs <= 0) {\n throw new Error('Databricks timeoutMs must be positive');\n }\n }\n\n async token(): Promise<string> {\n if (this.auth.kind === 'pat') return this.auth.token;\n if (this.oauthToken && this.oauthToken.expiresAt > Date.now() + 60_000) {\n return this.oauthToken.value;\n }\n const response = await this.fetchImpl(`${this.host}/oidc/v1/token`, {\n method: 'POST',\n headers: {\n authorization: `Basic ${toBase64(`${this.auth.clientId}:${this.auth.clientSecret}`)}`,\n 'content-type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({ grant_type: 'client_credentials', scope: 'all-apis' }),\n signal: AbortSignal.timeout(this.timeoutMs),\n });\n if (!response.ok) throw new Error(`Databricks OAuth exchange failed (${response.status})`);\n const body = (await response.json()) as { access_token?: string; expires_in?: number };\n if (!body.access_token) throw new Error('Databricks OAuth exchange returned no access token');\n this.oauthToken = {\n value: body.access_token,\n expiresAt: Date.now() + (body.expires_in ?? 3600) * 1000,\n };\n return this.oauthToken.value;\n }\n}\n\nexport function normalizeDatabricksHost(host: string): string {\n return host.startsWith('http://') || host.startsWith('https://')\n ? host.replace(/\\/$/, '')\n : `https://${host.replace(/\\/$/, '')}`;\n}\n\n/** Node has Buffer; edge/browser runtimes have btoa. Credentials are ASCII. */\nfunction toBase64(value: string): string {\n if (typeof Buffer !== 'undefined') return Buffer.from(value).toString('base64');\n return btoa(value);\n}\n","import {\n type DatabricksAuth,\n DatabricksBearerProvider,\n normalizeDatabricksHost,\n} from './databricks-auth.js';\nimport type {\n ChatMessage,\n ModelClient,\n ModelCompletionOptions,\n ModelResponse,\n} from './model-client.js';\n\nexport type { DatabricksAuth } from './databricks-auth.js';\n\n/**\n * Transport-agnostic Databricks Model Serving chat client\n * (`/serving-endpoints/{name}/invocations`, OpenAI-compatible chat payload).\n *\n * Auth mirrors the warehouse wiring: a PAT (`token`) wins when provided,\n * otherwise M2M OAuth client credentials are exchanged at\n * `/oidc/v1/token` and cached per instance until shortly before expiry.\n *\n * The `fetchImpl` injection keeps this package free of HTTP-client deps and\n * lets tests assert the exact request shape. Both the control plane\n * (`apps/api/src/server/model-client.ts`) and the Temporal worker\n * (`agents/harness-deploy`) wrap this class with their own env resolution.\n */\n\nexport interface DatabricksModelClientConfig {\n /** Workspace host — bare (`dbc-x.cloud.databricks.com`) or with scheme. */\n host: string;\n /** Serving endpoint name; recorded in EvalScore.modelEndpoint. */\n endpoint: string;\n auth: DatabricksAuth;\n /** Injected for tests; defaults to global fetch. */\n fetchImpl?: typeof fetch;\n /** Abort Model Serving and OAuth calls after this duration. Default: 60s. */\n timeoutMs?: number;\n}\n\nexport class DatabricksModelClient implements ModelClient {\n readonly endpoint: string;\n /** Exposed for wiring assertions (deps-construction tests). */\n readonly authMode: DatabricksAuth['kind'];\n private readonly host: string;\n private readonly fetchImpl: typeof fetch;\n private readonly timeoutMs: number;\n private readonly bearer: DatabricksBearerProvider;\n\n constructor(config: DatabricksModelClientConfig) {\n this.host = normalizeDatabricksHost(config.host);\n this.endpoint = config.endpoint;\n this.authMode = config.auth.kind;\n this.fetchImpl = config.fetchImpl ?? fetch;\n this.timeoutMs = config.timeoutMs ?? 60_000;\n if (!Number.isFinite(this.timeoutMs) || this.timeoutMs <= 0) {\n throw new Error('Databricks Model Serving timeoutMs must be positive');\n }\n this.bearer = new DatabricksBearerProvider({\n host: this.host,\n auth: config.auth,\n fetchImpl: this.fetchImpl,\n timeoutMs: this.timeoutMs,\n });\n }\n\n async complete(\n messages: readonly ChatMessage[],\n options: ModelCompletionOptions,\n ): Promise<ModelResponse> {\n if (!Number.isInteger(options.maxOutputTokens) || options.maxOutputTokens <= 0) {\n throw new Error('maxOutputTokens must be a positive integer');\n }\n if (!Number.isFinite(options.temperature) || options.temperature < 0) {\n throw new Error('temperature must be a non-negative finite number');\n }\n const response = await this.fetchImpl(\n `${this.host}/serving-endpoints/${encodeURIComponent(this.endpoint)}/invocations`,\n {\n method: 'POST',\n headers: {\n authorization: `Bearer ${await this.bearer.token()}`,\n 'content-type': 'application/json',\n },\n body: JSON.stringify({\n messages: messages.map((m) => ({ role: m.role, content: m.content })),\n temperature: options.temperature,\n max_tokens: options.maxOutputTokens,\n }),\n signal: AbortSignal.timeout(this.timeoutMs),\n },\n );\n if (!response.ok) {\n const body = await response.text();\n throw new Error(\n `Model Serving invocation failed (${response.status}): ${body.slice(0, 500)}`,\n );\n }\n const payload = (await response.json()) as unknown;\n const parsed = parseServingResponse(payload);\n return {\n text: parsed.text,\n tokens: {\n inputTokens: parsed.inputTokens,\n outputTokens: parsed.outputTokens,\n },\n };\n }\n}\n\nfunction parseServingResponse(payload: unknown): {\n text: string;\n inputTokens: number;\n outputTokens: number;\n} {\n if (!isRecord(payload)) throw new Error('Model Serving returned an invalid response object');\n const choices = payload.choices;\n const first = Array.isArray(choices) ? choices[0] : undefined;\n const message = isRecord(first) && isRecord(first.message) ? first.message : undefined;\n const text = message?.content;\n if (typeof text !== 'string' || text.trim() === '') {\n throw new Error('Model Serving returned no completion text');\n }\n const usage = isRecord(payload.usage) ? payload.usage : undefined;\n const inputTokens = usage?.prompt_tokens;\n const outputTokens = usage?.completion_tokens;\n if (!isTokenCount(inputTokens) || !isTokenCount(outputTokens)) {\n throw new Error('Model Serving returned invalid token usage');\n }\n return { text, inputTokens, outputTokens };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction isTokenCount(value: unknown): value is number {\n return typeof value === 'number' && Number.isInteger(value) && value >= 0;\n}\n","import { z } from 'zod';\nimport {\n type DatabricksAuth,\n DatabricksBearerProvider,\n normalizeDatabricksHost,\n} from './databricks-auth.js';\n\nconst MlflowMetric = z.object({\n key: z.string().min(1),\n value: z.number(),\n timestamp: z.number().optional(),\n step: z.number().optional(),\n});\n\nconst MlflowRunResponse = z.object({\n run: z.object({\n info: z.object({\n run_id: z.string().min(1),\n experiment_id: z.string().min(1),\n status: z.string().min(1),\n start_time: z.number().optional(),\n end_time: z.number().optional(),\n artifact_uri: z.string().optional(),\n run_name: z.string().optional(),\n }),\n data: z\n .object({\n metrics: z.array(MlflowMetric).default([]),\n params: z.array(z.object({ key: z.string(), value: z.string() })).default([]),\n tags: z.array(z.object({ key: z.string(), value: z.string() })).default([]),\n })\n .default({ metrics: [], params: [], tags: [] }),\n }),\n});\n\nexport type DatabricksMlflowRun = z.infer<typeof MlflowRunResponse>['run'];\n\nexport interface DatabricksMlflowClientConfig {\n host: string;\n auth: DatabricksAuth;\n fetchImpl?: typeof fetch;\n timeoutMs?: number;\n}\n\n/** Read-only adapter for native managed-MLflow run evidence. */\nexport class DatabricksMlflowClient {\n readonly host: string;\n readonly authMode: DatabricksAuth['kind'];\n private readonly fetchImpl: typeof fetch;\n private readonly timeoutMs: number;\n private readonly bearer: DatabricksBearerProvider;\n\n constructor(config: DatabricksMlflowClientConfig) {\n this.host = normalizeDatabricksHost(config.host);\n this.authMode = config.auth.kind;\n this.fetchImpl = config.fetchImpl ?? fetch;\n this.timeoutMs = config.timeoutMs ?? 60_000;\n if (!Number.isFinite(this.timeoutMs) || this.timeoutMs <= 0) {\n throw new Error('Databricks MLflow timeoutMs must be positive');\n }\n this.bearer = new DatabricksBearerProvider({\n host: this.host,\n auth: config.auth,\n fetchImpl: this.fetchImpl,\n timeoutMs: this.timeoutMs,\n });\n }\n\n async getRun(runId: string): Promise<DatabricksMlflowRun> {\n if (!runId.trim()) throw new Error('MLflow runId is required');\n const url = new URL(`${this.host}/api/2.0/mlflow/runs/get`);\n url.searchParams.set('run_id', runId);\n const response = await this.fetchImpl(url, {\n headers: { authorization: `Bearer ${await this.bearer.token()}` },\n signal: AbortSignal.timeout(this.timeoutMs),\n });\n if (!response.ok) {\n const body = await response.text();\n throw new Error(\n `Databricks MLflow run lookup failed (${response.status}): ${body.slice(0, 500)}`,\n );\n }\n const parsed = MlflowRunResponse.safeParse(await response.json());\n if (!parsed.success) {\n throw new Error(\n `Databricks MLflow returned an invalid run: ${parsed.error.issues[0]?.message ?? 'schema mismatch'}`,\n );\n }\n return parsed.data.run;\n }\n\n runUrl(run: DatabricksMlflowRun): string {\n return `${this.host}/ml/experiments/${encodeURIComponent(run.info.experiment_id)}/runs/${encodeURIComponent(run.info.run_id)}`;\n }\n}\n\nexport type MlflowGateOperator = '>=' | '>' | '<=' | '<' | '==' | '!=';\n\nexport interface MlflowMetricGate {\n metric: string;\n operator: MlflowGateOperator;\n threshold: number;\n}\n\nexport interface MlflowGateResult extends MlflowMetricGate {\n actual: number | null;\n passed: boolean;\n reason?: 'missing-metric';\n}\n\n/** Parse `accuracy>=0.9,toxicity<=0.05` without owning MLflow scoring itself. */\nexport function parseMlflowMetricGates(value: string): MlflowMetricGate[] {\n const entries = value\n .split(',')\n .map((entry) => entry.trim())\n .filter(Boolean);\n if (entries.length === 0) throw new Error('At least one MLflow metric gate is required');\n return entries.map((entry) => {\n const match = /^(.+?)(>=|<=|==|!=|>|<)(-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:e[+-]?\\d+)?)$/i.exec(entry);\n if (!match) throw new Error(`Invalid MLflow metric gate '${entry}'`);\n const metric = match[1]!.trim();\n if (!metric) throw new Error(`Invalid MLflow metric gate '${entry}'`);\n return {\n metric,\n operator: match[2] as MlflowGateOperator,\n threshold: Number(match[3]),\n };\n });\n}\n\n/** Evaluate release policy over metrics already computed and retained by MLflow. */\nexport function evaluateMlflowMetricGates(\n run: DatabricksMlflowRun,\n gates: readonly MlflowMetricGate[],\n): MlflowGateResult[] {\n const metrics = new Map(run.data.metrics.map((metric) => [metric.key, metric.value]));\n return gates.map((gate) => {\n const actual = metrics.get(gate.metric) ?? null;\n return {\n ...gate,\n actual,\n passed: actual === null ? false : compare(actual, gate.operator, gate.threshold),\n ...(actual === null ? { reason: 'missing-metric' as const } : {}),\n };\n });\n}\n\nfunction compare(actual: number, operator: MlflowGateOperator, threshold: number): boolean {\n switch (operator) {\n case '>=':\n return actual >= threshold;\n case '>':\n return actual > threshold;\n case '<=':\n return actual <= threshold;\n case '<':\n return actual < threshold;\n case '==':\n return actual === threshold;\n case '!=':\n return actual !== threshold;\n }\n}\n"]}
|
package/dist/databricks.d.cts
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
import { M as ModelClient, C as ChatMessage, g as ModelCompletionOptions, h as ModelResponse } from './model-client-B7yk_i1s.cjs';
|
|
2
|
-
import 'zod';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
/** Authentication accepted by Databricks workspace APIs. */
|
|
5
|
+
type DatabricksAuth = {
|
|
6
|
+
kind: 'pat';
|
|
7
|
+
token: string;
|
|
8
|
+
} | {
|
|
9
|
+
kind: 'oauth-m2m';
|
|
10
|
+
clientId: string;
|
|
11
|
+
clientSecret: string;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Small, dependency-free bearer provider shared by the Model Serving and
|
|
15
|
+
* MLflow adapters. OAuth tokens are cached until one minute before expiry.
|
|
16
|
+
*/
|
|
17
|
+
declare class DatabricksBearerProvider {
|
|
18
|
+
private readonly host;
|
|
19
|
+
private readonly auth;
|
|
20
|
+
private readonly fetchImpl;
|
|
21
|
+
private readonly timeoutMs;
|
|
22
|
+
private oauthToken;
|
|
23
|
+
constructor(config: {
|
|
24
|
+
host: string;
|
|
25
|
+
auth: DatabricksAuth;
|
|
26
|
+
fetchImpl?: typeof fetch;
|
|
27
|
+
timeoutMs?: number;
|
|
28
|
+
});
|
|
29
|
+
token(): Promise<string>;
|
|
30
|
+
}
|
|
31
|
+
declare function normalizeDatabricksHost(host: string): string;
|
|
3
32
|
|
|
4
33
|
/**
|
|
5
34
|
* Transport-agnostic Databricks Model Serving chat client
|
|
@@ -14,14 +43,6 @@ import 'zod';
|
|
|
14
43
|
* (`apps/api/src/server/model-client.ts`) and the Temporal worker
|
|
15
44
|
* (`agents/harness-deploy`) wrap this class with their own env resolution.
|
|
16
45
|
*/
|
|
17
|
-
type DatabricksAuth = {
|
|
18
|
-
kind: 'pat';
|
|
19
|
-
token: string;
|
|
20
|
-
} | {
|
|
21
|
-
kind: 'oauth-m2m';
|
|
22
|
-
clientId: string;
|
|
23
|
-
clientSecret: string;
|
|
24
|
-
};
|
|
25
46
|
interface DatabricksModelClientConfig {
|
|
26
47
|
/** Workspace host — bare (`dbc-x.cloud.databricks.com`) or with scheme. */
|
|
27
48
|
host: string;
|
|
@@ -38,13 +59,250 @@ declare class DatabricksModelClient implements ModelClient {
|
|
|
38
59
|
/** Exposed for wiring assertions (deps-construction tests). */
|
|
39
60
|
readonly authMode: DatabricksAuth['kind'];
|
|
40
61
|
private readonly host;
|
|
41
|
-
private readonly auth;
|
|
42
62
|
private readonly fetchImpl;
|
|
43
63
|
private readonly timeoutMs;
|
|
44
|
-
private
|
|
64
|
+
private readonly bearer;
|
|
45
65
|
constructor(config: DatabricksModelClientConfig);
|
|
46
66
|
complete(messages: readonly ChatMessage[], options: ModelCompletionOptions): Promise<ModelResponse>;
|
|
47
|
-
private resolveToken;
|
|
48
67
|
}
|
|
49
68
|
|
|
50
|
-
|
|
69
|
+
declare const MlflowRunResponse: z.ZodObject<{
|
|
70
|
+
run: z.ZodObject<{
|
|
71
|
+
info: z.ZodObject<{
|
|
72
|
+
run_id: z.ZodString;
|
|
73
|
+
experiment_id: z.ZodString;
|
|
74
|
+
status: z.ZodString;
|
|
75
|
+
start_time: z.ZodOptional<z.ZodNumber>;
|
|
76
|
+
end_time: z.ZodOptional<z.ZodNumber>;
|
|
77
|
+
artifact_uri: z.ZodOptional<z.ZodString>;
|
|
78
|
+
run_name: z.ZodOptional<z.ZodString>;
|
|
79
|
+
}, "strip", z.ZodTypeAny, {
|
|
80
|
+
status: string;
|
|
81
|
+
run_id: string;
|
|
82
|
+
experiment_id: string;
|
|
83
|
+
start_time?: number | undefined;
|
|
84
|
+
end_time?: number | undefined;
|
|
85
|
+
artifact_uri?: string | undefined;
|
|
86
|
+
run_name?: string | undefined;
|
|
87
|
+
}, {
|
|
88
|
+
status: string;
|
|
89
|
+
run_id: string;
|
|
90
|
+
experiment_id: string;
|
|
91
|
+
start_time?: number | undefined;
|
|
92
|
+
end_time?: number | undefined;
|
|
93
|
+
artifact_uri?: string | undefined;
|
|
94
|
+
run_name?: string | undefined;
|
|
95
|
+
}>;
|
|
96
|
+
data: z.ZodDefault<z.ZodObject<{
|
|
97
|
+
metrics: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
98
|
+
key: z.ZodString;
|
|
99
|
+
value: z.ZodNumber;
|
|
100
|
+
timestamp: z.ZodOptional<z.ZodNumber>;
|
|
101
|
+
step: z.ZodOptional<z.ZodNumber>;
|
|
102
|
+
}, "strip", z.ZodTypeAny, {
|
|
103
|
+
value: number;
|
|
104
|
+
key: string;
|
|
105
|
+
timestamp?: number | undefined;
|
|
106
|
+
step?: number | undefined;
|
|
107
|
+
}, {
|
|
108
|
+
value: number;
|
|
109
|
+
key: string;
|
|
110
|
+
timestamp?: number | undefined;
|
|
111
|
+
step?: number | undefined;
|
|
112
|
+
}>, "many">>;
|
|
113
|
+
params: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
114
|
+
key: z.ZodString;
|
|
115
|
+
value: z.ZodString;
|
|
116
|
+
}, "strip", z.ZodTypeAny, {
|
|
117
|
+
value: string;
|
|
118
|
+
key: string;
|
|
119
|
+
}, {
|
|
120
|
+
value: string;
|
|
121
|
+
key: string;
|
|
122
|
+
}>, "many">>;
|
|
123
|
+
tags: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
124
|
+
key: z.ZodString;
|
|
125
|
+
value: z.ZodString;
|
|
126
|
+
}, "strip", z.ZodTypeAny, {
|
|
127
|
+
value: string;
|
|
128
|
+
key: string;
|
|
129
|
+
}, {
|
|
130
|
+
value: string;
|
|
131
|
+
key: string;
|
|
132
|
+
}>, "many">>;
|
|
133
|
+
}, "strip", z.ZodTypeAny, {
|
|
134
|
+
params: {
|
|
135
|
+
value: string;
|
|
136
|
+
key: string;
|
|
137
|
+
}[];
|
|
138
|
+
metrics: {
|
|
139
|
+
value: number;
|
|
140
|
+
key: string;
|
|
141
|
+
timestamp?: number | undefined;
|
|
142
|
+
step?: number | undefined;
|
|
143
|
+
}[];
|
|
144
|
+
tags: {
|
|
145
|
+
value: string;
|
|
146
|
+
key: string;
|
|
147
|
+
}[];
|
|
148
|
+
}, {
|
|
149
|
+
params?: {
|
|
150
|
+
value: string;
|
|
151
|
+
key: string;
|
|
152
|
+
}[] | undefined;
|
|
153
|
+
metrics?: {
|
|
154
|
+
value: number;
|
|
155
|
+
key: string;
|
|
156
|
+
timestamp?: number | undefined;
|
|
157
|
+
step?: number | undefined;
|
|
158
|
+
}[] | undefined;
|
|
159
|
+
tags?: {
|
|
160
|
+
value: string;
|
|
161
|
+
key: string;
|
|
162
|
+
}[] | undefined;
|
|
163
|
+
}>>;
|
|
164
|
+
}, "strip", z.ZodTypeAny, {
|
|
165
|
+
info: {
|
|
166
|
+
status: string;
|
|
167
|
+
run_id: string;
|
|
168
|
+
experiment_id: string;
|
|
169
|
+
start_time?: number | undefined;
|
|
170
|
+
end_time?: number | undefined;
|
|
171
|
+
artifact_uri?: string | undefined;
|
|
172
|
+
run_name?: string | undefined;
|
|
173
|
+
};
|
|
174
|
+
data: {
|
|
175
|
+
params: {
|
|
176
|
+
value: string;
|
|
177
|
+
key: string;
|
|
178
|
+
}[];
|
|
179
|
+
metrics: {
|
|
180
|
+
value: number;
|
|
181
|
+
key: string;
|
|
182
|
+
timestamp?: number | undefined;
|
|
183
|
+
step?: number | undefined;
|
|
184
|
+
}[];
|
|
185
|
+
tags: {
|
|
186
|
+
value: string;
|
|
187
|
+
key: string;
|
|
188
|
+
}[];
|
|
189
|
+
};
|
|
190
|
+
}, {
|
|
191
|
+
info: {
|
|
192
|
+
status: string;
|
|
193
|
+
run_id: string;
|
|
194
|
+
experiment_id: string;
|
|
195
|
+
start_time?: number | undefined;
|
|
196
|
+
end_time?: number | undefined;
|
|
197
|
+
artifact_uri?: string | undefined;
|
|
198
|
+
run_name?: string | undefined;
|
|
199
|
+
};
|
|
200
|
+
data?: {
|
|
201
|
+
params?: {
|
|
202
|
+
value: string;
|
|
203
|
+
key: string;
|
|
204
|
+
}[] | undefined;
|
|
205
|
+
metrics?: {
|
|
206
|
+
value: number;
|
|
207
|
+
key: string;
|
|
208
|
+
timestamp?: number | undefined;
|
|
209
|
+
step?: number | undefined;
|
|
210
|
+
}[] | undefined;
|
|
211
|
+
tags?: {
|
|
212
|
+
value: string;
|
|
213
|
+
key: string;
|
|
214
|
+
}[] | undefined;
|
|
215
|
+
} | undefined;
|
|
216
|
+
}>;
|
|
217
|
+
}, "strip", z.ZodTypeAny, {
|
|
218
|
+
run: {
|
|
219
|
+
info: {
|
|
220
|
+
status: string;
|
|
221
|
+
run_id: string;
|
|
222
|
+
experiment_id: string;
|
|
223
|
+
start_time?: number | undefined;
|
|
224
|
+
end_time?: number | undefined;
|
|
225
|
+
artifact_uri?: string | undefined;
|
|
226
|
+
run_name?: string | undefined;
|
|
227
|
+
};
|
|
228
|
+
data: {
|
|
229
|
+
params: {
|
|
230
|
+
value: string;
|
|
231
|
+
key: string;
|
|
232
|
+
}[];
|
|
233
|
+
metrics: {
|
|
234
|
+
value: number;
|
|
235
|
+
key: string;
|
|
236
|
+
timestamp?: number | undefined;
|
|
237
|
+
step?: number | undefined;
|
|
238
|
+
}[];
|
|
239
|
+
tags: {
|
|
240
|
+
value: string;
|
|
241
|
+
key: string;
|
|
242
|
+
}[];
|
|
243
|
+
};
|
|
244
|
+
};
|
|
245
|
+
}, {
|
|
246
|
+
run: {
|
|
247
|
+
info: {
|
|
248
|
+
status: string;
|
|
249
|
+
run_id: string;
|
|
250
|
+
experiment_id: string;
|
|
251
|
+
start_time?: number | undefined;
|
|
252
|
+
end_time?: number | undefined;
|
|
253
|
+
artifact_uri?: string | undefined;
|
|
254
|
+
run_name?: string | undefined;
|
|
255
|
+
};
|
|
256
|
+
data?: {
|
|
257
|
+
params?: {
|
|
258
|
+
value: string;
|
|
259
|
+
key: string;
|
|
260
|
+
}[] | undefined;
|
|
261
|
+
metrics?: {
|
|
262
|
+
value: number;
|
|
263
|
+
key: string;
|
|
264
|
+
timestamp?: number | undefined;
|
|
265
|
+
step?: number | undefined;
|
|
266
|
+
}[] | undefined;
|
|
267
|
+
tags?: {
|
|
268
|
+
value: string;
|
|
269
|
+
key: string;
|
|
270
|
+
}[] | undefined;
|
|
271
|
+
} | undefined;
|
|
272
|
+
};
|
|
273
|
+
}>;
|
|
274
|
+
type DatabricksMlflowRun = z.infer<typeof MlflowRunResponse>['run'];
|
|
275
|
+
interface DatabricksMlflowClientConfig {
|
|
276
|
+
host: string;
|
|
277
|
+
auth: DatabricksAuth;
|
|
278
|
+
fetchImpl?: typeof fetch;
|
|
279
|
+
timeoutMs?: number;
|
|
280
|
+
}
|
|
281
|
+
/** Read-only adapter for native managed-MLflow run evidence. */
|
|
282
|
+
declare class DatabricksMlflowClient {
|
|
283
|
+
readonly host: string;
|
|
284
|
+
readonly authMode: DatabricksAuth['kind'];
|
|
285
|
+
private readonly fetchImpl;
|
|
286
|
+
private readonly timeoutMs;
|
|
287
|
+
private readonly bearer;
|
|
288
|
+
constructor(config: DatabricksMlflowClientConfig);
|
|
289
|
+
getRun(runId: string): Promise<DatabricksMlflowRun>;
|
|
290
|
+
runUrl(run: DatabricksMlflowRun): string;
|
|
291
|
+
}
|
|
292
|
+
type MlflowGateOperator = '>=' | '>' | '<=' | '<' | '==' | '!=';
|
|
293
|
+
interface MlflowMetricGate {
|
|
294
|
+
metric: string;
|
|
295
|
+
operator: MlflowGateOperator;
|
|
296
|
+
threshold: number;
|
|
297
|
+
}
|
|
298
|
+
interface MlflowGateResult extends MlflowMetricGate {
|
|
299
|
+
actual: number | null;
|
|
300
|
+
passed: boolean;
|
|
301
|
+
reason?: 'missing-metric';
|
|
302
|
+
}
|
|
303
|
+
/** Parse `accuracy>=0.9,toxicity<=0.05` without owning MLflow scoring itself. */
|
|
304
|
+
declare function parseMlflowMetricGates(value: string): MlflowMetricGate[];
|
|
305
|
+
/** Evaluate release policy over metrics already computed and retained by MLflow. */
|
|
306
|
+
declare function evaluateMlflowMetricGates(run: DatabricksMlflowRun, gates: readonly MlflowMetricGate[]): MlflowGateResult[];
|
|
307
|
+
|
|
308
|
+
export { type DatabricksAuth, DatabricksBearerProvider, DatabricksMlflowClient, type DatabricksMlflowClientConfig, type DatabricksMlflowRun, DatabricksModelClient, type DatabricksModelClientConfig, type MlflowGateOperator, type MlflowGateResult, type MlflowMetricGate, evaluateMlflowMetricGates, normalizeDatabricksHost, parseMlflowMetricGates };
|
package/dist/databricks.d.ts
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
import { M as ModelClient, C as ChatMessage, g as ModelCompletionOptions, h as ModelResponse } from './model-client-B7yk_i1s.js';
|
|
2
|
-
import 'zod';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
/** Authentication accepted by Databricks workspace APIs. */
|
|
5
|
+
type DatabricksAuth = {
|
|
6
|
+
kind: 'pat';
|
|
7
|
+
token: string;
|
|
8
|
+
} | {
|
|
9
|
+
kind: 'oauth-m2m';
|
|
10
|
+
clientId: string;
|
|
11
|
+
clientSecret: string;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Small, dependency-free bearer provider shared by the Model Serving and
|
|
15
|
+
* MLflow adapters. OAuth tokens are cached until one minute before expiry.
|
|
16
|
+
*/
|
|
17
|
+
declare class DatabricksBearerProvider {
|
|
18
|
+
private readonly host;
|
|
19
|
+
private readonly auth;
|
|
20
|
+
private readonly fetchImpl;
|
|
21
|
+
private readonly timeoutMs;
|
|
22
|
+
private oauthToken;
|
|
23
|
+
constructor(config: {
|
|
24
|
+
host: string;
|
|
25
|
+
auth: DatabricksAuth;
|
|
26
|
+
fetchImpl?: typeof fetch;
|
|
27
|
+
timeoutMs?: number;
|
|
28
|
+
});
|
|
29
|
+
token(): Promise<string>;
|
|
30
|
+
}
|
|
31
|
+
declare function normalizeDatabricksHost(host: string): string;
|
|
3
32
|
|
|
4
33
|
/**
|
|
5
34
|
* Transport-agnostic Databricks Model Serving chat client
|
|
@@ -14,14 +43,6 @@ import 'zod';
|
|
|
14
43
|
* (`apps/api/src/server/model-client.ts`) and the Temporal worker
|
|
15
44
|
* (`agents/harness-deploy`) wrap this class with their own env resolution.
|
|
16
45
|
*/
|
|
17
|
-
type DatabricksAuth = {
|
|
18
|
-
kind: 'pat';
|
|
19
|
-
token: string;
|
|
20
|
-
} | {
|
|
21
|
-
kind: 'oauth-m2m';
|
|
22
|
-
clientId: string;
|
|
23
|
-
clientSecret: string;
|
|
24
|
-
};
|
|
25
46
|
interface DatabricksModelClientConfig {
|
|
26
47
|
/** Workspace host — bare (`dbc-x.cloud.databricks.com`) or with scheme. */
|
|
27
48
|
host: string;
|
|
@@ -38,13 +59,250 @@ declare class DatabricksModelClient implements ModelClient {
|
|
|
38
59
|
/** Exposed for wiring assertions (deps-construction tests). */
|
|
39
60
|
readonly authMode: DatabricksAuth['kind'];
|
|
40
61
|
private readonly host;
|
|
41
|
-
private readonly auth;
|
|
42
62
|
private readonly fetchImpl;
|
|
43
63
|
private readonly timeoutMs;
|
|
44
|
-
private
|
|
64
|
+
private readonly bearer;
|
|
45
65
|
constructor(config: DatabricksModelClientConfig);
|
|
46
66
|
complete(messages: readonly ChatMessage[], options: ModelCompletionOptions): Promise<ModelResponse>;
|
|
47
|
-
private resolveToken;
|
|
48
67
|
}
|
|
49
68
|
|
|
50
|
-
|
|
69
|
+
declare const MlflowRunResponse: z.ZodObject<{
|
|
70
|
+
run: z.ZodObject<{
|
|
71
|
+
info: z.ZodObject<{
|
|
72
|
+
run_id: z.ZodString;
|
|
73
|
+
experiment_id: z.ZodString;
|
|
74
|
+
status: z.ZodString;
|
|
75
|
+
start_time: z.ZodOptional<z.ZodNumber>;
|
|
76
|
+
end_time: z.ZodOptional<z.ZodNumber>;
|
|
77
|
+
artifact_uri: z.ZodOptional<z.ZodString>;
|
|
78
|
+
run_name: z.ZodOptional<z.ZodString>;
|
|
79
|
+
}, "strip", z.ZodTypeAny, {
|
|
80
|
+
status: string;
|
|
81
|
+
run_id: string;
|
|
82
|
+
experiment_id: string;
|
|
83
|
+
start_time?: number | undefined;
|
|
84
|
+
end_time?: number | undefined;
|
|
85
|
+
artifact_uri?: string | undefined;
|
|
86
|
+
run_name?: string | undefined;
|
|
87
|
+
}, {
|
|
88
|
+
status: string;
|
|
89
|
+
run_id: string;
|
|
90
|
+
experiment_id: string;
|
|
91
|
+
start_time?: number | undefined;
|
|
92
|
+
end_time?: number | undefined;
|
|
93
|
+
artifact_uri?: string | undefined;
|
|
94
|
+
run_name?: string | undefined;
|
|
95
|
+
}>;
|
|
96
|
+
data: z.ZodDefault<z.ZodObject<{
|
|
97
|
+
metrics: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
98
|
+
key: z.ZodString;
|
|
99
|
+
value: z.ZodNumber;
|
|
100
|
+
timestamp: z.ZodOptional<z.ZodNumber>;
|
|
101
|
+
step: z.ZodOptional<z.ZodNumber>;
|
|
102
|
+
}, "strip", z.ZodTypeAny, {
|
|
103
|
+
value: number;
|
|
104
|
+
key: string;
|
|
105
|
+
timestamp?: number | undefined;
|
|
106
|
+
step?: number | undefined;
|
|
107
|
+
}, {
|
|
108
|
+
value: number;
|
|
109
|
+
key: string;
|
|
110
|
+
timestamp?: number | undefined;
|
|
111
|
+
step?: number | undefined;
|
|
112
|
+
}>, "many">>;
|
|
113
|
+
params: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
114
|
+
key: z.ZodString;
|
|
115
|
+
value: z.ZodString;
|
|
116
|
+
}, "strip", z.ZodTypeAny, {
|
|
117
|
+
value: string;
|
|
118
|
+
key: string;
|
|
119
|
+
}, {
|
|
120
|
+
value: string;
|
|
121
|
+
key: string;
|
|
122
|
+
}>, "many">>;
|
|
123
|
+
tags: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
124
|
+
key: z.ZodString;
|
|
125
|
+
value: z.ZodString;
|
|
126
|
+
}, "strip", z.ZodTypeAny, {
|
|
127
|
+
value: string;
|
|
128
|
+
key: string;
|
|
129
|
+
}, {
|
|
130
|
+
value: string;
|
|
131
|
+
key: string;
|
|
132
|
+
}>, "many">>;
|
|
133
|
+
}, "strip", z.ZodTypeAny, {
|
|
134
|
+
params: {
|
|
135
|
+
value: string;
|
|
136
|
+
key: string;
|
|
137
|
+
}[];
|
|
138
|
+
metrics: {
|
|
139
|
+
value: number;
|
|
140
|
+
key: string;
|
|
141
|
+
timestamp?: number | undefined;
|
|
142
|
+
step?: number | undefined;
|
|
143
|
+
}[];
|
|
144
|
+
tags: {
|
|
145
|
+
value: string;
|
|
146
|
+
key: string;
|
|
147
|
+
}[];
|
|
148
|
+
}, {
|
|
149
|
+
params?: {
|
|
150
|
+
value: string;
|
|
151
|
+
key: string;
|
|
152
|
+
}[] | undefined;
|
|
153
|
+
metrics?: {
|
|
154
|
+
value: number;
|
|
155
|
+
key: string;
|
|
156
|
+
timestamp?: number | undefined;
|
|
157
|
+
step?: number | undefined;
|
|
158
|
+
}[] | undefined;
|
|
159
|
+
tags?: {
|
|
160
|
+
value: string;
|
|
161
|
+
key: string;
|
|
162
|
+
}[] | undefined;
|
|
163
|
+
}>>;
|
|
164
|
+
}, "strip", z.ZodTypeAny, {
|
|
165
|
+
info: {
|
|
166
|
+
status: string;
|
|
167
|
+
run_id: string;
|
|
168
|
+
experiment_id: string;
|
|
169
|
+
start_time?: number | undefined;
|
|
170
|
+
end_time?: number | undefined;
|
|
171
|
+
artifact_uri?: string | undefined;
|
|
172
|
+
run_name?: string | undefined;
|
|
173
|
+
};
|
|
174
|
+
data: {
|
|
175
|
+
params: {
|
|
176
|
+
value: string;
|
|
177
|
+
key: string;
|
|
178
|
+
}[];
|
|
179
|
+
metrics: {
|
|
180
|
+
value: number;
|
|
181
|
+
key: string;
|
|
182
|
+
timestamp?: number | undefined;
|
|
183
|
+
step?: number | undefined;
|
|
184
|
+
}[];
|
|
185
|
+
tags: {
|
|
186
|
+
value: string;
|
|
187
|
+
key: string;
|
|
188
|
+
}[];
|
|
189
|
+
};
|
|
190
|
+
}, {
|
|
191
|
+
info: {
|
|
192
|
+
status: string;
|
|
193
|
+
run_id: string;
|
|
194
|
+
experiment_id: string;
|
|
195
|
+
start_time?: number | undefined;
|
|
196
|
+
end_time?: number | undefined;
|
|
197
|
+
artifact_uri?: string | undefined;
|
|
198
|
+
run_name?: string | undefined;
|
|
199
|
+
};
|
|
200
|
+
data?: {
|
|
201
|
+
params?: {
|
|
202
|
+
value: string;
|
|
203
|
+
key: string;
|
|
204
|
+
}[] | undefined;
|
|
205
|
+
metrics?: {
|
|
206
|
+
value: number;
|
|
207
|
+
key: string;
|
|
208
|
+
timestamp?: number | undefined;
|
|
209
|
+
step?: number | undefined;
|
|
210
|
+
}[] | undefined;
|
|
211
|
+
tags?: {
|
|
212
|
+
value: string;
|
|
213
|
+
key: string;
|
|
214
|
+
}[] | undefined;
|
|
215
|
+
} | undefined;
|
|
216
|
+
}>;
|
|
217
|
+
}, "strip", z.ZodTypeAny, {
|
|
218
|
+
run: {
|
|
219
|
+
info: {
|
|
220
|
+
status: string;
|
|
221
|
+
run_id: string;
|
|
222
|
+
experiment_id: string;
|
|
223
|
+
start_time?: number | undefined;
|
|
224
|
+
end_time?: number | undefined;
|
|
225
|
+
artifact_uri?: string | undefined;
|
|
226
|
+
run_name?: string | undefined;
|
|
227
|
+
};
|
|
228
|
+
data: {
|
|
229
|
+
params: {
|
|
230
|
+
value: string;
|
|
231
|
+
key: string;
|
|
232
|
+
}[];
|
|
233
|
+
metrics: {
|
|
234
|
+
value: number;
|
|
235
|
+
key: string;
|
|
236
|
+
timestamp?: number | undefined;
|
|
237
|
+
step?: number | undefined;
|
|
238
|
+
}[];
|
|
239
|
+
tags: {
|
|
240
|
+
value: string;
|
|
241
|
+
key: string;
|
|
242
|
+
}[];
|
|
243
|
+
};
|
|
244
|
+
};
|
|
245
|
+
}, {
|
|
246
|
+
run: {
|
|
247
|
+
info: {
|
|
248
|
+
status: string;
|
|
249
|
+
run_id: string;
|
|
250
|
+
experiment_id: string;
|
|
251
|
+
start_time?: number | undefined;
|
|
252
|
+
end_time?: number | undefined;
|
|
253
|
+
artifact_uri?: string | undefined;
|
|
254
|
+
run_name?: string | undefined;
|
|
255
|
+
};
|
|
256
|
+
data?: {
|
|
257
|
+
params?: {
|
|
258
|
+
value: string;
|
|
259
|
+
key: string;
|
|
260
|
+
}[] | undefined;
|
|
261
|
+
metrics?: {
|
|
262
|
+
value: number;
|
|
263
|
+
key: string;
|
|
264
|
+
timestamp?: number | undefined;
|
|
265
|
+
step?: number | undefined;
|
|
266
|
+
}[] | undefined;
|
|
267
|
+
tags?: {
|
|
268
|
+
value: string;
|
|
269
|
+
key: string;
|
|
270
|
+
}[] | undefined;
|
|
271
|
+
} | undefined;
|
|
272
|
+
};
|
|
273
|
+
}>;
|
|
274
|
+
type DatabricksMlflowRun = z.infer<typeof MlflowRunResponse>['run'];
|
|
275
|
+
interface DatabricksMlflowClientConfig {
|
|
276
|
+
host: string;
|
|
277
|
+
auth: DatabricksAuth;
|
|
278
|
+
fetchImpl?: typeof fetch;
|
|
279
|
+
timeoutMs?: number;
|
|
280
|
+
}
|
|
281
|
+
/** Read-only adapter for native managed-MLflow run evidence. */
|
|
282
|
+
declare class DatabricksMlflowClient {
|
|
283
|
+
readonly host: string;
|
|
284
|
+
readonly authMode: DatabricksAuth['kind'];
|
|
285
|
+
private readonly fetchImpl;
|
|
286
|
+
private readonly timeoutMs;
|
|
287
|
+
private readonly bearer;
|
|
288
|
+
constructor(config: DatabricksMlflowClientConfig);
|
|
289
|
+
getRun(runId: string): Promise<DatabricksMlflowRun>;
|
|
290
|
+
runUrl(run: DatabricksMlflowRun): string;
|
|
291
|
+
}
|
|
292
|
+
type MlflowGateOperator = '>=' | '>' | '<=' | '<' | '==' | '!=';
|
|
293
|
+
interface MlflowMetricGate {
|
|
294
|
+
metric: string;
|
|
295
|
+
operator: MlflowGateOperator;
|
|
296
|
+
threshold: number;
|
|
297
|
+
}
|
|
298
|
+
interface MlflowGateResult extends MlflowMetricGate {
|
|
299
|
+
actual: number | null;
|
|
300
|
+
passed: boolean;
|
|
301
|
+
reason?: 'missing-metric';
|
|
302
|
+
}
|
|
303
|
+
/** Parse `accuracy>=0.9,toxicity<=0.05` without owning MLflow scoring itself. */
|
|
304
|
+
declare function parseMlflowMetricGates(value: string): MlflowMetricGate[];
|
|
305
|
+
/** Evaluate release policy over metrics already computed and retained by MLflow. */
|
|
306
|
+
declare function evaluateMlflowMetricGates(run: DatabricksMlflowRun, gates: readonly MlflowMetricGate[]): MlflowGateResult[];
|
|
307
|
+
|
|
308
|
+
export { type DatabricksAuth, DatabricksBearerProvider, DatabricksMlflowClient, type DatabricksMlflowClientConfig, type DatabricksMlflowRun, DatabricksModelClient, type DatabricksModelClientConfig, type MlflowGateOperator, type MlflowGateResult, type MlflowMetricGate, evaluateMlflowMetricGates, normalizeDatabricksHost, parseMlflowMetricGates };
|
package/dist/databricks.js
CHANGED
|
@@ -1,23 +1,77 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
// src/databricks-auth.ts
|
|
4
|
+
var DatabricksBearerProvider = class {
|
|
5
|
+
host;
|
|
6
|
+
auth;
|
|
7
|
+
fetchImpl;
|
|
8
|
+
timeoutMs;
|
|
9
|
+
oauthToken = null;
|
|
10
|
+
constructor(config) {
|
|
11
|
+
this.host = normalizeDatabricksHost(config.host);
|
|
12
|
+
this.auth = config.auth;
|
|
13
|
+
this.fetchImpl = config.fetchImpl ?? fetch;
|
|
14
|
+
this.timeoutMs = config.timeoutMs ?? 6e4;
|
|
15
|
+
if (!Number.isFinite(this.timeoutMs) || this.timeoutMs <= 0) {
|
|
16
|
+
throw new Error("Databricks timeoutMs must be positive");
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
async token() {
|
|
20
|
+
if (this.auth.kind === "pat") return this.auth.token;
|
|
21
|
+
if (this.oauthToken && this.oauthToken.expiresAt > Date.now() + 6e4) {
|
|
22
|
+
return this.oauthToken.value;
|
|
23
|
+
}
|
|
24
|
+
const response = await this.fetchImpl(`${this.host}/oidc/v1/token`, {
|
|
25
|
+
method: "POST",
|
|
26
|
+
headers: {
|
|
27
|
+
authorization: `Basic ${toBase64(`${this.auth.clientId}:${this.auth.clientSecret}`)}`,
|
|
28
|
+
"content-type": "application/x-www-form-urlencoded"
|
|
29
|
+
},
|
|
30
|
+
body: new URLSearchParams({ grant_type: "client_credentials", scope: "all-apis" }),
|
|
31
|
+
signal: AbortSignal.timeout(this.timeoutMs)
|
|
32
|
+
});
|
|
33
|
+
if (!response.ok) throw new Error(`Databricks OAuth exchange failed (${response.status})`);
|
|
34
|
+
const body = await response.json();
|
|
35
|
+
if (!body.access_token) throw new Error("Databricks OAuth exchange returned no access token");
|
|
36
|
+
this.oauthToken = {
|
|
37
|
+
value: body.access_token,
|
|
38
|
+
expiresAt: Date.now() + (body.expires_in ?? 3600) * 1e3
|
|
39
|
+
};
|
|
40
|
+
return this.oauthToken.value;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
function normalizeDatabricksHost(host) {
|
|
44
|
+
return host.startsWith("http://") || host.startsWith("https://") ? host.replace(/\/$/, "") : `https://${host.replace(/\/$/, "")}`;
|
|
45
|
+
}
|
|
46
|
+
function toBase64(value) {
|
|
47
|
+
if (typeof Buffer !== "undefined") return Buffer.from(value).toString("base64");
|
|
48
|
+
return btoa(value);
|
|
49
|
+
}
|
|
50
|
+
|
|
1
51
|
// src/databricks-model-client.ts
|
|
2
52
|
var DatabricksModelClient = class {
|
|
3
53
|
endpoint;
|
|
4
54
|
/** Exposed for wiring assertions (deps-construction tests). */
|
|
5
55
|
authMode;
|
|
6
56
|
host;
|
|
7
|
-
auth;
|
|
8
57
|
fetchImpl;
|
|
9
58
|
timeoutMs;
|
|
10
|
-
|
|
59
|
+
bearer;
|
|
11
60
|
constructor(config) {
|
|
12
|
-
this.host =
|
|
61
|
+
this.host = normalizeDatabricksHost(config.host);
|
|
13
62
|
this.endpoint = config.endpoint;
|
|
14
|
-
this.auth = config.auth;
|
|
15
63
|
this.authMode = config.auth.kind;
|
|
16
64
|
this.fetchImpl = config.fetchImpl ?? fetch;
|
|
17
65
|
this.timeoutMs = config.timeoutMs ?? 6e4;
|
|
18
66
|
if (!Number.isFinite(this.timeoutMs) || this.timeoutMs <= 0) {
|
|
19
67
|
throw new Error("Databricks Model Serving timeoutMs must be positive");
|
|
20
68
|
}
|
|
69
|
+
this.bearer = new DatabricksBearerProvider({
|
|
70
|
+
host: this.host,
|
|
71
|
+
auth: config.auth,
|
|
72
|
+
fetchImpl: this.fetchImpl,
|
|
73
|
+
timeoutMs: this.timeoutMs
|
|
74
|
+
});
|
|
21
75
|
}
|
|
22
76
|
async complete(messages, options) {
|
|
23
77
|
if (!Number.isInteger(options.maxOutputTokens) || options.maxOutputTokens <= 0) {
|
|
@@ -31,7 +85,7 @@ var DatabricksModelClient = class {
|
|
|
31
85
|
{
|
|
32
86
|
method: "POST",
|
|
33
87
|
headers: {
|
|
34
|
-
authorization: `Bearer ${await this.
|
|
88
|
+
authorization: `Bearer ${await this.bearer.token()}`,
|
|
35
89
|
"content-type": "application/json"
|
|
36
90
|
},
|
|
37
91
|
body: JSON.stringify({
|
|
@@ -58,29 +112,6 @@ var DatabricksModelClient = class {
|
|
|
58
112
|
}
|
|
59
113
|
};
|
|
60
114
|
}
|
|
61
|
-
async resolveToken() {
|
|
62
|
-
if (this.auth.kind === "pat") return this.auth.token;
|
|
63
|
-
if (this.oauthToken && this.oauthToken.expiresAt > Date.now() + 6e4) {
|
|
64
|
-
return this.oauthToken.value;
|
|
65
|
-
}
|
|
66
|
-
const response = await this.fetchImpl(`${this.host}/oidc/v1/token`, {
|
|
67
|
-
method: "POST",
|
|
68
|
-
headers: {
|
|
69
|
-
authorization: `Basic ${toBase64(`${this.auth.clientId}:${this.auth.clientSecret}`)}`,
|
|
70
|
-
"content-type": "application/x-www-form-urlencoded"
|
|
71
|
-
},
|
|
72
|
-
body: new URLSearchParams({ grant_type: "client_credentials", scope: "all-apis" }),
|
|
73
|
-
signal: AbortSignal.timeout(this.timeoutMs)
|
|
74
|
-
});
|
|
75
|
-
if (!response.ok) throw new Error(`Databricks OAuth exchange failed (${response.status})`);
|
|
76
|
-
const body = await response.json();
|
|
77
|
-
if (!body.access_token) throw new Error("Databricks OAuth exchange returned no access token");
|
|
78
|
-
this.oauthToken = {
|
|
79
|
-
value: body.access_token,
|
|
80
|
-
expiresAt: Date.now() + (body.expires_in ?? 3600) * 1e3
|
|
81
|
-
};
|
|
82
|
-
return this.oauthToken.value;
|
|
83
|
-
}
|
|
84
115
|
};
|
|
85
116
|
function parseServingResponse(payload) {
|
|
86
117
|
if (!isRecord(payload)) throw new Error("Model Serving returned an invalid response object");
|
|
@@ -105,14 +136,121 @@ function isRecord(value) {
|
|
|
105
136
|
function isTokenCount(value) {
|
|
106
137
|
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
107
138
|
}
|
|
108
|
-
|
|
109
|
-
|
|
139
|
+
var MlflowMetric = z.object({
|
|
140
|
+
key: z.string().min(1),
|
|
141
|
+
value: z.number(),
|
|
142
|
+
timestamp: z.number().optional(),
|
|
143
|
+
step: z.number().optional()
|
|
144
|
+
});
|
|
145
|
+
var MlflowRunResponse = z.object({
|
|
146
|
+
run: z.object({
|
|
147
|
+
info: z.object({
|
|
148
|
+
run_id: z.string().min(1),
|
|
149
|
+
experiment_id: z.string().min(1),
|
|
150
|
+
status: z.string().min(1),
|
|
151
|
+
start_time: z.number().optional(),
|
|
152
|
+
end_time: z.number().optional(),
|
|
153
|
+
artifact_uri: z.string().optional(),
|
|
154
|
+
run_name: z.string().optional()
|
|
155
|
+
}),
|
|
156
|
+
data: z.object({
|
|
157
|
+
metrics: z.array(MlflowMetric).default([]),
|
|
158
|
+
params: z.array(z.object({ key: z.string(), value: z.string() })).default([]),
|
|
159
|
+
tags: z.array(z.object({ key: z.string(), value: z.string() })).default([])
|
|
160
|
+
}).default({ metrics: [], params: [], tags: [] })
|
|
161
|
+
})
|
|
162
|
+
});
|
|
163
|
+
var DatabricksMlflowClient = class {
|
|
164
|
+
host;
|
|
165
|
+
authMode;
|
|
166
|
+
fetchImpl;
|
|
167
|
+
timeoutMs;
|
|
168
|
+
bearer;
|
|
169
|
+
constructor(config) {
|
|
170
|
+
this.host = normalizeDatabricksHost(config.host);
|
|
171
|
+
this.authMode = config.auth.kind;
|
|
172
|
+
this.fetchImpl = config.fetchImpl ?? fetch;
|
|
173
|
+
this.timeoutMs = config.timeoutMs ?? 6e4;
|
|
174
|
+
if (!Number.isFinite(this.timeoutMs) || this.timeoutMs <= 0) {
|
|
175
|
+
throw new Error("Databricks MLflow timeoutMs must be positive");
|
|
176
|
+
}
|
|
177
|
+
this.bearer = new DatabricksBearerProvider({
|
|
178
|
+
host: this.host,
|
|
179
|
+
auth: config.auth,
|
|
180
|
+
fetchImpl: this.fetchImpl,
|
|
181
|
+
timeoutMs: this.timeoutMs
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
async getRun(runId) {
|
|
185
|
+
if (!runId.trim()) throw new Error("MLflow runId is required");
|
|
186
|
+
const url = new URL(`${this.host}/api/2.0/mlflow/runs/get`);
|
|
187
|
+
url.searchParams.set("run_id", runId);
|
|
188
|
+
const response = await this.fetchImpl(url, {
|
|
189
|
+
headers: { authorization: `Bearer ${await this.bearer.token()}` },
|
|
190
|
+
signal: AbortSignal.timeout(this.timeoutMs)
|
|
191
|
+
});
|
|
192
|
+
if (!response.ok) {
|
|
193
|
+
const body = await response.text();
|
|
194
|
+
throw new Error(
|
|
195
|
+
`Databricks MLflow run lookup failed (${response.status}): ${body.slice(0, 500)}`
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
const parsed = MlflowRunResponse.safeParse(await response.json());
|
|
199
|
+
if (!parsed.success) {
|
|
200
|
+
throw new Error(
|
|
201
|
+
`Databricks MLflow returned an invalid run: ${parsed.error.issues[0]?.message ?? "schema mismatch"}`
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
return parsed.data.run;
|
|
205
|
+
}
|
|
206
|
+
runUrl(run) {
|
|
207
|
+
return `${this.host}/ml/experiments/${encodeURIComponent(run.info.experiment_id)}/runs/${encodeURIComponent(run.info.run_id)}`;
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
function parseMlflowMetricGates(value) {
|
|
211
|
+
const entries = value.split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
212
|
+
if (entries.length === 0) throw new Error("At least one MLflow metric gate is required");
|
|
213
|
+
return entries.map((entry) => {
|
|
214
|
+
const match = /^(.+?)(>=|<=|==|!=|>|<)(-?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?)$/i.exec(entry);
|
|
215
|
+
if (!match) throw new Error(`Invalid MLflow metric gate '${entry}'`);
|
|
216
|
+
const metric = match[1].trim();
|
|
217
|
+
if (!metric) throw new Error(`Invalid MLflow metric gate '${entry}'`);
|
|
218
|
+
return {
|
|
219
|
+
metric,
|
|
220
|
+
operator: match[2],
|
|
221
|
+
threshold: Number(match[3])
|
|
222
|
+
};
|
|
223
|
+
});
|
|
110
224
|
}
|
|
111
|
-
function
|
|
112
|
-
|
|
113
|
-
return
|
|
225
|
+
function evaluateMlflowMetricGates(run, gates) {
|
|
226
|
+
const metrics = new Map(run.data.metrics.map((metric) => [metric.key, metric.value]));
|
|
227
|
+
return gates.map((gate) => {
|
|
228
|
+
const actual = metrics.get(gate.metric) ?? null;
|
|
229
|
+
return {
|
|
230
|
+
...gate,
|
|
231
|
+
actual,
|
|
232
|
+
passed: actual === null ? false : compare(actual, gate.operator, gate.threshold),
|
|
233
|
+
...actual === null ? { reason: "missing-metric" } : {}
|
|
234
|
+
};
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
function compare(actual, operator, threshold) {
|
|
238
|
+
switch (operator) {
|
|
239
|
+
case ">=":
|
|
240
|
+
return actual >= threshold;
|
|
241
|
+
case ">":
|
|
242
|
+
return actual > threshold;
|
|
243
|
+
case "<=":
|
|
244
|
+
return actual <= threshold;
|
|
245
|
+
case "<":
|
|
246
|
+
return actual < threshold;
|
|
247
|
+
case "==":
|
|
248
|
+
return actual === threshold;
|
|
249
|
+
case "!=":
|
|
250
|
+
return actual !== threshold;
|
|
251
|
+
}
|
|
114
252
|
}
|
|
115
253
|
|
|
116
|
-
export { DatabricksModelClient };
|
|
254
|
+
export { DatabricksBearerProvider, DatabricksMlflowClient, DatabricksModelClient, evaluateMlflowMetricGates, normalizeDatabricksHost, parseMlflowMetricGates };
|
|
117
255
|
//# sourceMappingURL=databricks.js.map
|
|
118
256
|
//# sourceMappingURL=databricks.js.map
|
package/dist/databricks.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/databricks-model-client.ts"],"names":[],"mappings":"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","file":"databricks.js","sourcesContent":["import type {\n ChatMessage,\n ModelClient,\n ModelCompletionOptions,\n ModelResponse,\n} from './model-client.js';\n\n/**\n * Transport-agnostic Databricks Model Serving chat client\n * (`/serving-endpoints/{name}/invocations`, OpenAI-compatible chat payload).\n *\n * Auth mirrors the warehouse wiring: a PAT (`token`) wins when provided,\n * otherwise M2M OAuth client credentials are exchanged at\n * `/oidc/v1/token` and cached per instance until shortly before expiry.\n *\n * The `fetchImpl` injection keeps this package free of HTTP-client deps and\n * lets tests assert the exact request shape. Both the control plane\n * (`apps/api/src/server/model-client.ts`) and the Temporal worker\n * (`agents/harness-deploy`) wrap this class with their own env resolution.\n */\n\nexport type DatabricksAuth =\n | { kind: 'pat'; token: string }\n | { kind: 'oauth-m2m'; clientId: string; clientSecret: string };\n\nexport interface DatabricksModelClientConfig {\n /** Workspace host — bare (`dbc-x.cloud.databricks.com`) or with scheme. */\n host: string;\n /** Serving endpoint name; recorded in EvalScore.modelEndpoint. */\n endpoint: string;\n auth: DatabricksAuth;\n /** Injected for tests; defaults to global fetch. */\n fetchImpl?: typeof fetch;\n /** Abort Model Serving and OAuth calls after this duration. Default: 60s. */\n timeoutMs?: number;\n}\n\nexport class DatabricksModelClient implements ModelClient {\n readonly endpoint: string;\n /** Exposed for wiring assertions (deps-construction tests). */\n readonly authMode: DatabricksAuth['kind'];\n private readonly host: string;\n private readonly auth: DatabricksAuth;\n private readonly fetchImpl: typeof fetch;\n private readonly timeoutMs: number;\n private oauthToken: { value: string; expiresAt: number } | null = null;\n\n constructor(config: DatabricksModelClientConfig) {\n this.host = normalizeHost(config.host);\n this.endpoint = config.endpoint;\n this.auth = config.auth;\n this.authMode = config.auth.kind;\n this.fetchImpl = config.fetchImpl ?? fetch;\n this.timeoutMs = config.timeoutMs ?? 60_000;\n if (!Number.isFinite(this.timeoutMs) || this.timeoutMs <= 0) {\n throw new Error('Databricks Model Serving timeoutMs must be positive');\n }\n }\n\n async complete(\n messages: readonly ChatMessage[],\n options: ModelCompletionOptions,\n ): Promise<ModelResponse> {\n if (!Number.isInteger(options.maxOutputTokens) || options.maxOutputTokens <= 0) {\n throw new Error('maxOutputTokens must be a positive integer');\n }\n if (!Number.isFinite(options.temperature) || options.temperature < 0) {\n throw new Error('temperature must be a non-negative finite number');\n }\n const response = await this.fetchImpl(\n `${this.host}/serving-endpoints/${encodeURIComponent(this.endpoint)}/invocations`,\n {\n method: 'POST',\n headers: {\n authorization: `Bearer ${await this.resolveToken()}`,\n 'content-type': 'application/json',\n },\n body: JSON.stringify({\n messages: messages.map((m) => ({ role: m.role, content: m.content })),\n temperature: options.temperature,\n max_tokens: options.maxOutputTokens,\n }),\n signal: AbortSignal.timeout(this.timeoutMs),\n },\n );\n if (!response.ok) {\n const body = await response.text();\n throw new Error(\n `Model Serving invocation failed (${response.status}): ${body.slice(0, 500)}`,\n );\n }\n const payload = (await response.json()) as unknown;\n const parsed = parseServingResponse(payload);\n return {\n text: parsed.text,\n tokens: {\n inputTokens: parsed.inputTokens,\n outputTokens: parsed.outputTokens,\n },\n };\n }\n\n private async resolveToken(): Promise<string> {\n if (this.auth.kind === 'pat') return this.auth.token;\n if (this.oauthToken && this.oauthToken.expiresAt > Date.now() + 60_000) {\n return this.oauthToken.value;\n }\n const response = await this.fetchImpl(`${this.host}/oidc/v1/token`, {\n method: 'POST',\n headers: {\n authorization: `Basic ${toBase64(`${this.auth.clientId}:${this.auth.clientSecret}`)}`,\n 'content-type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({ grant_type: 'client_credentials', scope: 'all-apis' }),\n signal: AbortSignal.timeout(this.timeoutMs),\n });\n if (!response.ok) throw new Error(`Databricks OAuth exchange failed (${response.status})`);\n const body = (await response.json()) as { access_token?: string; expires_in?: number };\n if (!body.access_token) throw new Error('Databricks OAuth exchange returned no access token');\n this.oauthToken = {\n value: body.access_token,\n expiresAt: Date.now() + (body.expires_in ?? 3600) * 1000,\n };\n return this.oauthToken.value;\n }\n}\n\nfunction parseServingResponse(payload: unknown): {\n text: string;\n inputTokens: number;\n outputTokens: number;\n} {\n if (!isRecord(payload)) throw new Error('Model Serving returned an invalid response object');\n const choices = payload.choices;\n const first = Array.isArray(choices) ? choices[0] : undefined;\n const message = isRecord(first) && isRecord(first.message) ? first.message : undefined;\n const text = message?.content;\n if (typeof text !== 'string' || text.trim() === '') {\n throw new Error('Model Serving returned no completion text');\n }\n const usage = isRecord(payload.usage) ? payload.usage : undefined;\n const inputTokens = usage?.prompt_tokens;\n const outputTokens = usage?.completion_tokens;\n if (!isTokenCount(inputTokens) || !isTokenCount(outputTokens)) {\n throw new Error('Model Serving returned invalid token usage');\n }\n return { text, inputTokens, outputTokens };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction isTokenCount(value: unknown): value is number {\n return typeof value === 'number' && Number.isInteger(value) && value >= 0;\n}\n\nfunction normalizeHost(host: string): string {\n return host.startsWith('http://') || host.startsWith('https://')\n ? host.replace(/\\/$/, '')\n : `https://${host.replace(/\\/$/, '')}`;\n}\n\n/** Node has Buffer; edge/browser runtimes have btoa. Credentials are ASCII. */\nfunction toBase64(value: string): string {\n if (typeof Buffer !== 'undefined') return Buffer.from(value).toString('base64');\n return btoa(value);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/databricks-auth.ts","../src/databricks-model-client.ts","../src/databricks-mlflow-client.ts"],"names":[],"mappings":"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","file":"databricks.js","sourcesContent":["/** Authentication accepted by Databricks workspace APIs. */\nexport type DatabricksAuth =\n | { kind: 'pat'; token: string }\n | { kind: 'oauth-m2m'; clientId: string; clientSecret: string };\n\n/**\n * Small, dependency-free bearer provider shared by the Model Serving and\n * MLflow adapters. OAuth tokens are cached until one minute before expiry.\n */\nexport class DatabricksBearerProvider {\n private readonly host: string;\n private readonly auth: DatabricksAuth;\n private readonly fetchImpl: typeof fetch;\n private readonly timeoutMs: number;\n private oauthToken: { value: string; expiresAt: number } | null = null;\n\n constructor(config: {\n host: string;\n auth: DatabricksAuth;\n fetchImpl?: typeof fetch;\n timeoutMs?: number;\n }) {\n this.host = normalizeDatabricksHost(config.host);\n this.auth = config.auth;\n this.fetchImpl = config.fetchImpl ?? fetch;\n this.timeoutMs = config.timeoutMs ?? 60_000;\n if (!Number.isFinite(this.timeoutMs) || this.timeoutMs <= 0) {\n throw new Error('Databricks timeoutMs must be positive');\n }\n }\n\n async token(): Promise<string> {\n if (this.auth.kind === 'pat') return this.auth.token;\n if (this.oauthToken && this.oauthToken.expiresAt > Date.now() + 60_000) {\n return this.oauthToken.value;\n }\n const response = await this.fetchImpl(`${this.host}/oidc/v1/token`, {\n method: 'POST',\n headers: {\n authorization: `Basic ${toBase64(`${this.auth.clientId}:${this.auth.clientSecret}`)}`,\n 'content-type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({ grant_type: 'client_credentials', scope: 'all-apis' }),\n signal: AbortSignal.timeout(this.timeoutMs),\n });\n if (!response.ok) throw new Error(`Databricks OAuth exchange failed (${response.status})`);\n const body = (await response.json()) as { access_token?: string; expires_in?: number };\n if (!body.access_token) throw new Error('Databricks OAuth exchange returned no access token');\n this.oauthToken = {\n value: body.access_token,\n expiresAt: Date.now() + (body.expires_in ?? 3600) * 1000,\n };\n return this.oauthToken.value;\n }\n}\n\nexport function normalizeDatabricksHost(host: string): string {\n return host.startsWith('http://') || host.startsWith('https://')\n ? host.replace(/\\/$/, '')\n : `https://${host.replace(/\\/$/, '')}`;\n}\n\n/** Node has Buffer; edge/browser runtimes have btoa. Credentials are ASCII. */\nfunction toBase64(value: string): string {\n if (typeof Buffer !== 'undefined') return Buffer.from(value).toString('base64');\n return btoa(value);\n}\n","import {\n type DatabricksAuth,\n DatabricksBearerProvider,\n normalizeDatabricksHost,\n} from './databricks-auth.js';\nimport type {\n ChatMessage,\n ModelClient,\n ModelCompletionOptions,\n ModelResponse,\n} from './model-client.js';\n\nexport type { DatabricksAuth } from './databricks-auth.js';\n\n/**\n * Transport-agnostic Databricks Model Serving chat client\n * (`/serving-endpoints/{name}/invocations`, OpenAI-compatible chat payload).\n *\n * Auth mirrors the warehouse wiring: a PAT (`token`) wins when provided,\n * otherwise M2M OAuth client credentials are exchanged at\n * `/oidc/v1/token` and cached per instance until shortly before expiry.\n *\n * The `fetchImpl` injection keeps this package free of HTTP-client deps and\n * lets tests assert the exact request shape. Both the control plane\n * (`apps/api/src/server/model-client.ts`) and the Temporal worker\n * (`agents/harness-deploy`) wrap this class with their own env resolution.\n */\n\nexport interface DatabricksModelClientConfig {\n /** Workspace host — bare (`dbc-x.cloud.databricks.com`) or with scheme. */\n host: string;\n /** Serving endpoint name; recorded in EvalScore.modelEndpoint. */\n endpoint: string;\n auth: DatabricksAuth;\n /** Injected for tests; defaults to global fetch. */\n fetchImpl?: typeof fetch;\n /** Abort Model Serving and OAuth calls after this duration. Default: 60s. */\n timeoutMs?: number;\n}\n\nexport class DatabricksModelClient implements ModelClient {\n readonly endpoint: string;\n /** Exposed for wiring assertions (deps-construction tests). */\n readonly authMode: DatabricksAuth['kind'];\n private readonly host: string;\n private readonly fetchImpl: typeof fetch;\n private readonly timeoutMs: number;\n private readonly bearer: DatabricksBearerProvider;\n\n constructor(config: DatabricksModelClientConfig) {\n this.host = normalizeDatabricksHost(config.host);\n this.endpoint = config.endpoint;\n this.authMode = config.auth.kind;\n this.fetchImpl = config.fetchImpl ?? fetch;\n this.timeoutMs = config.timeoutMs ?? 60_000;\n if (!Number.isFinite(this.timeoutMs) || this.timeoutMs <= 0) {\n throw new Error('Databricks Model Serving timeoutMs must be positive');\n }\n this.bearer = new DatabricksBearerProvider({\n host: this.host,\n auth: config.auth,\n fetchImpl: this.fetchImpl,\n timeoutMs: this.timeoutMs,\n });\n }\n\n async complete(\n messages: readonly ChatMessage[],\n options: ModelCompletionOptions,\n ): Promise<ModelResponse> {\n if (!Number.isInteger(options.maxOutputTokens) || options.maxOutputTokens <= 0) {\n throw new Error('maxOutputTokens must be a positive integer');\n }\n if (!Number.isFinite(options.temperature) || options.temperature < 0) {\n throw new Error('temperature must be a non-negative finite number');\n }\n const response = await this.fetchImpl(\n `${this.host}/serving-endpoints/${encodeURIComponent(this.endpoint)}/invocations`,\n {\n method: 'POST',\n headers: {\n authorization: `Bearer ${await this.bearer.token()}`,\n 'content-type': 'application/json',\n },\n body: JSON.stringify({\n messages: messages.map((m) => ({ role: m.role, content: m.content })),\n temperature: options.temperature,\n max_tokens: options.maxOutputTokens,\n }),\n signal: AbortSignal.timeout(this.timeoutMs),\n },\n );\n if (!response.ok) {\n const body = await response.text();\n throw new Error(\n `Model Serving invocation failed (${response.status}): ${body.slice(0, 500)}`,\n );\n }\n const payload = (await response.json()) as unknown;\n const parsed = parseServingResponse(payload);\n return {\n text: parsed.text,\n tokens: {\n inputTokens: parsed.inputTokens,\n outputTokens: parsed.outputTokens,\n },\n };\n }\n}\n\nfunction parseServingResponse(payload: unknown): {\n text: string;\n inputTokens: number;\n outputTokens: number;\n} {\n if (!isRecord(payload)) throw new Error('Model Serving returned an invalid response object');\n const choices = payload.choices;\n const first = Array.isArray(choices) ? choices[0] : undefined;\n const message = isRecord(first) && isRecord(first.message) ? first.message : undefined;\n const text = message?.content;\n if (typeof text !== 'string' || text.trim() === '') {\n throw new Error('Model Serving returned no completion text');\n }\n const usage = isRecord(payload.usage) ? payload.usage : undefined;\n const inputTokens = usage?.prompt_tokens;\n const outputTokens = usage?.completion_tokens;\n if (!isTokenCount(inputTokens) || !isTokenCount(outputTokens)) {\n throw new Error('Model Serving returned invalid token usage');\n }\n return { text, inputTokens, outputTokens };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction isTokenCount(value: unknown): value is number {\n return typeof value === 'number' && Number.isInteger(value) && value >= 0;\n}\n","import { z } from 'zod';\nimport {\n type DatabricksAuth,\n DatabricksBearerProvider,\n normalizeDatabricksHost,\n} from './databricks-auth.js';\n\nconst MlflowMetric = z.object({\n key: z.string().min(1),\n value: z.number(),\n timestamp: z.number().optional(),\n step: z.number().optional(),\n});\n\nconst MlflowRunResponse = z.object({\n run: z.object({\n info: z.object({\n run_id: z.string().min(1),\n experiment_id: z.string().min(1),\n status: z.string().min(1),\n start_time: z.number().optional(),\n end_time: z.number().optional(),\n artifact_uri: z.string().optional(),\n run_name: z.string().optional(),\n }),\n data: z\n .object({\n metrics: z.array(MlflowMetric).default([]),\n params: z.array(z.object({ key: z.string(), value: z.string() })).default([]),\n tags: z.array(z.object({ key: z.string(), value: z.string() })).default([]),\n })\n .default({ metrics: [], params: [], tags: [] }),\n }),\n});\n\nexport type DatabricksMlflowRun = z.infer<typeof MlflowRunResponse>['run'];\n\nexport interface DatabricksMlflowClientConfig {\n host: string;\n auth: DatabricksAuth;\n fetchImpl?: typeof fetch;\n timeoutMs?: number;\n}\n\n/** Read-only adapter for native managed-MLflow run evidence. */\nexport class DatabricksMlflowClient {\n readonly host: string;\n readonly authMode: DatabricksAuth['kind'];\n private readonly fetchImpl: typeof fetch;\n private readonly timeoutMs: number;\n private readonly bearer: DatabricksBearerProvider;\n\n constructor(config: DatabricksMlflowClientConfig) {\n this.host = normalizeDatabricksHost(config.host);\n this.authMode = config.auth.kind;\n this.fetchImpl = config.fetchImpl ?? fetch;\n this.timeoutMs = config.timeoutMs ?? 60_000;\n if (!Number.isFinite(this.timeoutMs) || this.timeoutMs <= 0) {\n throw new Error('Databricks MLflow timeoutMs must be positive');\n }\n this.bearer = new DatabricksBearerProvider({\n host: this.host,\n auth: config.auth,\n fetchImpl: this.fetchImpl,\n timeoutMs: this.timeoutMs,\n });\n }\n\n async getRun(runId: string): Promise<DatabricksMlflowRun> {\n if (!runId.trim()) throw new Error('MLflow runId is required');\n const url = new URL(`${this.host}/api/2.0/mlflow/runs/get`);\n url.searchParams.set('run_id', runId);\n const response = await this.fetchImpl(url, {\n headers: { authorization: `Bearer ${await this.bearer.token()}` },\n signal: AbortSignal.timeout(this.timeoutMs),\n });\n if (!response.ok) {\n const body = await response.text();\n throw new Error(\n `Databricks MLflow run lookup failed (${response.status}): ${body.slice(0, 500)}`,\n );\n }\n const parsed = MlflowRunResponse.safeParse(await response.json());\n if (!parsed.success) {\n throw new Error(\n `Databricks MLflow returned an invalid run: ${parsed.error.issues[0]?.message ?? 'schema mismatch'}`,\n );\n }\n return parsed.data.run;\n }\n\n runUrl(run: DatabricksMlflowRun): string {\n return `${this.host}/ml/experiments/${encodeURIComponent(run.info.experiment_id)}/runs/${encodeURIComponent(run.info.run_id)}`;\n }\n}\n\nexport type MlflowGateOperator = '>=' | '>' | '<=' | '<' | '==' | '!=';\n\nexport interface MlflowMetricGate {\n metric: string;\n operator: MlflowGateOperator;\n threshold: number;\n}\n\nexport interface MlflowGateResult extends MlflowMetricGate {\n actual: number | null;\n passed: boolean;\n reason?: 'missing-metric';\n}\n\n/** Parse `accuracy>=0.9,toxicity<=0.05` without owning MLflow scoring itself. */\nexport function parseMlflowMetricGates(value: string): MlflowMetricGate[] {\n const entries = value\n .split(',')\n .map((entry) => entry.trim())\n .filter(Boolean);\n if (entries.length === 0) throw new Error('At least one MLflow metric gate is required');\n return entries.map((entry) => {\n const match = /^(.+?)(>=|<=|==|!=|>|<)(-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:e[+-]?\\d+)?)$/i.exec(entry);\n if (!match) throw new Error(`Invalid MLflow metric gate '${entry}'`);\n const metric = match[1]!.trim();\n if (!metric) throw new Error(`Invalid MLflow metric gate '${entry}'`);\n return {\n metric,\n operator: match[2] as MlflowGateOperator,\n threshold: Number(match[3]),\n };\n });\n}\n\n/** Evaluate release policy over metrics already computed and retained by MLflow. */\nexport function evaluateMlflowMetricGates(\n run: DatabricksMlflowRun,\n gates: readonly MlflowMetricGate[],\n): MlflowGateResult[] {\n const metrics = new Map(run.data.metrics.map((metric) => [metric.key, metric.value]));\n return gates.map((gate) => {\n const actual = metrics.get(gate.metric) ?? null;\n return {\n ...gate,\n actual,\n passed: actual === null ? false : compare(actual, gate.operator, gate.threshold),\n ...(actual === null ? { reason: 'missing-metric' as const } : {}),\n };\n });\n}\n\nfunction compare(actual: number, operator: MlflowGateOperator, threshold: number): boolean {\n switch (operator) {\n case '>=':\n return actual >= threshold;\n case '>':\n return actual > threshold;\n case '<=':\n return actual <= threshold;\n case '<':\n return actual < threshold;\n case '==':\n return actual === threshold;\n case '!=':\n return actual !== threshold;\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fabricorg/experiments-evals",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "LLM and code evaluators for Fabric Experiments: judge library, ModelClient contract, run aggregation.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -10,12 +10,24 @@
|
|
|
10
10
|
"module": "./dist/index.js",
|
|
11
11
|
"exports": {
|
|
12
12
|
".": {
|
|
13
|
-
"import": {
|
|
14
|
-
|
|
13
|
+
"import": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"default": "./dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"require": {
|
|
18
|
+
"types": "./dist/index.d.cts",
|
|
19
|
+
"default": "./dist/index.cjs"
|
|
20
|
+
}
|
|
15
21
|
},
|
|
16
22
|
"./databricks": {
|
|
17
|
-
"import": {
|
|
18
|
-
|
|
23
|
+
"import": {
|
|
24
|
+
"types": "./dist/databricks.d.ts",
|
|
25
|
+
"default": "./dist/databricks.js"
|
|
26
|
+
},
|
|
27
|
+
"require": {
|
|
28
|
+
"types": "./dist/databricks.d.cts",
|
|
29
|
+
"default": "./dist/databricks.cjs"
|
|
30
|
+
}
|
|
19
31
|
}
|
|
20
32
|
},
|
|
21
33
|
"files": ["dist"],
|