@fabricorg/databricks-testkit 0.7.1 → 0.8.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 +2 -2
- package/dist/{chunk-SCVVMUAF.js → chunk-MV752EAZ.js} +144 -10
- package/dist/chunk-MV752EAZ.js.map +1 -0
- package/dist/index.cjs +646 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +54 -7
- package/dist/index.d.ts +54 -7
- package/dist/index.js +473 -15
- package/dist/index.js.map +1 -1
- package/dist/parity.cjs +124 -7
- package/dist/parity.cjs.map +1 -1
- package/dist/parity.js +1 -1
- package/dist/{workspace-context-VLBKMX3V.js → workspace-context-PCQAQSCO.js} +3 -3
- package/dist/{workspace-context-VLBKMX3V.js.map → workspace-context-PCQAQSCO.js.map} +1 -1
- package/package.json +1 -2
- package/dist/chunk-SCVVMUAF.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var promises = require('fs/promises');
|
|
4
|
-
var databricks = require('@fabric-harness/databricks');
|
|
5
4
|
var path = require('path');
|
|
6
5
|
var experimentsDbPool = require('@fabricorg/experiments-db-pool');
|
|
7
6
|
var crypto = require('crypto');
|
|
@@ -177,6 +176,146 @@ var init_duckdb_context = __esm({
|
|
|
177
176
|
}
|
|
178
177
|
});
|
|
179
178
|
|
|
179
|
+
// src/workspace-client.ts
|
|
180
|
+
async function waitForDatabricksRun(client, runId, options = {}) {
|
|
181
|
+
const deadline = Date.now() + (options.timeoutMs ?? 6e5);
|
|
182
|
+
const interval = options.pollIntervalMs ?? 5e3;
|
|
183
|
+
let current = await client.get("/api/2.2/jobs/runs/get", {
|
|
184
|
+
run_id: runId
|
|
185
|
+
});
|
|
186
|
+
while (!isTerminalRun(current) && Date.now() < deadline) {
|
|
187
|
+
await delay(interval, options.signal);
|
|
188
|
+
current = await client.get("/api/2.2/jobs/runs/get", {
|
|
189
|
+
run_id: runId
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
return current;
|
|
193
|
+
}
|
|
194
|
+
async function waitForDatabricksStatement(client, statementId, options = {}) {
|
|
195
|
+
const deadline = Date.now() + (options.timeoutMs ?? 3e5);
|
|
196
|
+
const interval = options.pollIntervalMs ?? 2e3;
|
|
197
|
+
const path = `/api/2.0/sql/statements/${encodeURIComponent(statementId)}`;
|
|
198
|
+
let current = await client.get(path);
|
|
199
|
+
while (!isTerminalStatement(current) && Date.now() < deadline) {
|
|
200
|
+
await delay(interval, options.signal);
|
|
201
|
+
current = await client.get(path);
|
|
202
|
+
}
|
|
203
|
+
return current;
|
|
204
|
+
}
|
|
205
|
+
function workspaceOrigin(host) {
|
|
206
|
+
const value = host.trim();
|
|
207
|
+
if (!value) throw new Error("databricks: workspace host cannot be empty");
|
|
208
|
+
const url = new URL(value.includes("://") ? value : `https://${value}`);
|
|
209
|
+
if (url.username || url.password) {
|
|
210
|
+
throw new Error("databricks: workspace host must not contain credentials");
|
|
211
|
+
}
|
|
212
|
+
return url.origin;
|
|
213
|
+
}
|
|
214
|
+
function isRetryableStatus(status) {
|
|
215
|
+
return status === 429 || status === 499 || status >= 500 && status <= 599;
|
|
216
|
+
}
|
|
217
|
+
function retryDelayMs(response, attempt, base) {
|
|
218
|
+
const retryAfterHeader = response.headers.get("retry-after");
|
|
219
|
+
const retryAfter = retryAfterHeader === null ? Number.NaN : Number(retryAfterHeader);
|
|
220
|
+
return Number.isFinite(retryAfter) && retryAfter >= 0 ? retryAfter * 1e3 : base * 2 ** attempt;
|
|
221
|
+
}
|
|
222
|
+
function isTerminalRun(run) {
|
|
223
|
+
const state = run.state;
|
|
224
|
+
return ["TERMINATED", "SKIPPED", "INTERNAL_ERROR"].includes(state?.life_cycle_state ?? "");
|
|
225
|
+
}
|
|
226
|
+
function isTerminalStatement(statement) {
|
|
227
|
+
const status = statement.status;
|
|
228
|
+
return ["SUCCEEDED", "FAILED", "CANCELED", "CLOSED"].includes(status?.state ?? "");
|
|
229
|
+
}
|
|
230
|
+
function delay(ms, signal) {
|
|
231
|
+
if (!signal) return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
232
|
+
if (signal.aborted) {
|
|
233
|
+
return Promise.reject(
|
|
234
|
+
signal.reason ?? new DOMException("The operation was aborted", "AbortError")
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
return new Promise((resolve2, reject) => {
|
|
238
|
+
const timer = setTimeout(done, ms);
|
|
239
|
+
signal.addEventListener("abort", aborted, { once: true });
|
|
240
|
+
function done() {
|
|
241
|
+
signal?.removeEventListener("abort", aborted);
|
|
242
|
+
resolve2();
|
|
243
|
+
}
|
|
244
|
+
function aborted() {
|
|
245
|
+
clearTimeout(timer);
|
|
246
|
+
reject(signal?.reason ?? new DOMException("The operation was aborted", "AbortError"));
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
var WorkspaceRestClient;
|
|
251
|
+
var init_workspace_client = __esm({
|
|
252
|
+
"src/workspace-client.ts"() {
|
|
253
|
+
WorkspaceRestClient = class {
|
|
254
|
+
host;
|
|
255
|
+
token;
|
|
256
|
+
fetchImpl;
|
|
257
|
+
maxRetries;
|
|
258
|
+
retryBaseDelayMs;
|
|
259
|
+
signal;
|
|
260
|
+
constructor(options) {
|
|
261
|
+
this.host = workspaceOrigin(options.host);
|
|
262
|
+
this.token = options.token;
|
|
263
|
+
this.fetchImpl = options.fetchImpl ?? fetch;
|
|
264
|
+
this.maxRetries = options.maxRetries ?? 3;
|
|
265
|
+
this.retryBaseDelayMs = options.retryBaseDelayMs ?? 500;
|
|
266
|
+
this.signal = options.signal;
|
|
267
|
+
}
|
|
268
|
+
get(path, query) {
|
|
269
|
+
return this.request("GET", path, void 0, query);
|
|
270
|
+
}
|
|
271
|
+
post(path, body, query, options) {
|
|
272
|
+
return this.request("POST", path, body, query, options);
|
|
273
|
+
}
|
|
274
|
+
async request(method, path, body, query = {}, options = {}) {
|
|
275
|
+
const url = new URL(`${this.host}${path.startsWith("/") ? path : `/${path}`}`);
|
|
276
|
+
for (const [key, value] of Object.entries(query)) {
|
|
277
|
+
if (value !== void 0) url.searchParams.set(key, String(value));
|
|
278
|
+
}
|
|
279
|
+
const bodyHasIdempotencyToken = typeof body === "object" && body !== null && typeof body.idempotency_token === "string";
|
|
280
|
+
const canRetry = options.retry === "always" || options.retry !== "never" && (options.retry === "safe" || method === "GET" || Boolean(options.idempotencyKey) || bodyHasIdempotencyToken);
|
|
281
|
+
const maxRetries = canRetry ? options.maxRetries ?? this.maxRetries : 0;
|
|
282
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
283
|
+
const token = typeof this.token === "function" ? await this.token() : this.token;
|
|
284
|
+
let response;
|
|
285
|
+
try {
|
|
286
|
+
response = await this.fetchImpl(url, {
|
|
287
|
+
method,
|
|
288
|
+
headers: {
|
|
289
|
+
authorization: `Bearer ${token}`,
|
|
290
|
+
...body !== void 0 ? { "content-type": "application/json" } : {},
|
|
291
|
+
...options.idempotencyKey ? { "idempotency-key": options.idempotencyKey } : {}
|
|
292
|
+
},
|
|
293
|
+
...body !== void 0 ? { body: JSON.stringify(body) } : {},
|
|
294
|
+
...this.signal ? { signal: this.signal } : {}
|
|
295
|
+
});
|
|
296
|
+
} catch (error) {
|
|
297
|
+
if (this.signal?.aborted || attempt >= maxRetries) throw error;
|
|
298
|
+
await delay(this.retryBaseDelayMs * 2 ** attempt, this.signal);
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
if (response.ok) {
|
|
302
|
+
if (response.status === 204) return void 0;
|
|
303
|
+
return await response.json();
|
|
304
|
+
}
|
|
305
|
+
if (isRetryableStatus(response.status) && attempt < maxRetries) {
|
|
306
|
+
await delay(retryDelayMs(response, attempt, this.retryBaseDelayMs), this.signal);
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
const message = (await response.text()).split(token).join("[REDACTED]");
|
|
310
|
+
throw new Error(
|
|
311
|
+
`databricks: ${response.status} ${response.statusText}: ${message.slice(0, 2e3)}`
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
|
|
180
319
|
// src/workspace-context.ts
|
|
181
320
|
var workspace_context_exports = {};
|
|
182
321
|
__export(workspace_context_exports, {
|
|
@@ -212,11 +351,10 @@ async function createWorkspaceContext(options = {}) {
|
|
|
212
351
|
const state = submitted.status?.state;
|
|
213
352
|
let terminal = submitted;
|
|
214
353
|
if (state !== "SUCCEEDED" && state !== "FAILED" && state !== "CANCELED" && state !== "CLOSED") {
|
|
215
|
-
terminal = await
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
);
|
|
354
|
+
terminal = await waitForDatabricksStatement(client, submitted.statement_id ?? "", {
|
|
355
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
356
|
+
timeoutMs: options.timeoutMs
|
|
357
|
+
});
|
|
220
358
|
}
|
|
221
359
|
if (terminal.status?.state !== "SUCCEEDED") {
|
|
222
360
|
throw new Error(
|
|
@@ -332,7 +470,7 @@ function resolveWarehouseId(env) {
|
|
|
332
470
|
function createClientFromEnv(env, fetchImpl) {
|
|
333
471
|
const host = env.DATABRICKS_HOST;
|
|
334
472
|
if (!host) throw new Error("Live profile requires DATABRICKS_HOST");
|
|
335
|
-
return new
|
|
473
|
+
return new WorkspaceRestClient({
|
|
336
474
|
host,
|
|
337
475
|
token: resolveTokenProvider(host, env, fetchImpl),
|
|
338
476
|
...fetchImpl ? { fetchImpl } : {}
|
|
@@ -423,6 +561,7 @@ var NUMERIC_TYPES;
|
|
|
423
561
|
var init_workspace_context = __esm({
|
|
424
562
|
"src/workspace-context.ts"() {
|
|
425
563
|
init_fixtures();
|
|
564
|
+
init_workspace_client();
|
|
426
565
|
NUMERIC_TYPES = /^(INT|LONG|SHORT|BYTE|FLOAT|DOUBLE|DECIMAL)/i;
|
|
427
566
|
}
|
|
428
567
|
});
|
|
@@ -751,6 +890,7 @@ function abortableDelay(ms, signal) {
|
|
|
751
890
|
}
|
|
752
891
|
|
|
753
892
|
// src/live-checks.ts
|
|
893
|
+
init_workspace_client();
|
|
754
894
|
function sqlRoundTripCheck() {
|
|
755
895
|
return {
|
|
756
896
|
id: "sql",
|
|
@@ -839,7 +979,7 @@ function jobRunCheck() {
|
|
|
839
979
|
const submitted = await client.post("/api/2.1/jobs/run-now", {
|
|
840
980
|
job_id: jobId
|
|
841
981
|
});
|
|
842
|
-
const run = await
|
|
982
|
+
const run = await waitForDatabricksRun(client, submitted.run_id, {
|
|
843
983
|
timeoutMs: Number(env.DBX_TEST_TIMEOUT_MS ?? 9e5),
|
|
844
984
|
pollIntervalMs: Number(env.DBX_TEST_POLL_INTERVAL_MS ?? 1e4)
|
|
845
985
|
});
|
|
@@ -895,7 +1035,7 @@ function notebookSubmitCheck() {
|
|
|
895
1035
|
]
|
|
896
1036
|
});
|
|
897
1037
|
if (!submitted.run_id) throw new Error("Notebook submit returned no run_id");
|
|
898
|
-
const run = await
|
|
1038
|
+
const run = await waitForDatabricksRun(client, submitted.run_id, {
|
|
899
1039
|
timeoutMs: Number(env.DBX_TEST_TIMEOUT_MS ?? 9e5),
|
|
900
1040
|
pollIntervalMs: Number(env.DBX_TEST_POLL_INTERVAL_MS ?? 1e4)
|
|
901
1041
|
});
|
|
@@ -963,7 +1103,7 @@ function dbtBuildCheck() {
|
|
|
963
1103
|
request
|
|
964
1104
|
);
|
|
965
1105
|
if (!submitted.run_id) throw new Error("dbt submit returned no run_id");
|
|
966
|
-
const run = await
|
|
1106
|
+
const run = await waitForDatabricksRun(client, submitted.run_id, {
|
|
967
1107
|
timeoutMs: Number(env.DBX_TEST_TIMEOUT_MS ?? 9e5),
|
|
968
1108
|
pollIntervalMs: Number(env.DBX_TEST_POLL_INTERVAL_MS ?? 1e4)
|
|
969
1109
|
});
|
|
@@ -1130,7 +1270,7 @@ async function waitForOperation(client, initial, options) {
|
|
|
1130
1270
|
while (!operation.done) {
|
|
1131
1271
|
if (!operation.name) throw new Error("Lakebase operation did not return an operation name");
|
|
1132
1272
|
if (Date.now() >= deadline) throw new Error(`Lakebase operation '${operation.name}' timed out`);
|
|
1133
|
-
await
|
|
1273
|
+
await delay2(options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS);
|
|
1134
1274
|
operation = await client.get(`/api/2.0/postgres/${operation.name}`);
|
|
1135
1275
|
}
|
|
1136
1276
|
if (operation.error) {
|
|
@@ -1140,7 +1280,7 @@ async function waitForOperation(client, initial, options) {
|
|
|
1140
1280
|
}
|
|
1141
1281
|
return operation.response;
|
|
1142
1282
|
}
|
|
1143
|
-
function
|
|
1283
|
+
function delay2(ms) {
|
|
1144
1284
|
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
1145
1285
|
}
|
|
1146
1286
|
|
|
@@ -1322,6 +1462,9 @@ function autoLoaderIngestionCheck(options = {}) {
|
|
|
1322
1462
|
}
|
|
1323
1463
|
};
|
|
1324
1464
|
}
|
|
1465
|
+
|
|
1466
|
+
// src/resilience-checks.ts
|
|
1467
|
+
init_workspace_client();
|
|
1325
1468
|
function safeName(prefix) {
|
|
1326
1469
|
return `${prefix}_${crypto.randomUUID().replaceAll("-", "_")}`;
|
|
1327
1470
|
}
|
|
@@ -1516,7 +1659,7 @@ function classicComputeCheck() {
|
|
|
1516
1659
|
]
|
|
1517
1660
|
});
|
|
1518
1661
|
if (!submitted.run_id) throw new Error("Classic compute submit returned no run_id");
|
|
1519
|
-
const run = await
|
|
1662
|
+
const run = await waitForDatabricksRun(client, submitted.run_id, {
|
|
1520
1663
|
timeoutMs: Number(env.DBX_TEST_CLASSIC_TIMEOUT_MS ?? 18e5),
|
|
1521
1664
|
pollIntervalMs: Number(env.DBX_TEST_POLL_INTERVAL_MS ?? 15e3)
|
|
1522
1665
|
});
|
|
@@ -1841,6 +1984,9 @@ function createFoundationTargetPacks() {
|
|
|
1841
1984
|
];
|
|
1842
1985
|
}
|
|
1843
1986
|
var foundationTargetPacks = createFoundationTargetPacks();
|
|
1987
|
+
|
|
1988
|
+
// src/jobs-orchestration.ts
|
|
1989
|
+
init_workspace_client();
|
|
1844
1990
|
var DatabricksJobsAdapter = class {
|
|
1845
1991
|
constructor(client) {
|
|
1846
1992
|
this.client = client;
|
|
@@ -1864,7 +2010,7 @@ var DatabricksJobsAdapter = class {
|
|
|
1864
2010
|
return normalizeJobRun(runId, raw);
|
|
1865
2011
|
}
|
|
1866
2012
|
async wait(runId, options = {}) {
|
|
1867
|
-
const raw = await
|
|
2013
|
+
const raw = await waitForDatabricksRun(this.client, runId, {
|
|
1868
2014
|
timeoutMs: options.timeoutMs,
|
|
1869
2015
|
pollIntervalMs: options.pollIntervalMs
|
|
1870
2016
|
});
|
|
@@ -1885,7 +2031,7 @@ var DatabricksJobsAdapter = class {
|
|
|
1885
2031
|
`Databricks run ${runId} reached ${run.lifeCycleState}/${run.resultState} before it could be canceled`
|
|
1886
2032
|
);
|
|
1887
2033
|
}
|
|
1888
|
-
await
|
|
2034
|
+
await delay3(pollIntervalMs);
|
|
1889
2035
|
}
|
|
1890
2036
|
throw new Error(`Databricks run ${runId} did not become active within ${timeoutMs}ms`);
|
|
1891
2037
|
}
|
|
@@ -1907,7 +2053,7 @@ var DatabricksJobsAdapter = class {
|
|
|
1907
2053
|
const run = await this.get(runId);
|
|
1908
2054
|
const repair = run.repairs.find((candidate) => candidate.repairId === repairId);
|
|
1909
2055
|
if (repair && isTerminal(repair.lifeCycleState)) return run;
|
|
1910
|
-
await
|
|
2056
|
+
await delay3(pollIntervalMs);
|
|
1911
2057
|
}
|
|
1912
2058
|
throw new Error(
|
|
1913
2059
|
`Databricks repair ${repairId} for run ${runId} timed out after ${timeoutMs}ms`
|
|
@@ -2098,7 +2244,7 @@ function normalizeJobRun(fallbackRunId, raw) {
|
|
|
2098
2244
|
function isTerminal(state) {
|
|
2099
2245
|
return ["TERMINATED", "SKIPPED", "INTERNAL_ERROR", "BLOCKED"].includes(state);
|
|
2100
2246
|
}
|
|
2101
|
-
function
|
|
2247
|
+
function delay3(ms) {
|
|
2102
2248
|
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
2103
2249
|
}
|
|
2104
2250
|
function objectOf(value) {
|
|
@@ -2349,6 +2495,7 @@ function createLakeflowJobsTargetPack() {
|
|
|
2349
2495
|
}
|
|
2350
2496
|
|
|
2351
2497
|
// src/advanced-workload-checks.ts
|
|
2498
|
+
init_workspace_client();
|
|
2352
2499
|
init_workspace_context();
|
|
2353
2500
|
var DatabricksAdvancedWorkloadsAdapter = class {
|
|
2354
2501
|
constructor(client) {
|
|
@@ -2422,6 +2569,15 @@ var DatabricksAdvancedWorkloadsAdapter = class {
|
|
|
2422
2569
|
warehouse(id) {
|
|
2423
2570
|
return this.client.get(`/api/2.0/sql/warehouses/${segment(id)}`);
|
|
2424
2571
|
}
|
|
2572
|
+
app(name) {
|
|
2573
|
+
return this.client.get(`/api/2.0/apps/${segment(name)}`);
|
|
2574
|
+
}
|
|
2575
|
+
agentService(fullName) {
|
|
2576
|
+
return this.client.get(`/api/2.1/unity-catalog/agent-services/${segment(fullName)}`);
|
|
2577
|
+
}
|
|
2578
|
+
agentServicePermissions(fullName) {
|
|
2579
|
+
return this.client.get(`/api/2.1/unity-catalog/permissions/AGENT_SERVICE/${segment(fullName)}`);
|
|
2580
|
+
}
|
|
2425
2581
|
};
|
|
2426
2582
|
function segment(value) {
|
|
2427
2583
|
return encodeURIComponent(value);
|
|
@@ -2468,6 +2624,27 @@ async function booleanSql(label, sql, query) {
|
|
|
2468
2624
|
throw new Error(`${label} assertion did not return a truthy first column`);
|
|
2469
2625
|
}
|
|
2470
2626
|
}
|
|
2627
|
+
async function runCertificationJob(label, jobIdValue, env, client, options = {}) {
|
|
2628
|
+
const jobId = Number(jobIdValue);
|
|
2629
|
+
if (!Number.isSafeInteger(jobId) || jobId <= 0) {
|
|
2630
|
+
throw new Error(`${label} job id must be a positive integer`);
|
|
2631
|
+
}
|
|
2632
|
+
const submitted = await client.post("/api/2.1/jobs/run-now", {
|
|
2633
|
+
job_id: jobId
|
|
2634
|
+
});
|
|
2635
|
+
if (!Number.isSafeInteger(submitted.run_id)) {
|
|
2636
|
+
throw new Error(`${label} certification job returned no run_id`);
|
|
2637
|
+
}
|
|
2638
|
+
const run = await waitForDatabricksRun(client, submitted.run_id, {
|
|
2639
|
+
timeoutMs: options.timeoutMs ?? Number(env.DBX_TEST_TIMEOUT_MS ?? 9e5),
|
|
2640
|
+
pollIntervalMs: Number(env.DBX_TEST_POLL_INTERVAL_MS ?? 1e4)
|
|
2641
|
+
});
|
|
2642
|
+
const result = run.state?.result_state;
|
|
2643
|
+
if (result !== "SUCCESS") {
|
|
2644
|
+
throw new Error(`${label} certification job ${jobId} ended in ${result ?? "UNKNOWN"}`);
|
|
2645
|
+
}
|
|
2646
|
+
return { jobId, runId: submitted.run_id };
|
|
2647
|
+
}
|
|
2471
2648
|
function advancedStreamingCdcCheck() {
|
|
2472
2649
|
const names = [
|
|
2473
2650
|
"DBX_TEST_STREAMING_PIPELINE_ID",
|
|
@@ -2847,6 +3024,201 @@ function securityPostureCostCheck() {
|
|
|
2847
3024
|
}
|
|
2848
3025
|
};
|
|
2849
3026
|
}
|
|
3027
|
+
function mlflow3GenAiQualityCheck() {
|
|
3028
|
+
const names = [
|
|
3029
|
+
"DBX_TEST_MLFLOW3_JOB_ID",
|
|
3030
|
+
"DBX_TEST_MLFLOW3_TRACE_ASSERTION_SQL",
|
|
3031
|
+
"DBX_TEST_MLFLOW3_EVAL_ASSERTION_SQL",
|
|
3032
|
+
"DBX_TEST_MLFLOW3_SCORER_ASSERTION_SQL",
|
|
3033
|
+
"DBX_TEST_MLFLOW3_MONITOR_ASSERTION_SQL",
|
|
3034
|
+
"DBX_TEST_AI_SEARCH_EVAL_ASSERTION_SQL"
|
|
3035
|
+
];
|
|
3036
|
+
return {
|
|
3037
|
+
id: "mlflow3-genai-quality",
|
|
3038
|
+
description: "MLflow 3 tracing, offline agent evaluation and production scorer monitoring produce durable evidence",
|
|
3039
|
+
configured: (env) => required2(env, names).length === 0,
|
|
3040
|
+
async run({ env, client, warehouse: warehouse2 }) {
|
|
3041
|
+
requireEnv(env, names);
|
|
3042
|
+
const job = await runCertificationJob(
|
|
3043
|
+
"MLflow 3 GenAI",
|
|
3044
|
+
env.DBX_TEST_MLFLOW3_JOB_ID,
|
|
3045
|
+
env,
|
|
3046
|
+
client,
|
|
3047
|
+
{ timeoutMs: Number(env.DBX_TEST_MLFLOW3_TIMEOUT_MS ?? 72e5) }
|
|
3048
|
+
);
|
|
3049
|
+
await booleanSql("MLflow 3 trace", env.DBX_TEST_MLFLOW3_TRACE_ASSERTION_SQL, warehouse2.query);
|
|
3050
|
+
await booleanSql(
|
|
3051
|
+
"MLflow 3 offline evaluation",
|
|
3052
|
+
env.DBX_TEST_MLFLOW3_EVAL_ASSERTION_SQL,
|
|
3053
|
+
warehouse2.query
|
|
3054
|
+
);
|
|
3055
|
+
await booleanSql(
|
|
3056
|
+
"MLflow 3 production scorer lifecycle",
|
|
3057
|
+
env.DBX_TEST_MLFLOW3_SCORER_ASSERTION_SQL,
|
|
3058
|
+
warehouse2.query
|
|
3059
|
+
);
|
|
3060
|
+
await booleanSql(
|
|
3061
|
+
"MLflow 3 production monitoring",
|
|
3062
|
+
env.DBX_TEST_MLFLOW3_MONITOR_ASSERTION_SQL,
|
|
3063
|
+
warehouse2.query
|
|
3064
|
+
);
|
|
3065
|
+
await booleanSql(
|
|
3066
|
+
"AI Search retrieval quality",
|
|
3067
|
+
env.DBX_TEST_AI_SEARCH_EVAL_ASSERTION_SQL,
|
|
3068
|
+
warehouse2.query
|
|
3069
|
+
);
|
|
3070
|
+
return {
|
|
3071
|
+
...job,
|
|
3072
|
+
traces: true,
|
|
3073
|
+
offlineEvaluation: true,
|
|
3074
|
+
scorerLifecycle: true,
|
|
3075
|
+
monitor: true,
|
|
3076
|
+
aiSearchEvaluation: true
|
|
3077
|
+
};
|
|
3078
|
+
}
|
|
3079
|
+
};
|
|
3080
|
+
}
|
|
3081
|
+
function agentEvaluationPipelineCheck() {
|
|
3082
|
+
const names = ["DBX_TEST_AGENT_EVAL_E2E_JOB_ID", "DBX_TEST_AGENT_EVAL_ASSERTION_SQL"];
|
|
3083
|
+
return {
|
|
3084
|
+
id: "agent-evaluation-pipeline",
|
|
3085
|
+
description: "Experiments API, outbox, Temporal worker, Model Serving judge, SQL score sink and Quality completion execute end to end",
|
|
3086
|
+
configured: (env) => required2(env, names).length === 0,
|
|
3087
|
+
async run({ env, client, warehouse: warehouse2 }) {
|
|
3088
|
+
requireEnv(env, names);
|
|
3089
|
+
const job = await runCertificationJob(
|
|
3090
|
+
"Agent evaluation end-to-end",
|
|
3091
|
+
env.DBX_TEST_AGENT_EVAL_E2E_JOB_ID,
|
|
3092
|
+
env,
|
|
3093
|
+
client
|
|
3094
|
+
);
|
|
3095
|
+
await booleanSql(
|
|
3096
|
+
"Agent evaluation terminal Quality evidence",
|
|
3097
|
+
env.DBX_TEST_AGENT_EVAL_ASSERTION_SQL,
|
|
3098
|
+
warehouse2.query
|
|
3099
|
+
);
|
|
3100
|
+
return { ...job, api: true, outbox: true, temporal: true, judged: true, persisted: true };
|
|
3101
|
+
}
|
|
3102
|
+
};
|
|
3103
|
+
}
|
|
3104
|
+
function managedMcpAgentsCheck() {
|
|
3105
|
+
const names = [
|
|
3106
|
+
"DBX_TEST_MCP_JOB_ID",
|
|
3107
|
+
"DBX_TEST_MCP_MANAGED_ASSERTION_SQL",
|
|
3108
|
+
"DBX_TEST_MCP_SERVICE_ASSERTION_SQL",
|
|
3109
|
+
"DBX_TEST_MCP_DENY_ASSERTION_SQL"
|
|
3110
|
+
];
|
|
3111
|
+
return {
|
|
3112
|
+
id: "managed-mcp-agents",
|
|
3113
|
+
description: "Managed MCP and Unity Catalog MCP Services support list, call and deny behavior",
|
|
3114
|
+
configured: (env) => required2(env, names).length === 0,
|
|
3115
|
+
async run({ env, client, warehouse: warehouse2 }) {
|
|
3116
|
+
requireEnv(env, names);
|
|
3117
|
+
const job = await runCertificationJob("Databricks MCP", env.DBX_TEST_MCP_JOB_ID, env, client);
|
|
3118
|
+
await booleanSql(
|
|
3119
|
+
"Managed MCP list/call",
|
|
3120
|
+
env.DBX_TEST_MCP_MANAGED_ASSERTION_SQL,
|
|
3121
|
+
warehouse2.query
|
|
3122
|
+
);
|
|
3123
|
+
await booleanSql(
|
|
3124
|
+
"Unity Catalog MCP Service list/call",
|
|
3125
|
+
env.DBX_TEST_MCP_SERVICE_ASSERTION_SQL,
|
|
3126
|
+
warehouse2.query
|
|
3127
|
+
);
|
|
3128
|
+
await booleanSql(
|
|
3129
|
+
"MCP excluded-tool denial",
|
|
3130
|
+
env.DBX_TEST_MCP_DENY_ASSERTION_SQL,
|
|
3131
|
+
warehouse2.query
|
|
3132
|
+
);
|
|
3133
|
+
return { ...job, managedServer: true, mcpService: true, denyControl: true };
|
|
3134
|
+
}
|
|
3135
|
+
};
|
|
3136
|
+
}
|
|
3137
|
+
function dataQualityAppTelemetryCheck() {
|
|
3138
|
+
const names = [
|
|
3139
|
+
"DBX_TEST_TELEMETRY_APP_NAME",
|
|
3140
|
+
"DBX_TEST_DQM_ASSERTION_SQL",
|
|
3141
|
+
"DBX_TEST_APP_LOGS_ASSERTION_SQL",
|
|
3142
|
+
"DBX_TEST_APP_SPANS_ASSERTION_SQL",
|
|
3143
|
+
"DBX_TEST_APP_METRICS_ASSERTION_SQL"
|
|
3144
|
+
];
|
|
3145
|
+
return {
|
|
3146
|
+
id: "data-quality-app-telemetry",
|
|
3147
|
+
description: "Unity Catalog data quality monitoring and Databricks Apps logs, spans and metrics are current",
|
|
3148
|
+
configured: (env) => required2(env, names).length === 0,
|
|
3149
|
+
async run({ env, client, warehouse: warehouse2 }) {
|
|
3150
|
+
requireEnv(env, names);
|
|
3151
|
+
const appName = env.DBX_TEST_TELEMETRY_APP_NAME;
|
|
3152
|
+
const app = await adapter(client).app(appName);
|
|
3153
|
+
const destinations = Array.isArray(app.telemetry_export_destinations) ? app.telemetry_export_destinations : [];
|
|
3154
|
+
const unityCatalog = destinations.find(
|
|
3155
|
+
(destination) => isObject(destination) && isObject(destination.unity_catalog)
|
|
3156
|
+
);
|
|
3157
|
+
if (!isObject(unityCatalog) || !isObject(unityCatalog.unity_catalog)) {
|
|
3158
|
+
throw new Error(`Databricks App ${appName} has no Unity Catalog telemetry destination`);
|
|
3159
|
+
}
|
|
3160
|
+
const tables = unityCatalog.unity_catalog;
|
|
3161
|
+
for (const field of ["logs_table", "traces_table", "metrics_table"]) {
|
|
3162
|
+
if (typeof tables[field] !== "string" || !tables[field]) {
|
|
3163
|
+
throw new Error(`Databricks App ${appName} telemetry destination has no ${field}`);
|
|
3164
|
+
}
|
|
3165
|
+
}
|
|
3166
|
+
await booleanSql("Data Quality Monitoring", env.DBX_TEST_DQM_ASSERTION_SQL, warehouse2.query);
|
|
3167
|
+
await booleanSql("App logs", env.DBX_TEST_APP_LOGS_ASSERTION_SQL, warehouse2.query);
|
|
3168
|
+
await booleanSql("App spans", env.DBX_TEST_APP_SPANS_ASSERTION_SQL, warehouse2.query);
|
|
3169
|
+
await booleanSql("App metrics", env.DBX_TEST_APP_METRICS_ASSERTION_SQL, warehouse2.query);
|
|
3170
|
+
return { app: appName, unityCatalogTelemetry: true, dataQualityMonitoring: true };
|
|
3171
|
+
}
|
|
3172
|
+
};
|
|
3173
|
+
}
|
|
3174
|
+
function agentServicesEnrollmentCheck() {
|
|
3175
|
+
const names = [
|
|
3176
|
+
"DBX_TEST_AGENT_SERVICE_FULL_NAME",
|
|
3177
|
+
"DBX_TEST_AGENT_SERVICE_CONNECTION",
|
|
3178
|
+
"DBX_TEST_AGENT_SERVICE_EXECUTE_PRINCIPAL"
|
|
3179
|
+
];
|
|
3180
|
+
return {
|
|
3181
|
+
id: "agent-services-enrollment",
|
|
3182
|
+
description: "A customer-owned Harness agent is registered as an external Unity Catalog Agent Service with governed access",
|
|
3183
|
+
configured: (env) => required2(env, names).length === 0,
|
|
3184
|
+
async run({ env, client }) {
|
|
3185
|
+
requireEnv(env, names);
|
|
3186
|
+
const fullName = env.DBX_TEST_AGENT_SERVICE_FULL_NAME;
|
|
3187
|
+
const api = adapter(client);
|
|
3188
|
+
const service = await api.agentService(fullName);
|
|
3189
|
+
if (service.agent_service_type !== "AGENT_SERVICE_TYPE_EXTERNAL") {
|
|
3190
|
+
throw new Error(`${fullName} is not an external Agent Service`);
|
|
3191
|
+
}
|
|
3192
|
+
const expectedConnection = env.DBX_TEST_AGENT_SERVICE_CONNECTION;
|
|
3193
|
+
const actualConnection = service.config?.connection?.name ?? "";
|
|
3194
|
+
if (actualConnection !== expectedConnection && actualConnection.split("/").at(-1) !== expectedConnection) {
|
|
3195
|
+
throw new Error(`${fullName} uses unexpected connection ${actualConnection || "UNKNOWN"}`);
|
|
3196
|
+
}
|
|
3197
|
+
if (!service.config?.base_path?.startsWith("/")) {
|
|
3198
|
+
throw new Error(`${fullName} has no absolute external base path`);
|
|
3199
|
+
}
|
|
3200
|
+
const permissions = await api.agentServicePermissions(fullName);
|
|
3201
|
+
const assignments = Array.isArray(permissions.privilege_assignments) ? permissions.privilege_assignments : [];
|
|
3202
|
+
const expectedPrincipal = env.DBX_TEST_AGENT_SERVICE_EXECUTE_PRINCIPAL;
|
|
3203
|
+
const assignment = assignments.find(
|
|
3204
|
+
(entry) => isObject(entry) && entry.principal === expectedPrincipal
|
|
3205
|
+
);
|
|
3206
|
+
const privileges = isObject(assignment) && Array.isArray(assignment.privileges) ? assignment.privileges : [];
|
|
3207
|
+
const canExecute = privileges.some(
|
|
3208
|
+
(privilege) => isObject(privilege) && privilege.privilege === "EXECUTE"
|
|
3209
|
+
);
|
|
3210
|
+
if (!canExecute) {
|
|
3211
|
+
throw new Error(`${expectedPrincipal} does not have EXECUTE on ${fullName}`);
|
|
3212
|
+
}
|
|
3213
|
+
return {
|
|
3214
|
+
fullName: service.full_name ?? service.name ?? fullName,
|
|
3215
|
+
connection: actualConnection,
|
|
3216
|
+
executePrincipal: expectedPrincipal,
|
|
3217
|
+
runtimeInvocationAvailable: false
|
|
3218
|
+
};
|
|
3219
|
+
}
|
|
3220
|
+
};
|
|
3221
|
+
}
|
|
2850
3222
|
function regionalDrCheck(options = {}) {
|
|
2851
3223
|
const names = [
|
|
2852
3224
|
"DBX_TEST_DR_HOST",
|
|
@@ -2931,7 +3303,7 @@ function createAdvancedStreamingTargetPack() {
|
|
|
2931
3303
|
"lakeflow-connect.replication"
|
|
2932
3304
|
],
|
|
2933
3305
|
checks: () => [restrictedPrincipalCheck(), advancedStreamingCdcCheck(), lakeflowConnectCheck()],
|
|
2934
|
-
requiredChecks: ["streaming-cdc", "lakeflow-connect"],
|
|
3306
|
+
requiredChecks: ["restricted-principal", "streaming-cdc", "lakeflow-connect"],
|
|
2935
3307
|
requirements: [
|
|
2936
3308
|
workspace,
|
|
2937
3309
|
auth,
|
|
@@ -2970,7 +3342,7 @@ function createAiBiGenieTargetPack() {
|
|
|
2970
3342
|
maturity: "live-gated",
|
|
2971
3343
|
capabilities: ["aibi.dashboard", "genie.space", "genie.conversation"],
|
|
2972
3344
|
checks: () => [restrictedPrincipalCheck(), aiBiDashboardCheck(), genieCheck()],
|
|
2973
|
-
requiredChecks: ["aibi-dashboard", "genie"],
|
|
3345
|
+
requiredChecks: ["restricted-principal", "aibi-dashboard", "genie"],
|
|
2974
3346
|
requirements: [
|
|
2975
3347
|
workspace,
|
|
2976
3348
|
auth,
|
|
@@ -3002,7 +3374,7 @@ function createMlflowLifecycleTargetPack() {
|
|
|
3002
3374
|
"models.lifecycle"
|
|
3003
3375
|
],
|
|
3004
3376
|
checks: () => [restrictedPrincipalCheck(), mlflowLifecycleCheck()],
|
|
3005
|
-
requiredChecks: ["mlflow-lifecycle"],
|
|
3377
|
+
requiredChecks: ["restricted-principal", "mlflow-lifecycle"],
|
|
3006
3378
|
requirements: [
|
|
3007
3379
|
workspace,
|
|
3008
3380
|
auth,
|
|
@@ -3034,7 +3406,7 @@ function createFeatureEngineeringTargetPack() {
|
|
|
3034
3406
|
"features.serving"
|
|
3035
3407
|
],
|
|
3036
3408
|
checks: () => [restrictedPrincipalCheck(), featureEngineeringServingCheck()],
|
|
3037
|
-
requiredChecks: ["feature-engineering"],
|
|
3409
|
+
requiredChecks: ["restricted-principal", "feature-engineering"],
|
|
3038
3410
|
requirements: [
|
|
3039
3411
|
workspace,
|
|
3040
3412
|
auth,
|
|
@@ -3071,7 +3443,7 @@ function createModelServingTargetPack() {
|
|
|
3071
3443
|
"ai-gateway.configuration"
|
|
3072
3444
|
],
|
|
3073
3445
|
checks: () => [restrictedPrincipalCheck(), modelServingCheck()],
|
|
3074
|
-
requiredChecks: ["model-serving"],
|
|
3446
|
+
requiredChecks: ["restricted-principal", "model-serving"],
|
|
3075
3447
|
requirements: [
|
|
3076
3448
|
workspace,
|
|
3077
3449
|
auth,
|
|
@@ -3107,7 +3479,7 @@ function createVectorRagAgentsTargetPack() {
|
|
|
3107
3479
|
"agents.serving"
|
|
3108
3480
|
],
|
|
3109
3481
|
checks: () => [restrictedPrincipalCheck(), vectorSearchCheck(), ragAgentCheck()],
|
|
3110
|
-
requiredChecks: ["vector-search", "rag-agent"],
|
|
3482
|
+
requiredChecks: ["restricted-principal", "vector-search", "rag-agent"],
|
|
3111
3483
|
requirements: [
|
|
3112
3484
|
workspace,
|
|
3113
3485
|
auth,
|
|
@@ -3137,7 +3509,7 @@ function createFederationSharingTargetPack() {
|
|
|
3137
3509
|
"clean-rooms.access"
|
|
3138
3510
|
],
|
|
3139
3511
|
checks: () => [restrictedPrincipalCheck(), federationSharingCleanRoomsCheck()],
|
|
3140
|
-
requiredChecks: ["federation-sharing-cleanrooms"],
|
|
3512
|
+
requiredChecks: ["restricted-principal", "federation-sharing-cleanrooms"],
|
|
3141
3513
|
requirements: [
|
|
3142
3514
|
workspace,
|
|
3143
3515
|
auth,
|
|
@@ -3171,7 +3543,7 @@ function createSecurityCostDrTargetPack() {
|
|
|
3171
3543
|
"dr.secondary-region"
|
|
3172
3544
|
],
|
|
3173
3545
|
checks: () => [restrictedPrincipalCheck(), securityPostureCostCheck(), regionalDrCheck()],
|
|
3174
|
-
requiredChecks: ["security-cost", "regional-dr"],
|
|
3546
|
+
requiredChecks: ["restricted-principal", "security-cost", "regional-dr"],
|
|
3175
3547
|
requirements: [
|
|
3176
3548
|
workspace,
|
|
3177
3549
|
auth,
|
|
@@ -3193,6 +3565,234 @@ function createSecurityCostDrTargetPack() {
|
|
|
3193
3565
|
]
|
|
3194
3566
|
});
|
|
3195
3567
|
}
|
|
3568
|
+
function createMlflow3AgentQualityTargetPack() {
|
|
3569
|
+
return defineTargetPack({
|
|
3570
|
+
id: "mlflow3-agent-quality",
|
|
3571
|
+
name: "MLflow 3 agent quality and monitoring",
|
|
3572
|
+
description: "Native MLflow 3 traces, offline agent evaluation, production scorer lifecycle and online monitoring.",
|
|
3573
|
+
version: "1.0.0",
|
|
3574
|
+
maturity: "shipped",
|
|
3575
|
+
capabilities: [
|
|
3576
|
+
"mlflow3.tracing",
|
|
3577
|
+
"mlflow3.agent-evaluation",
|
|
3578
|
+
"mlflow3.production-scorers",
|
|
3579
|
+
"mlflow3.production-monitoring",
|
|
3580
|
+
"ai-search.retrieval-evaluation"
|
|
3581
|
+
],
|
|
3582
|
+
checks: () => [restrictedPrincipalCheck(), mlflow3GenAiQualityCheck()],
|
|
3583
|
+
requiredChecks: ["restricted-principal", "mlflow3-genai-quality"],
|
|
3584
|
+
requirements: [
|
|
3585
|
+
workspace,
|
|
3586
|
+
auth,
|
|
3587
|
+
warehouse,
|
|
3588
|
+
requirement("mlflow3-job", "Native MLflow 3 certification Job", "DBX_TEST_MLFLOW3_JOB_ID"),
|
|
3589
|
+
requirement(
|
|
3590
|
+
"mlflow3-traces",
|
|
3591
|
+
"Boolean SQL proving a fresh MLflow 3 trace",
|
|
3592
|
+
"DBX_TEST_MLFLOW3_TRACE_ASSERTION_SQL"
|
|
3593
|
+
),
|
|
3594
|
+
requirement(
|
|
3595
|
+
"mlflow3-evaluation",
|
|
3596
|
+
"Boolean SQL proving offline agent evaluation results",
|
|
3597
|
+
"DBX_TEST_MLFLOW3_EVAL_ASSERTION_SQL"
|
|
3598
|
+
),
|
|
3599
|
+
requirement(
|
|
3600
|
+
"mlflow3-scorers",
|
|
3601
|
+
"Boolean SQL proving production scorer lifecycle evidence",
|
|
3602
|
+
"DBX_TEST_MLFLOW3_SCORER_ASSERTION_SQL"
|
|
3603
|
+
),
|
|
3604
|
+
requirement(
|
|
3605
|
+
"mlflow3-monitoring",
|
|
3606
|
+
"Boolean SQL proving production monitoring output",
|
|
3607
|
+
"DBX_TEST_MLFLOW3_MONITOR_ASSERTION_SQL"
|
|
3608
|
+
),
|
|
3609
|
+
requirement(
|
|
3610
|
+
"ai-search-evaluation",
|
|
3611
|
+
"Boolean SQL proving AI Search retrieval-quality thresholds",
|
|
3612
|
+
"DBX_TEST_AI_SEARCH_EVAL_ASSERTION_SQL"
|
|
3613
|
+
)
|
|
3614
|
+
],
|
|
3615
|
+
docsUrl: `${DOCS2}#mlflow-3-agent-quality-and-monitoring`,
|
|
3616
|
+
certificationScopes: [
|
|
3617
|
+
`${AZURE_M2M_SCOPE}; Python-native MLflow 3 trace, evaluation, complete scorer lifecycle and monitor evidence`
|
|
3618
|
+
]
|
|
3619
|
+
});
|
|
3620
|
+
}
|
|
3621
|
+
function createAgentEvaluationPipelineTargetPack() {
|
|
3622
|
+
return defineTargetPack({
|
|
3623
|
+
id: "agent-evaluation-pipeline",
|
|
3624
|
+
name: "Agent evaluation delivery pipeline",
|
|
3625
|
+
description: "Exact-candidate API submission, outbox dispatch, Temporal execution, Databricks judge inference, SQL score persistence and Quality completion.",
|
|
3626
|
+
version: "1.0.0",
|
|
3627
|
+
maturity: "live-gated",
|
|
3628
|
+
capabilities: [
|
|
3629
|
+
"agent-evals.api-submission",
|
|
3630
|
+
"agent-evals.temporal-dispatch",
|
|
3631
|
+
"agent-evals.model-serving-judge",
|
|
3632
|
+
"agent-evals.sql-persistence",
|
|
3633
|
+
"agent-evals.quality-evidence"
|
|
3634
|
+
],
|
|
3635
|
+
checks: () => [
|
|
3636
|
+
restrictedPrincipalCheck(),
|
|
3637
|
+
evalJudgeModelServingCheck(),
|
|
3638
|
+
agentEvaluationPipelineCheck()
|
|
3639
|
+
],
|
|
3640
|
+
requiredChecks: ["restricted-principal", "model-eval-judge", "agent-evaluation-pipeline"],
|
|
3641
|
+
requirements: [
|
|
3642
|
+
workspace,
|
|
3643
|
+
auth,
|
|
3644
|
+
warehouse,
|
|
3645
|
+
requirement(
|
|
3646
|
+
"eval-serving-endpoint",
|
|
3647
|
+
"Databricks Model Serving endpoint satisfying the evaluator chat contract",
|
|
3648
|
+
"DBX_TEST_EVAL_SERVING_ENDPOINT"
|
|
3649
|
+
),
|
|
3650
|
+
requirement(
|
|
3651
|
+
"agent-eval-job",
|
|
3652
|
+
"Exact-candidate end-to-end agent evaluation certification Job",
|
|
3653
|
+
"DBX_TEST_AGENT_EVAL_E2E_JOB_ID"
|
|
3654
|
+
),
|
|
3655
|
+
requirement(
|
|
3656
|
+
"agent-eval-evidence",
|
|
3657
|
+
"Boolean SQL proving terminal scores and Quality evidence",
|
|
3658
|
+
"DBX_TEST_AGENT_EVAL_ASSERTION_SQL"
|
|
3659
|
+
)
|
|
3660
|
+
],
|
|
3661
|
+
docsUrl: `${DOCS2}#agent-evaluation-delivery-pipeline`,
|
|
3662
|
+
certificationScopes: [
|
|
3663
|
+
`${AZURE_M2M_SCOPE}; Experiments API through customer worker and Databricks judge to SQL/Quality`
|
|
3664
|
+
]
|
|
3665
|
+
});
|
|
3666
|
+
}
|
|
3667
|
+
function createManagedMcpAgentsTargetPack() {
|
|
3668
|
+
return defineTargetPack({
|
|
3669
|
+
id: "managed-mcp-agents",
|
|
3670
|
+
name: "Managed MCP and MCP Services",
|
|
3671
|
+
description: "Streamable HTTP tool discovery and calls for Databricks managed MCP servers and governed Unity Catalog MCP Services, including a deny control.",
|
|
3672
|
+
version: "1.0.0",
|
|
3673
|
+
maturity: "live-gated",
|
|
3674
|
+
capabilities: [
|
|
3675
|
+
"mcp.managed.list-call",
|
|
3676
|
+
"mcp.services.list-call",
|
|
3677
|
+
"mcp.unity-catalog-governance",
|
|
3678
|
+
"mcp.denial"
|
|
3679
|
+
],
|
|
3680
|
+
checks: () => [restrictedPrincipalCheck(), managedMcpAgentsCheck()],
|
|
3681
|
+
requiredChecks: ["restricted-principal", "managed-mcp-agents"],
|
|
3682
|
+
requirements: [
|
|
3683
|
+
workspace,
|
|
3684
|
+
auth,
|
|
3685
|
+
warehouse,
|
|
3686
|
+
requirement("mcp-job", "Native Streamable HTTP MCP certification Job", "DBX_TEST_MCP_JOB_ID"),
|
|
3687
|
+
requirement(
|
|
3688
|
+
"managed-mcp",
|
|
3689
|
+
"Boolean SQL proving managed MCP list/call behavior",
|
|
3690
|
+
"DBX_TEST_MCP_MANAGED_ASSERTION_SQL"
|
|
3691
|
+
),
|
|
3692
|
+
requirement(
|
|
3693
|
+
"mcp-service",
|
|
3694
|
+
"Boolean SQL proving UC MCP Service list/call behavior",
|
|
3695
|
+
"DBX_TEST_MCP_SERVICE_ASSERTION_SQL"
|
|
3696
|
+
),
|
|
3697
|
+
requirement(
|
|
3698
|
+
"mcp-denial",
|
|
3699
|
+
"Boolean SQL proving an MCP tool excluded by UC selectors was denied",
|
|
3700
|
+
"DBX_TEST_MCP_DENY_ASSERTION_SQL"
|
|
3701
|
+
)
|
|
3702
|
+
],
|
|
3703
|
+
docsUrl: `${DOCS2}#managed-mcp-and-mcp-services`,
|
|
3704
|
+
certificationScopes: [
|
|
3705
|
+
`${AZURE_M2M_SCOPE}; managed AI Search or SQL MCP plus a UC MCP Service over Streamable HTTP`
|
|
3706
|
+
]
|
|
3707
|
+
});
|
|
3708
|
+
}
|
|
3709
|
+
function createDataQualityObservabilityTargetPack() {
|
|
3710
|
+
return defineTargetPack({
|
|
3711
|
+
id: "data-quality-observability",
|
|
3712
|
+
name: "Data quality and Databricks Apps observability",
|
|
3713
|
+
description: "Unity Catalog Data Quality Monitoring plus Databricks Apps OpenTelemetry logs, spans and metrics.",
|
|
3714
|
+
version: "1.0.0",
|
|
3715
|
+
maturity: "shipped",
|
|
3716
|
+
capabilities: [
|
|
3717
|
+
"data-quality.freshness-completeness",
|
|
3718
|
+
"apps.telemetry.logs",
|
|
3719
|
+
"apps.telemetry.traces",
|
|
3720
|
+
"apps.telemetry.metrics"
|
|
3721
|
+
],
|
|
3722
|
+
checks: () => [restrictedPrincipalCheck(), dataQualityAppTelemetryCheck()],
|
|
3723
|
+
requiredChecks: ["restricted-principal", "data-quality-app-telemetry"],
|
|
3724
|
+
requirements: [
|
|
3725
|
+
workspace,
|
|
3726
|
+
auth,
|
|
3727
|
+
warehouse,
|
|
3728
|
+
requirement("telemetry-app", "App with UC telemetry export", "DBX_TEST_TELEMETRY_APP_NAME"),
|
|
3729
|
+
requirement(
|
|
3730
|
+
"data-quality",
|
|
3731
|
+
"Boolean SQL over current Data Quality Monitoring results",
|
|
3732
|
+
"DBX_TEST_DQM_ASSERTION_SQL"
|
|
3733
|
+
),
|
|
3734
|
+
requirement(
|
|
3735
|
+
"app-logs",
|
|
3736
|
+
"Boolean SQL proving current app logs",
|
|
3737
|
+
"DBX_TEST_APP_LOGS_ASSERTION_SQL"
|
|
3738
|
+
),
|
|
3739
|
+
requirement(
|
|
3740
|
+
"app-spans",
|
|
3741
|
+
"Boolean SQL proving current app spans",
|
|
3742
|
+
"DBX_TEST_APP_SPANS_ASSERTION_SQL"
|
|
3743
|
+
),
|
|
3744
|
+
requirement(
|
|
3745
|
+
"app-metrics",
|
|
3746
|
+
"Boolean SQL proving current app metrics",
|
|
3747
|
+
"DBX_TEST_APP_METRICS_ASSERTION_SQL"
|
|
3748
|
+
)
|
|
3749
|
+
],
|
|
3750
|
+
docsUrl: `${DOCS2}#data-quality-and-databricks-apps-observability`,
|
|
3751
|
+
certificationScopes: [
|
|
3752
|
+
`${AZURE_M2M_SCOPE}; system.data_quality_monitoring.table_results and UC OTel tables`
|
|
3753
|
+
]
|
|
3754
|
+
});
|
|
3755
|
+
}
|
|
3756
|
+
function createAgentServicesEnrollmentTargetPack() {
|
|
3757
|
+
return defineTargetPack({
|
|
3758
|
+
id: "agent-services-enrollment",
|
|
3759
|
+
name: "Unity Catalog Agent Services enrollment",
|
|
3760
|
+
description: "Customer-owned Fabric Harness agents registered as external Unity Catalog Agent Services with explicit EXECUTE grants.",
|
|
3761
|
+
version: "1.0.0",
|
|
3762
|
+
maturity: "shipped",
|
|
3763
|
+
capabilities: [
|
|
3764
|
+
"agent-services.external-registration",
|
|
3765
|
+
"agent-services.discovery",
|
|
3766
|
+
"agent-services.permissions",
|
|
3767
|
+
"harness.customer-worker-enrollment"
|
|
3768
|
+
],
|
|
3769
|
+
checks: () => [restrictedPrincipalCheck(), agentServicesEnrollmentCheck()],
|
|
3770
|
+
requiredChecks: ["restricted-principal", "agent-services-enrollment"],
|
|
3771
|
+
requirements: [
|
|
3772
|
+
workspace,
|
|
3773
|
+
auth,
|
|
3774
|
+
requirement(
|
|
3775
|
+
"agent-service",
|
|
3776
|
+
"External Unity Catalog Agent Service full name",
|
|
3777
|
+
"DBX_TEST_AGENT_SERVICE_FULL_NAME"
|
|
3778
|
+
),
|
|
3779
|
+
requirement(
|
|
3780
|
+
"agent-connection",
|
|
3781
|
+
"Expected Unity Catalog HTTP connection",
|
|
3782
|
+
"DBX_TEST_AGENT_SERVICE_CONNECTION"
|
|
3783
|
+
),
|
|
3784
|
+
requirement(
|
|
3785
|
+
"agent-execute-principal",
|
|
3786
|
+
"Principal expected to have EXECUTE",
|
|
3787
|
+
"DBX_TEST_AGENT_SERVICE_EXECUTE_PRINCIPAL"
|
|
3788
|
+
)
|
|
3789
|
+
],
|
|
3790
|
+
docsUrl: `${DOCS2}#unity-catalog-agent-services-enrollment`,
|
|
3791
|
+
certificationScopes: [
|
|
3792
|
+
`${AZURE_M2M_SCOPE}; Agent Services Beta registration/discovery/ACL only; runtime invocation is not claimed`
|
|
3793
|
+
]
|
|
3794
|
+
});
|
|
3795
|
+
}
|
|
3196
3796
|
function createAdvancedTargetPacks() {
|
|
3197
3797
|
return [
|
|
3198
3798
|
createAdvancedStreamingTargetPack(),
|
|
@@ -3202,7 +3802,12 @@ function createAdvancedTargetPacks() {
|
|
|
3202
3802
|
createModelServingTargetPack(),
|
|
3203
3803
|
createVectorRagAgentsTargetPack(),
|
|
3204
3804
|
createFederationSharingTargetPack(),
|
|
3205
|
-
createSecurityCostDrTargetPack()
|
|
3805
|
+
createSecurityCostDrTargetPack(),
|
|
3806
|
+
createMlflow3AgentQualityTargetPack(),
|
|
3807
|
+
createManagedMcpAgentsTargetPack(),
|
|
3808
|
+
createDataQualityObservabilityTargetPack(),
|
|
3809
|
+
createAgentServicesEnrollmentTargetPack(),
|
|
3810
|
+
createAgentEvaluationPipelineTargetPack()
|
|
3206
3811
|
];
|
|
3207
3812
|
}
|
|
3208
3813
|
|
|
@@ -3216,17 +3821,14 @@ function createBuiltinTargetPacks() {
|
|
|
3216
3821
|
}
|
|
3217
3822
|
var builtinTargetPacks = createBuiltinTargetPacks();
|
|
3218
3823
|
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
});
|
|
3223
|
-
Object.defineProperty(exports, "waitForDatabricksStatement", {
|
|
3224
|
-
enumerable: true,
|
|
3225
|
-
get: function () { return databricks.waitForDatabricksStatement; }
|
|
3226
|
-
});
|
|
3824
|
+
// src/index.ts
|
|
3825
|
+
init_workspace_client();
|
|
3826
|
+
|
|
3227
3827
|
exports.DatabricksAdvancedWorkloadsAdapter = DatabricksAdvancedWorkloadsAdapter;
|
|
3228
3828
|
exports.DatabricksJobsAdapter = DatabricksJobsAdapter;
|
|
3229
3829
|
exports.advancedStreamingCdcCheck = advancedStreamingCdcCheck;
|
|
3830
|
+
exports.agentEvaluationPipelineCheck = agentEvaluationPipelineCheck;
|
|
3831
|
+
exports.agentServicesEnrollmentCheck = agentServicesEnrollmentCheck;
|
|
3230
3832
|
exports.aiBiDashboardCheck = aiBiDashboardCheck;
|
|
3231
3833
|
exports.appHealthCheck = appHealthCheck;
|
|
3232
3834
|
exports.assertDeltaContract = assertDeltaContract;
|
|
@@ -3237,10 +3839,13 @@ exports.classicComputeCheck = classicComputeCheck;
|
|
|
3237
3839
|
exports.compareToGolden = compareToGolden;
|
|
3238
3840
|
exports.createAdvancedStreamingTargetPack = createAdvancedStreamingTargetPack;
|
|
3239
3841
|
exports.createAdvancedTargetPacks = createAdvancedTargetPacks;
|
|
3842
|
+
exports.createAgentEvaluationPipelineTargetPack = createAgentEvaluationPipelineTargetPack;
|
|
3843
|
+
exports.createAgentServicesEnrollmentTargetPack = createAgentServicesEnrollmentTargetPack;
|
|
3240
3844
|
exports.createAiBiGenieTargetPack = createAiBiGenieTargetPack;
|
|
3241
3845
|
exports.createAppsOperationalFoundationPack = createAppsOperationalFoundationPack;
|
|
3242
3846
|
exports.createBuiltinTargetPacks = createBuiltinTargetPacks;
|
|
3243
3847
|
exports.createClientFromEnv = createClientFromEnv;
|
|
3848
|
+
exports.createDataQualityObservabilityTargetPack = createDataQualityObservabilityTargetPack;
|
|
3244
3849
|
exports.createDuckDbContext = createDuckDbContext;
|
|
3245
3850
|
exports.createEphemeralLakebaseBranch = createEphemeralLakebaseBranch;
|
|
3246
3851
|
exports.createExecutionContext = createExecutionContext;
|
|
@@ -3251,6 +3856,8 @@ exports.createJobsCodeFoundationPack = createJobsCodeFoundationPack;
|
|
|
3251
3856
|
exports.createLakebaseTestProvider = createLakebaseTestProvider;
|
|
3252
3857
|
exports.createLakeflowFoundationPack = createLakeflowFoundationPack;
|
|
3253
3858
|
exports.createLakeflowJobsTargetPack = createLakeflowJobsTargetPack;
|
|
3859
|
+
exports.createManagedMcpAgentsTargetPack = createManagedMcpAgentsTargetPack;
|
|
3860
|
+
exports.createMlflow3AgentQualityTargetPack = createMlflow3AgentQualityTargetPack;
|
|
3254
3861
|
exports.createMlflowLifecycleTargetPack = createMlflowLifecycleTargetPack;
|
|
3255
3862
|
exports.createMockDatabricksClient = createMockDatabricksClient;
|
|
3256
3863
|
exports.createModelServingTargetPack = createModelServingTargetPack;
|
|
@@ -3261,6 +3868,7 @@ exports.createUnityStorageFoundationPack = createUnityStorageFoundationPack;
|
|
|
3261
3868
|
exports.createVectorRagAgentsTargetPack = createVectorRagAgentsTargetPack;
|
|
3262
3869
|
exports.createWorkspaceContext = createWorkspaceContext;
|
|
3263
3870
|
exports.customLiveCheck = customLiveCheck;
|
|
3871
|
+
exports.dataQualityAppTelemetryCheck = dataQualityAppTelemetryCheck;
|
|
3264
3872
|
exports.dbtBuildCheck = dbtBuildCheck;
|
|
3265
3873
|
exports.defineLiveSuite = defineLiveSuite;
|
|
3266
3874
|
exports.defineTargetPack = defineTargetPack;
|
|
@@ -3284,6 +3892,8 @@ exports.jobsOrchestrationCheck = jobsOrchestrationCheck;
|
|
|
3284
3892
|
exports.lakebaseCredentialCheck = lakebaseCredentialCheck;
|
|
3285
3893
|
exports.lakebaseRoundTripCheck = lakebaseRoundTripCheck;
|
|
3286
3894
|
exports.lakeflowConnectCheck = lakeflowConnectCheck;
|
|
3895
|
+
exports.managedMcpAgentsCheck = managedMcpAgentsCheck;
|
|
3896
|
+
exports.mlflow3GenAiQualityCheck = mlflow3GenAiQualityCheck;
|
|
3287
3897
|
exports.mlflowLifecycleCheck = mlflowLifecycleCheck;
|
|
3288
3898
|
exports.modelServingCheck = modelServingCheck;
|
|
3289
3899
|
exports.normalizeJobRun = normalizeJobRun;
|
|
@@ -3318,6 +3928,8 @@ exports.uploadVolumeFile = uploadVolumeFile;
|
|
|
3318
3928
|
exports.validateJobGraph = validateJobGraph;
|
|
3319
3929
|
exports.vectorSearchCheck = vectorSearchCheck;
|
|
3320
3930
|
exports.volumeIOCheck = volumeIOCheck;
|
|
3931
|
+
exports.waitForDatabricksRun = waitForDatabricksRun;
|
|
3932
|
+
exports.waitForDatabricksStatement = waitForDatabricksStatement;
|
|
3321
3933
|
exports.waitForPipelineUpdate = waitForPipelineUpdate;
|
|
3322
3934
|
//# sourceMappingURL=index.cjs.map
|
|
3323
3935
|
//# sourceMappingURL=index.cjs.map
|