@fabric-harness/databricks 0.18.0 → 1.0.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/dist/app.d.ts +81 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +87 -0
- package/dist/app.js.map +1 -0
- package/dist/client.d.ts +7 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +4 -0
- package/dist/client.js.map +1 -1
- package/dist/governance.d.ts +4 -0
- package/dist/governance.d.ts.map +1 -1
- package/dist/governance.js +14 -2
- package/dist/governance.js.map +1 -1
- package/dist/identity.d.ts +34 -0
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +63 -0
- package/dist/identity.js.map +1 -1
- package/dist/index.d.ts +14 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/mlflow-tracing.d.ts +111 -0
- package/dist/mlflow-tracing.d.ts.map +1 -0
- package/dist/mlflow-tracing.js +167 -0
- package/dist/mlflow-tracing.js.map +1 -0
- package/dist/persistence.d.ts +73 -0
- package/dist/persistence.d.ts.map +1 -0
- package/dist/persistence.js +82 -0
- package/dist/persistence.js.map +1 -0
- package/dist/serving-usage.d.ts +48 -0
- package/dist/serving-usage.d.ts.map +1 -0
- package/dist/serving-usage.js +100 -0
- package/dist/serving-usage.js.map +1 -0
- package/dist/telemetry-sink.d.ts +29 -0
- package/dist/telemetry-sink.d.ts.map +1 -0
- package/dist/telemetry-sink.js +129 -0
- package/dist/telemetry-sink.js.map +1 -0
- package/dist/volumes-attachment-store.d.ts +64 -0
- package/dist/volumes-attachment-store.d.ts.map +1 -0
- package/dist/volumes-attachment-store.js +172 -0
- package/dist/volumes-attachment-store.js.map +1 -0
- package/package.json +9 -4
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import type { FabricEvent, SubmissionTelemetrySink, TelemetrySpan } from '@fabric-harness/sdk';
|
|
2
|
+
import type { DatabricksRestClient } from './client.js';
|
|
3
|
+
/**
|
|
4
|
+
* MLflow trace export (v2 migration, workstream C6): one MLflow trace per
|
|
5
|
+
* settled submission, so agent activity shows up in the workspace's MLflow
|
|
6
|
+
* experiment UI next to models and evaluations.
|
|
7
|
+
*
|
|
8
|
+
* Wiring: `sink` goes into `createSubmissionRunner({ telemetry })` and
|
|
9
|
+
* `onEvent` fans in with the app's `onEvent` callback. A `submission_started`
|
|
10
|
+
* telemetry event opens a span buffer keyed by the store session id
|
|
11
|
+
* (`agent:<agent>:<instanceId>:<session>` — the `sessionId` stamped on
|
|
12
|
+
* `FabricEvent`s during the attempt); every event that matches an open buffer
|
|
13
|
+
* and converts via the SDK's `eventToTelemetrySpan` (i.e. carries
|
|
14
|
+
* `data.durationMs`) is appended as a child span. `submission_settled` builds
|
|
15
|
+
* the trace and POSTs it to the MLflow **V3 traces API**
|
|
16
|
+
* (`POST /api/3.0/mlflow/traces`), then drops the buffer.
|
|
17
|
+
*
|
|
18
|
+
* The V3 payload ({@link buildMlflowTrace}) is implemented to the published
|
|
19
|
+
* API spec — OTLP-shaped spans with base64 ids, unix-nano timestamps and
|
|
20
|
+
* typed attributes — but live verification requires a workspace: run the
|
|
21
|
+
* live-gated smoke test (`DATABRICKS_LIVE_TEST=1` +
|
|
22
|
+
* `DATABRICKS_MLFLOW_EXPERIMENT_ID`) against yours before relying on it.
|
|
23
|
+
*
|
|
24
|
+
* Both `sink.record` and `onEvent` never throw — failures (including the
|
|
25
|
+
* async POST) are reported through `onError`.
|
|
26
|
+
*/
|
|
27
|
+
export interface MlflowTraceExporterOptions {
|
|
28
|
+
client: DatabricksRestClient;
|
|
29
|
+
/** MLflow experiment receiving the traces. */
|
|
30
|
+
experimentId: string;
|
|
31
|
+
/** Failure reporter (default console.error). */
|
|
32
|
+
onError?: (context: string, error: unknown) => void;
|
|
33
|
+
}
|
|
34
|
+
export interface MlflowTraceExporter {
|
|
35
|
+
/** Wire into `createSubmissionRunner({ telemetry })`. */
|
|
36
|
+
sink: SubmissionTelemetrySink;
|
|
37
|
+
/** Fan in with the app's `onEvent` to capture per-turn/tool spans. */
|
|
38
|
+
onEvent: (event: FabricEvent) => void;
|
|
39
|
+
}
|
|
40
|
+
export declare function mlflowTraceExporter(options: MlflowTraceExporterOptions): MlflowTraceExporter;
|
|
41
|
+
export interface MlflowTraceInput {
|
|
42
|
+
submissionId: string;
|
|
43
|
+
agent: string;
|
|
44
|
+
instanceId: string;
|
|
45
|
+
session: string;
|
|
46
|
+
experimentId: string;
|
|
47
|
+
/** Epoch ms when the submission started executing. */
|
|
48
|
+
startedAtMs: number;
|
|
49
|
+
/** Epoch ms when the submission settled. */
|
|
50
|
+
settledAtMs: number;
|
|
51
|
+
state: 'OK' | 'ERROR';
|
|
52
|
+
/** Child spans (from `eventToTelemetrySpan`) buffered during execution. */
|
|
53
|
+
spans: TelemetrySpan[];
|
|
54
|
+
attemptCount?: number;
|
|
55
|
+
tenantId?: string;
|
|
56
|
+
error?: string;
|
|
57
|
+
}
|
|
58
|
+
interface OtlpAttribute {
|
|
59
|
+
key: string;
|
|
60
|
+
value: {
|
|
61
|
+
string_value: string;
|
|
62
|
+
} | {
|
|
63
|
+
double_value: number;
|
|
64
|
+
} | {
|
|
65
|
+
int_value: string;
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
interface MlflowSpan {
|
|
69
|
+
trace_id: string;
|
|
70
|
+
span_id: string;
|
|
71
|
+
parent_span_id?: string;
|
|
72
|
+
name: string;
|
|
73
|
+
start_time_unix_nano: string;
|
|
74
|
+
end_time_unix_nano: string;
|
|
75
|
+
attributes: OtlpAttribute[];
|
|
76
|
+
status: {
|
|
77
|
+
code: 'STATUS_CODE_OK' | 'STATUS_CODE_ERROR';
|
|
78
|
+
message?: string;
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
export interface MlflowTracePayload {
|
|
82
|
+
trace: {
|
|
83
|
+
trace_info: {
|
|
84
|
+
trace_id: string;
|
|
85
|
+
trace_location: {
|
|
86
|
+
type: 'MLFLOW_EXPERIMENT';
|
|
87
|
+
mlflow_experiment: {
|
|
88
|
+
experiment_id: string;
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
request_time: string;
|
|
92
|
+
execution_duration: string;
|
|
93
|
+
state: 'OK' | 'ERROR';
|
|
94
|
+
trace_metadata: Record<string, string>;
|
|
95
|
+
tags: Record<string, string>;
|
|
96
|
+
};
|
|
97
|
+
trace_data: {
|
|
98
|
+
spans: MlflowSpan[];
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Build the MLflow V3 `POST /api/3.0/mlflow/traces` payload for one settled
|
|
104
|
+
* submission. Pure and deterministic: trace/span ids derive from the
|
|
105
|
+
* submission id (+ span index) via SHA-256, so replaying the same settled
|
|
106
|
+
* submission produces byte-identical ids. The root span covers the whole
|
|
107
|
+
* submission; buffered {@link TelemetrySpan}s become its children.
|
|
108
|
+
*/
|
|
109
|
+
export declare function buildMlflowTrace(input: MlflowTraceInput): MlflowTracePayload;
|
|
110
|
+
export {};
|
|
111
|
+
//# sourceMappingURL=mlflow-tracing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mlflow-tracing.d.ts","sourceRoot":"","sources":["../src/mlflow-tracing.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,WAAW,EACX,uBAAuB,EACvB,aAAa,EACd,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAExD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,oBAAoB,CAAC;IAC7B,8CAA8C;IAC9C,YAAY,EAAE,MAAM,CAAC;IACrB,gDAAgD;IAChD,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACrD;AAED,MAAM,WAAW,mBAAmB;IAClC,yDAAyD;IACzD,IAAI,EAAE,uBAAuB,CAAC;IAC9B,sEAAsE;IACtE,OAAO,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;CACvC;AAaD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,0BAA0B,GAAG,mBAAmB,CAoE5F;AAID,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,sDAAsD;IACtD,WAAW,EAAE,MAAM,CAAC;IACpB,4CAA4C;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,IAAI,GAAG,OAAO,CAAC;IACtB,2EAA2E;IAC3E,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,aAAa;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;CACpF;AAED,UAAU,UAAU;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB,EAAE,MAAM,CAAC;IAC7B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,UAAU,EAAE,aAAa,EAAE,CAAC;IAC5B,MAAM,EAAE;QAAE,IAAI,EAAE,gBAAgB,GAAG,mBAAmB,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5E;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE;QACL,UAAU,EAAE;YACV,QAAQ,EAAE,MAAM,CAAC;YACjB,cAAc,EAAE;gBACd,IAAI,EAAE,mBAAmB,CAAC;gBAC1B,iBAAiB,EAAE;oBAAE,aAAa,EAAE,MAAM,CAAA;iBAAE,CAAC;aAC9C,CAAC;YACF,YAAY,EAAE,MAAM,CAAC;YACrB,kBAAkB,EAAE,MAAM,CAAC;YAC3B,KAAK,EAAE,IAAI,GAAG,OAAO,CAAC;YACtB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YACvC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;SAC9B,CAAC;QACF,UAAU,EAAE;YAAE,KAAK,EAAE,UAAU,EAAE,CAAA;SAAE,CAAC;KACrC,CAAC;CACH;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,gBAAgB,GAAG,kBAAkB,CAmE5E"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { eventToTelemetrySpan } from '@fabric-harness/sdk';
|
|
3
|
+
export function mlflowTraceExporter(options) {
|
|
4
|
+
const onError = options.onError ??
|
|
5
|
+
((context, error) => {
|
|
6
|
+
console.error(`[fabric-harness:mlflow-tracing] ${context}:`, error);
|
|
7
|
+
});
|
|
8
|
+
/** Open buffers keyed by store session id (`agent:<agent>:<instanceId>:<session>`). */
|
|
9
|
+
const buffers = new Map();
|
|
10
|
+
return {
|
|
11
|
+
sink: {
|
|
12
|
+
record(event) {
|
|
13
|
+
try {
|
|
14
|
+
const key = `agent:${event.agent}:${event.instanceId}:${event.session}`;
|
|
15
|
+
if (event.type === 'submission_started') {
|
|
16
|
+
buffers.set(key, {
|
|
17
|
+
submissionId: event.submissionId,
|
|
18
|
+
agent: event.agent,
|
|
19
|
+
instanceId: event.instanceId,
|
|
20
|
+
session: event.session,
|
|
21
|
+
startedAtMs: event.at,
|
|
22
|
+
attemptCount: event.attemptCount,
|
|
23
|
+
...(event.tenantId !== undefined ? { tenantId: event.tenantId } : {}),
|
|
24
|
+
spans: [],
|
|
25
|
+
});
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (event.type !== 'submission_settled')
|
|
29
|
+
return;
|
|
30
|
+
const buffer = buffers.get(key);
|
|
31
|
+
buffers.delete(key);
|
|
32
|
+
// Settlement without an observed start (exporter attached late) still
|
|
33
|
+
// produces a trace: derive the start from the settlement duration.
|
|
34
|
+
const startedAtMs = buffer?.startedAtMs ?? event.at - (event.durationMs ?? 0);
|
|
35
|
+
const attemptCount = event.attemptCount ?? buffer?.attemptCount;
|
|
36
|
+
const trace = buildMlflowTrace({
|
|
37
|
+
submissionId: event.submissionId,
|
|
38
|
+
agent: event.agent,
|
|
39
|
+
instanceId: event.instanceId,
|
|
40
|
+
session: event.session,
|
|
41
|
+
experimentId: options.experimentId,
|
|
42
|
+
startedAtMs,
|
|
43
|
+
settledAtMs: event.at,
|
|
44
|
+
state: event.outcome === 'completed' ? 'OK' : 'ERROR',
|
|
45
|
+
spans: buffer?.spans ?? [],
|
|
46
|
+
...(attemptCount !== undefined ? { attemptCount } : {}),
|
|
47
|
+
...(event.tenantId !== undefined ? { tenantId: event.tenantId } : {}),
|
|
48
|
+
...(event.error !== undefined ? { error: event.error } : {}),
|
|
49
|
+
});
|
|
50
|
+
void options.client
|
|
51
|
+
.post('/api/3.0/mlflow/traces', trace)
|
|
52
|
+
.catch((error) => onError(`trace export failed for ${event.submissionId}`, error));
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
onError('telemetry event handling failed', error);
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
onEvent(event) {
|
|
60
|
+
try {
|
|
61
|
+
if (!event.sessionId)
|
|
62
|
+
return;
|
|
63
|
+
const buffer = buffers.get(event.sessionId);
|
|
64
|
+
if (!buffer)
|
|
65
|
+
return;
|
|
66
|
+
const span = eventToTelemetrySpan(event);
|
|
67
|
+
if (span)
|
|
68
|
+
buffer.spans.push(span);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
onError('event buffering failed', error);
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Build the MLflow V3 `POST /api/3.0/mlflow/traces` payload for one settled
|
|
78
|
+
* submission. Pure and deterministic: trace/span ids derive from the
|
|
79
|
+
* submission id (+ span index) via SHA-256, so replaying the same settled
|
|
80
|
+
* submission produces byte-identical ids. The root span covers the whole
|
|
81
|
+
* submission; buffered {@link TelemetrySpan}s become its children.
|
|
82
|
+
*/
|
|
83
|
+
export function buildMlflowTrace(input) {
|
|
84
|
+
const traceBytes = sha256(input.submissionId).subarray(0, 16);
|
|
85
|
+
const traceIdBase64 = traceBytes.toString('base64');
|
|
86
|
+
const rootSpanId = spanIdFor(input.submissionId, 0);
|
|
87
|
+
const durationSeconds = Math.max(0, input.settledAtMs - input.startedAtMs) / 1000;
|
|
88
|
+
const rootSpan = {
|
|
89
|
+
trace_id: traceIdBase64,
|
|
90
|
+
span_id: rootSpanId,
|
|
91
|
+
name: `fabric.submission ${input.agent}`,
|
|
92
|
+
start_time_unix_nano: toUnixNano(input.startedAtMs),
|
|
93
|
+
end_time_unix_nano: toUnixNano(input.settledAtMs),
|
|
94
|
+
attributes: toOtlpAttributes({
|
|
95
|
+
'fabric.submission_id': input.submissionId,
|
|
96
|
+
'fabric.agent': input.agent,
|
|
97
|
+
'fabric.instance_id': input.instanceId,
|
|
98
|
+
'fabric.session': input.session,
|
|
99
|
+
...(input.attemptCount !== undefined ? { 'fabric.attempt_count': input.attemptCount } : {}),
|
|
100
|
+
...(input.error !== undefined ? { 'fabric.error': input.error } : {}),
|
|
101
|
+
}),
|
|
102
|
+
status: {
|
|
103
|
+
code: input.state === 'OK' ? 'STATUS_CODE_OK' : 'STATUS_CODE_ERROR',
|
|
104
|
+
...(input.error !== undefined ? { message: input.error } : {}),
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
const childSpans = input.spans.map((span, index) => ({
|
|
108
|
+
trace_id: traceIdBase64,
|
|
109
|
+
span_id: spanIdFor(input.submissionId, index + 1),
|
|
110
|
+
parent_span_id: rootSpanId,
|
|
111
|
+
name: span.name,
|
|
112
|
+
start_time_unix_nano: toUnixNano(Date.parse(span.startedAt)),
|
|
113
|
+
end_time_unix_nano: toUnixNano(Date.parse(span.endedAt)),
|
|
114
|
+
attributes: toOtlpAttributes(span.attributes),
|
|
115
|
+
status: { code: 'STATUS_CODE_OK' },
|
|
116
|
+
}));
|
|
117
|
+
return {
|
|
118
|
+
trace: {
|
|
119
|
+
trace_info: {
|
|
120
|
+
trace_id: `tr-${traceBytes.toString('hex')}`,
|
|
121
|
+
trace_location: {
|
|
122
|
+
type: 'MLFLOW_EXPERIMENT',
|
|
123
|
+
mlflow_experiment: { experiment_id: input.experimentId },
|
|
124
|
+
},
|
|
125
|
+
request_time: new Date(input.startedAtMs).toISOString(),
|
|
126
|
+
execution_duration: `${durationSeconds.toFixed(3)}s`,
|
|
127
|
+
state: input.state,
|
|
128
|
+
trace_metadata: {
|
|
129
|
+
'fabric.submission_id': input.submissionId,
|
|
130
|
+
'fabric.agent': input.agent,
|
|
131
|
+
'fabric.instance_id': input.instanceId,
|
|
132
|
+
'fabric.session': input.session,
|
|
133
|
+
...(input.attemptCount !== undefined
|
|
134
|
+
? { 'fabric.attempt_count': String(input.attemptCount) }
|
|
135
|
+
: {}),
|
|
136
|
+
},
|
|
137
|
+
tags: {
|
|
138
|
+
'fabric.agent': input.agent,
|
|
139
|
+
...(input.tenantId !== undefined ? { 'fabric.tenant_id': input.tenantId } : {}),
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
trace_data: { spans: [rootSpan, ...childSpans] },
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
function sha256(seed) {
|
|
147
|
+
return createHash('sha256').update(seed).digest();
|
|
148
|
+
}
|
|
149
|
+
/** Deterministic 8-byte OTLP span id: sha-256 of `<submissionId>:span:<index>`. Index 0 is the root. */
|
|
150
|
+
function spanIdFor(submissionId, index) {
|
|
151
|
+
return sha256(`${submissionId}:span:${index}`).subarray(0, 8).toString('base64');
|
|
152
|
+
}
|
|
153
|
+
function toUnixNano(epochMs) {
|
|
154
|
+
return `${Math.round(epochMs)}000000`;
|
|
155
|
+
}
|
|
156
|
+
function toOtlpAttributes(attributes) {
|
|
157
|
+
return Object.entries(attributes).map(([key, value]) => {
|
|
158
|
+
if (typeof value === 'number') {
|
|
159
|
+
// OTLP proto3 JSON mapping: int64 serializes as a string.
|
|
160
|
+
return Number.isInteger(value)
|
|
161
|
+
? { key, value: { int_value: String(value) } }
|
|
162
|
+
: { key, value: { double_value: value } };
|
|
163
|
+
}
|
|
164
|
+
return { key, value: { string_value: String(value) } };
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=mlflow-tracing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mlflow-tracing.js","sourceRoot":"","sources":["../src/mlflow-tracing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { AgentSubmissionStore, ConversationStreamStore, PersistenceAdapter, SessionStore } from '@fabric-harness/sdk';
|
|
2
|
+
import type { LakebaseClient, LakebaseClientOptions } from './lakebase.js';
|
|
3
|
+
/**
|
|
4
|
+
* The slice of `@fabric-harness/node` this adapter assembles. Loaded
|
|
5
|
+
* dynamically (node is an optional peer — Databricks Apps always run on
|
|
6
|
+
* Node, but this package stays importable without it); injectable for tests.
|
|
7
|
+
*/
|
|
8
|
+
export interface DatabricksPersistenceStoreModule {
|
|
9
|
+
PostgresSessionStore: new (options: {
|
|
10
|
+
client: LakebaseClient;
|
|
11
|
+
initialize?: boolean;
|
|
12
|
+
}) => SessionStore;
|
|
13
|
+
PostgresSubmissionStore: new (options: {
|
|
14
|
+
client: LakebaseClient;
|
|
15
|
+
initialize?: boolean;
|
|
16
|
+
}) => AgentSubmissionStore;
|
|
17
|
+
PostgresConversationStreamStore: new (options: {
|
|
18
|
+
client: LakebaseClient;
|
|
19
|
+
initialize?: boolean;
|
|
20
|
+
}) => ConversationStreamStore;
|
|
21
|
+
ensurePostgresSubmissionTables(client: LakebaseClient): Promise<void>;
|
|
22
|
+
ensurePostgresConversationStreamTables(client: LakebaseClient): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
export interface DatabricksPersistenceOptions {
|
|
25
|
+
/**
|
|
26
|
+
* Lakebase connection options (rotating-OAuth `token` supported), or an
|
|
27
|
+
* already-constructed client to reuse.
|
|
28
|
+
*/
|
|
29
|
+
lakebase: LakebaseClientOptions | LakebaseClient;
|
|
30
|
+
/** Override the `@fabric-harness/node` loader (tests / bundlers). */
|
|
31
|
+
loadStores?: () => Promise<DatabricksPersistenceStoreModule>;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Everything the v2 runtime persists, on Lakebase, in one adapter:
|
|
35
|
+
* sessions (canonical conversation state), durable agent submissions
|
|
36
|
+
* (admission/FIFO/leases/settlement), and offset-addressable conversation
|
|
37
|
+
* streams. Implements the SDK {@link PersistenceAdapter} contract, so it
|
|
38
|
+
* plugs straight into `init({ persistence })`; the extra `connect*` methods
|
|
39
|
+
* feed `startDevServer({ submissionStore, conversationStreamStore })`.
|
|
40
|
+
*/
|
|
41
|
+
export interface DatabricksPersistence extends PersistenceAdapter {
|
|
42
|
+
readonly client: LakebaseClient;
|
|
43
|
+
connect(): Promise<SessionStore>;
|
|
44
|
+
connectSubmissionStore(): Promise<AgentSubmissionStore>;
|
|
45
|
+
connectConversationStreamStore(): Promise<ConversationStreamStore>;
|
|
46
|
+
/** All three stores in one call (server wiring convenience). */
|
|
47
|
+
connectAll(): Promise<{
|
|
48
|
+
store: SessionStore;
|
|
49
|
+
submissionStore: AgentSubmissionStore;
|
|
50
|
+
conversationStreamStore: ConversationStreamStore;
|
|
51
|
+
}>;
|
|
52
|
+
/** Idempotent schema setup for every store (CREATE TABLE IF NOT EXISTS). */
|
|
53
|
+
migrate(): Promise<void>;
|
|
54
|
+
close(): Promise<void>;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* One-call Lakebase persistence for Databricks-hosted agents.
|
|
58
|
+
*
|
|
59
|
+
* ```ts
|
|
60
|
+
* const persistence = databricksPersistence({
|
|
61
|
+
* lakebase: { host, database: 'fabric', user: clientId, token: identity },
|
|
62
|
+
* });
|
|
63
|
+
* const { store, submissionStore, conversationStreamStore } = await persistence.connectAll();
|
|
64
|
+
* await startDevServer({ submissionStore, conversationStreamStore });
|
|
65
|
+
* ```
|
|
66
|
+
*
|
|
67
|
+
* The Postgres store implementations come from `@fabric-harness/node`
|
|
68
|
+
* (optional peer) and run unchanged against Lakebase — the only Databricks
|
|
69
|
+
* specifics are the rotating-token pool and TLS defaults from
|
|
70
|
+
* {@link lakebaseClient}.
|
|
71
|
+
*/
|
|
72
|
+
export declare function databricksPersistence(options: DatabricksPersistenceOptions): DatabricksPersistence;
|
|
73
|
+
//# sourceMappingURL=persistence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persistence.d.ts","sourceRoot":"","sources":["../src/persistence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oBAAoB,EACpB,uBAAuB,EACvB,kBAAkB,EAClB,YAAY,EACb,MAAM,qBAAqB,CAAC;AAE7B,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAE3E;;;;GAIG;AACH,MAAM,WAAW,gCAAgC;IAC/C,oBAAoB,EAAE,KAAK,OAAO,EAAE;QAAE,MAAM,EAAE,cAAc,CAAC;QAAC,UAAU,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,YAAY,CAAC;IACtG,uBAAuB,EAAE,KAAK,OAAO,EAAE;QAAE,MAAM,EAAE,cAAc,CAAC;QAAC,UAAU,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,oBAAoB,CAAC;IACjH,+BAA+B,EAAE,KAAK,OAAO,EAAE;QAAE,MAAM,EAAE,cAAc,CAAC;QAAC,UAAU,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,uBAAuB,CAAC;IAC5H,8BAA8B,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtE,sCAAsC,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/E;AAED,MAAM,WAAW,4BAA4B;IAC3C;;;OAGG;IACH,QAAQ,EAAE,qBAAqB,GAAG,cAAc,CAAC;IACjD,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,gCAAgC,CAAC,CAAC;CAC9D;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,qBAAsB,SAAQ,kBAAkB;IAC/D,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,OAAO,IAAI,OAAO,CAAC,YAAY,CAAC,CAAC;IACjC,sBAAsB,IAAI,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACxD,8BAA8B,IAAI,OAAO,CAAC,uBAAuB,CAAC,CAAC;IACnE,gEAAgE;IAChE,UAAU,IAAI,OAAO,CAAC;QACpB,KAAK,EAAE,YAAY,CAAC;QACpB,eAAe,EAAE,oBAAoB,CAAC;QACtC,uBAAuB,EAAE,uBAAuB,CAAC;KAClD,CAAC,CAAC;IACH,4EAA4E;IAC5E,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,4BAA4B,GAAG,qBAAqB,CA2DlG"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { lakebaseClient } from './lakebase.js';
|
|
2
|
+
/**
|
|
3
|
+
* One-call Lakebase persistence for Databricks-hosted agents.
|
|
4
|
+
*
|
|
5
|
+
* ```ts
|
|
6
|
+
* const persistence = databricksPersistence({
|
|
7
|
+
* lakebase: { host, database: 'fabric', user: clientId, token: identity },
|
|
8
|
+
* });
|
|
9
|
+
* const { store, submissionStore, conversationStreamStore } = await persistence.connectAll();
|
|
10
|
+
* await startDevServer({ submissionStore, conversationStreamStore });
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* The Postgres store implementations come from `@fabric-harness/node`
|
|
14
|
+
* (optional peer) and run unchanged against Lakebase — the only Databricks
|
|
15
|
+
* specifics are the rotating-token pool and TLS defaults from
|
|
16
|
+
* {@link lakebaseClient}.
|
|
17
|
+
*/
|
|
18
|
+
export function databricksPersistence(options) {
|
|
19
|
+
const client = isLakebaseClient(options.lakebase) ? options.lakebase : lakebaseClient(options.lakebase);
|
|
20
|
+
const ownsClient = !isLakebaseClient(options.lakebase);
|
|
21
|
+
const loadStores = options.loadStores ?? defaultStoreLoader;
|
|
22
|
+
let modulePromise;
|
|
23
|
+
const stores = () => {
|
|
24
|
+
modulePromise ??= loadStores();
|
|
25
|
+
return modulePromise;
|
|
26
|
+
};
|
|
27
|
+
let sessionStore;
|
|
28
|
+
let submissionStore;
|
|
29
|
+
let conversationStreamStore;
|
|
30
|
+
const persistence = {
|
|
31
|
+
client,
|
|
32
|
+
async connect() {
|
|
33
|
+
const module = await stores();
|
|
34
|
+
sessionStore ??= new module.PostgresSessionStore({ client });
|
|
35
|
+
return sessionStore;
|
|
36
|
+
},
|
|
37
|
+
async connectSubmissionStore() {
|
|
38
|
+
const module = await stores();
|
|
39
|
+
submissionStore ??= new module.PostgresSubmissionStore({ client });
|
|
40
|
+
return submissionStore;
|
|
41
|
+
},
|
|
42
|
+
async connectConversationStreamStore() {
|
|
43
|
+
const module = await stores();
|
|
44
|
+
conversationStreamStore ??= new module.PostgresConversationStreamStore({ client });
|
|
45
|
+
return conversationStreamStore;
|
|
46
|
+
},
|
|
47
|
+
async connectAll() {
|
|
48
|
+
return {
|
|
49
|
+
store: await persistence.connect(),
|
|
50
|
+
submissionStore: await persistence.connectSubmissionStore(),
|
|
51
|
+
conversationStreamStore: await persistence.connectConversationStreamStore(),
|
|
52
|
+
};
|
|
53
|
+
},
|
|
54
|
+
async migrate() {
|
|
55
|
+
const module = await stores();
|
|
56
|
+
// Session tables initialize inside the store constructor; the
|
|
57
|
+
// submission/stream ensure functions are exported directly.
|
|
58
|
+
await persistence.connect();
|
|
59
|
+
await module.ensurePostgresSubmissionTables(client);
|
|
60
|
+
await module.ensurePostgresConversationStreamTables(client);
|
|
61
|
+
},
|
|
62
|
+
async close() {
|
|
63
|
+
if (ownsClient)
|
|
64
|
+
await client.end();
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
return persistence;
|
|
68
|
+
}
|
|
69
|
+
function isLakebaseClient(value) {
|
|
70
|
+
return typeof value.query === 'function';
|
|
71
|
+
}
|
|
72
|
+
async function defaultStoreLoader() {
|
|
73
|
+
try {
|
|
74
|
+
// Dynamic specifier keeps bundlers from hard-wiring the optional peer.
|
|
75
|
+
const specifier = '@fabric-harness/node';
|
|
76
|
+
return (await import(specifier));
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
throw new Error(`databricksPersistence() requires the optional "@fabric-harness/node" package (it provides the Postgres store implementations). Install it alongside @fabric-harness/databricks. ${error instanceof Error ? error.message : String(error)}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=persistence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persistence.js","sourceRoot":"","sources":["../src/persistence.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAkD/C;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAqC;IACzE,MAAM,MAAM,GAAG,gBAAgB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACxG,MAAM,UAAU,GAAG,CAAC,gBAAgB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACvD,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,kBAAkB,CAAC;IAE5D,IAAI,aAAoE,CAAC;IACzE,MAAM,MAAM,GAAG,GAAG,EAAE;QAClB,aAAa,KAAK,UAAU,EAAE,CAAC;QAC/B,OAAO,aAAa,CAAC;IACvB,CAAC,CAAC;IAEF,IAAI,YAAsC,CAAC;IAC3C,IAAI,eAAiD,CAAC;IACtD,IAAI,uBAA4D,CAAC;IAEjE,MAAM,WAAW,GAA0B;QACzC,MAAM;QAEN,KAAK,CAAC,OAAO;YACX,MAAM,MAAM,GAAG,MAAM,MAAM,EAAE,CAAC;YAC9B,YAAY,KAAK,IAAI,MAAM,CAAC,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;YAC7D,OAAO,YAAY,CAAC;QACtB,CAAC;QAED,KAAK,CAAC,sBAAsB;YAC1B,MAAM,MAAM,GAAG,MAAM,MAAM,EAAE,CAAC;YAC9B,eAAe,KAAK,IAAI,MAAM,CAAC,uBAAuB,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;YACnE,OAAO,eAAe,CAAC;QACzB,CAAC;QAED,KAAK,CAAC,8BAA8B;YAClC,MAAM,MAAM,GAAG,MAAM,MAAM,EAAE,CAAC;YAC9B,uBAAuB,KAAK,IAAI,MAAM,CAAC,+BAA+B,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;YACnF,OAAO,uBAAuB,CAAC;QACjC,CAAC;QAED,KAAK,CAAC,UAAU;YACd,OAAO;gBACL,KAAK,EAAE,MAAM,WAAW,CAAC,OAAO,EAAE;gBAClC,eAAe,EAAE,MAAM,WAAW,CAAC,sBAAsB,EAAE;gBAC3D,uBAAuB,EAAE,MAAM,WAAW,CAAC,8BAA8B,EAAE;aAC5E,CAAC;QACJ,CAAC;QAED,KAAK,CAAC,OAAO;YACX,MAAM,MAAM,GAAG,MAAM,MAAM,EAAE,CAAC;YAC9B,8DAA8D;YAC9D,4DAA4D;YAC5D,MAAM,WAAW,CAAC,OAAO,EAAE,CAAC;YAC5B,MAAM,MAAM,CAAC,8BAA8B,CAAC,MAAM,CAAC,CAAC;YACpD,MAAM,MAAM,CAAC,sCAAsC,CAAC,MAAM,CAAC,CAAC;QAC9D,CAAC;QAED,KAAK,CAAC,KAAK;YACT,IAAI,UAAU;gBAAE,MAAM,MAAM,CAAC,GAAG,EAAE,CAAC;QACrC,CAAC;KACF,CAAC;IAEF,OAAO,WAAW,CAAC;AACrB,CAAC;AAED,SAAS,gBAAgB,CAAC,KAA+C;IACvE,OAAO,OAAQ,KAAwB,CAAC,KAAK,KAAK,UAAU,CAAC;AAC/D,CAAC;AAED,KAAK,UAAU,kBAAkB;IAC/B,IAAI,CAAC;QACH,uEAAuE;QACvE,MAAM,SAAS,GAAG,sBAAsB,CAAC;QACzC,OAAO,CAAC,MAAM,MAAM,CAAC,SAAS,CAAC,CAAgD,CAAC;IAClF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CACb,mLAAmL,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAC5O,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { FabricEvent, SubmissionTelemetrySink } from '@fabric-harness/sdk';
|
|
2
|
+
/**
|
|
3
|
+
* Serving usage capture (v2 migration, workstream C7): attribute model/serving
|
|
4
|
+
* spend to submissions through the ambient submission context.
|
|
5
|
+
*/
|
|
6
|
+
export interface ServingUsageCaptureOptions {
|
|
7
|
+
/** Telemetry sink receiving `submission_usage` events (e.g. `databricksTelemetry().sink`). */
|
|
8
|
+
sink: SubmissionTelemetrySink;
|
|
9
|
+
/** Failure reporter for sink errors (default: silent, matching fire-and-forget telemetry). */
|
|
10
|
+
onError?: (error: unknown) => void;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Build an `onEvent` fan-in that turns model-usage events into
|
|
14
|
+
* `submission_usage` telemetry. For every {@link FabricEvent} carrying
|
|
15
|
+
* `data.usage` (`{ inputTokens?, outputTokens?, costUsd? }`, at least one
|
|
16
|
+
* numeric field) — and optionally `data.model` — while an ambient
|
|
17
|
+
* {@link currentSubmissionContext} is set, it emits one `submission_usage`
|
|
18
|
+
* event stamped with the submission correlation (submission/attempt ids,
|
|
19
|
+
* agent identity, tenant, actor).
|
|
20
|
+
*
|
|
21
|
+
* Events observed outside a submission context are dropped silently — not
|
|
22
|
+
* every session turn belongs to a submission. The submission context does not
|
|
23
|
+
* carry the submission kind, so emitted events use `kind: 'unknown'`.
|
|
24
|
+
*/
|
|
25
|
+
export declare function servingUsageCapture(options: ServingUsageCaptureOptions): (event: FabricEvent) => void;
|
|
26
|
+
export interface InferenceTableUsageQueryOptions {
|
|
27
|
+
/** Fully-qualified inference/payload table, `catalog.schema.table`. */
|
|
28
|
+
fullTableName: string;
|
|
29
|
+
/** Only aggregate requests at or after this ISO-8601 instant. */
|
|
30
|
+
sinceIso?: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* SQL aggregating a serving endpoint's inference/payload table by
|
|
34
|
+
* `client_request_id` — one row per submission with the request count and the
|
|
35
|
+
* first/last request timestamps — for offline joins against
|
|
36
|
+
* `fh_submission_telemetry.submission_id`.
|
|
37
|
+
*
|
|
38
|
+
* Join contract: callers MUST send the submission id as the serving request's
|
|
39
|
+
* `client_request_id` (e.g. the `client_request_id` field on the
|
|
40
|
+
* serving-endpoint invocation) — the inference table records it verbatim, and
|
|
41
|
+
* this query's `submission_id` column only joins when that convention holds.
|
|
42
|
+
* Rows without a `client_request_id` (traffic from other callers) are
|
|
43
|
+
* excluded. Assumes the AI Gateway payload-table schema (`request_time`
|
|
44
|
+
* timestamp column); adapt the projection for classic inference tables that
|
|
45
|
+
* use `timestamp_ms`.
|
|
46
|
+
*/
|
|
47
|
+
export declare function inferenceTableUsageQuery(options: InferenceTableUsageQueryOptions): string;
|
|
48
|
+
//# sourceMappingURL=serving-usage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serving-usage.d.ts","sourceRoot":"","sources":["../src/serving-usage.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAEhF;;;GAGG;AACH,MAAM,WAAW,0BAA0B;IACzC,8FAA8F;IAC9F,IAAI,EAAE,uBAAuB,CAAC;IAC9B,8FAA8F;IAC9F,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACpC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,0BAA0B,GAAG,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAiCrG;AAuBD,MAAM,WAAW,+BAA+B;IAC9C,uEAAuE;IACvE,aAAa,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,+BAA+B,GAAG,MAAM,CAyBzF"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { currentSubmissionContext, emitSubmissionTelemetry } from '@fabric-harness/sdk';
|
|
2
|
+
/**
|
|
3
|
+
* Build an `onEvent` fan-in that turns model-usage events into
|
|
4
|
+
* `submission_usage` telemetry. For every {@link FabricEvent} carrying
|
|
5
|
+
* `data.usage` (`{ inputTokens?, outputTokens?, costUsd? }`, at least one
|
|
6
|
+
* numeric field) — and optionally `data.model` — while an ambient
|
|
7
|
+
* {@link currentSubmissionContext} is set, it emits one `submission_usage`
|
|
8
|
+
* event stamped with the submission correlation (submission/attempt ids,
|
|
9
|
+
* agent identity, tenant, actor).
|
|
10
|
+
*
|
|
11
|
+
* Events observed outside a submission context are dropped silently — not
|
|
12
|
+
* every session turn belongs to a submission. The submission context does not
|
|
13
|
+
* carry the submission kind, so emitted events use `kind: 'unknown'`.
|
|
14
|
+
*/
|
|
15
|
+
export function servingUsageCapture(options) {
|
|
16
|
+
return (event) => {
|
|
17
|
+
try {
|
|
18
|
+
const usage = usageOf(event);
|
|
19
|
+
if (!usage)
|
|
20
|
+
return;
|
|
21
|
+
const context = currentSubmissionContext();
|
|
22
|
+
if (!context)
|
|
23
|
+
return;
|
|
24
|
+
const model = typeof event.data?.model === 'string' ? event.data.model : undefined;
|
|
25
|
+
const at = Date.parse(event.timestamp);
|
|
26
|
+
emitSubmissionTelemetry(options.sink, {
|
|
27
|
+
type: 'submission_usage',
|
|
28
|
+
submissionId: context.submissionId,
|
|
29
|
+
attemptId: context.attemptId,
|
|
30
|
+
kind: 'unknown',
|
|
31
|
+
agent: context.agent,
|
|
32
|
+
instanceId: context.instanceId,
|
|
33
|
+
session: context.session,
|
|
34
|
+
at: Number.isFinite(at) ? at : Date.now(),
|
|
35
|
+
...(context.tenantId !== undefined ? { tenantId: context.tenantId } : {}),
|
|
36
|
+
...(context.actor !== undefined ? { actor: context.actor } : {}),
|
|
37
|
+
...(model !== undefined ? { model } : {}),
|
|
38
|
+
...(usage.inputTokens !== undefined ? { inputTokens: usage.inputTokens } : {}),
|
|
39
|
+
...(usage.outputTokens !== undefined ? { outputTokens: usage.outputTokens } : {}),
|
|
40
|
+
...(usage.costUsd !== undefined ? { costUsd: usage.costUsd } : {}),
|
|
41
|
+
}, options.onError);
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
options.onError?.(error);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/** `data.usage` with at least one numeric field, else `undefined`. */
|
|
49
|
+
function usageOf(event) {
|
|
50
|
+
const raw = event.data?.usage;
|
|
51
|
+
if (typeof raw !== 'object' || raw === null || Array.isArray(raw))
|
|
52
|
+
return undefined;
|
|
53
|
+
const usage = raw;
|
|
54
|
+
const fields = {
|
|
55
|
+
...(typeof usage.inputTokens === 'number' ? { inputTokens: usage.inputTokens } : {}),
|
|
56
|
+
...(typeof usage.outputTokens === 'number' ? { outputTokens: usage.outputTokens } : {}),
|
|
57
|
+
...(typeof usage.costUsd === 'number' ? { costUsd: usage.costUsd } : {}),
|
|
58
|
+
};
|
|
59
|
+
return Object.keys(fields).length > 0 ? fields : undefined;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* SQL aggregating a serving endpoint's inference/payload table by
|
|
63
|
+
* `client_request_id` — one row per submission with the request count and the
|
|
64
|
+
* first/last request timestamps — for offline joins against
|
|
65
|
+
* `fh_submission_telemetry.submission_id`.
|
|
66
|
+
*
|
|
67
|
+
* Join contract: callers MUST send the submission id as the serving request's
|
|
68
|
+
* `client_request_id` (e.g. the `client_request_id` field on the
|
|
69
|
+
* serving-endpoint invocation) — the inference table records it verbatim, and
|
|
70
|
+
* this query's `submission_id` column only joins when that convention holds.
|
|
71
|
+
* Rows without a `client_request_id` (traffic from other callers) are
|
|
72
|
+
* excluded. Assumes the AI Gateway payload-table schema (`request_time`
|
|
73
|
+
* timestamp column); adapt the projection for classic inference tables that
|
|
74
|
+
* use `timestamp_ms`.
|
|
75
|
+
*/
|
|
76
|
+
export function inferenceTableUsageQuery(options) {
|
|
77
|
+
if (!/^[A-Za-z0-9_]+\.[A-Za-z0-9_]+\.[A-Za-z0-9_]+$/.test(options.fullTableName)) {
|
|
78
|
+
throw new Error(`databricks serving-usage: fullTableName must be catalog.schema.table, got: ${options.fullTableName}`);
|
|
79
|
+
}
|
|
80
|
+
let since = '';
|
|
81
|
+
if (options.sinceIso !== undefined) {
|
|
82
|
+
// Strict shape check first — Date.parse alone is far too lenient to gate a SQL literal.
|
|
83
|
+
const isoShape = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,3})?(Z|[+-]\d{2}:\d{2})$/;
|
|
84
|
+
const parsed = Date.parse(options.sinceIso);
|
|
85
|
+
if (!isoShape.test(options.sinceIso) || !Number.isFinite(parsed)) {
|
|
86
|
+
throw new Error(`databricks serving-usage: sinceIso is not a valid ISO-8601 instant: ${options.sinceIso}`);
|
|
87
|
+
}
|
|
88
|
+
since = `\n AND request_time >= TIMESTAMP '${new Date(parsed).toISOString()}'`;
|
|
89
|
+
}
|
|
90
|
+
return `SELECT
|
|
91
|
+
client_request_id AS submission_id,
|
|
92
|
+
COUNT(*) AS request_count,
|
|
93
|
+
MIN(request_time) AS first_request_at,
|
|
94
|
+
MAX(request_time) AS last_request_at
|
|
95
|
+
FROM ${options.fullTableName}
|
|
96
|
+
WHERE client_request_id IS NOT NULL${since}
|
|
97
|
+
GROUP BY client_request_id
|
|
98
|
+
ORDER BY last_request_at DESC`;
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=serving-usage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serving-usage.js","sourceRoot":"","sources":["../src/serving-usage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { SubmissionTelemetrySink } from '@fabric-harness/sdk';
|
|
2
|
+
import type { DatabricksLineageRecord } from './governance.js';
|
|
3
|
+
import type { LakebaseClient } from './lakebase.js';
|
|
4
|
+
/**
|
|
5
|
+
* Cost + lineage as core telemetry (v2, workstream C4): submission lifecycle
|
|
6
|
+
* events and governed tool-call lineage land in Lakebase tables keyed by the
|
|
7
|
+
* same `submissionId` that drives events, settlement, and audit — so spend,
|
|
8
|
+
* lineage, and outcomes join on one stable key. Writes are fire-and-forget:
|
|
9
|
+
* a telemetry failure must never affect execution.
|
|
10
|
+
*/
|
|
11
|
+
export interface DatabricksTelemetryOptions {
|
|
12
|
+
client: LakebaseClient;
|
|
13
|
+
/** Create the telemetry tables on first use. Default true. */
|
|
14
|
+
initialize?: boolean;
|
|
15
|
+
/** Failure reporter (default console.error). */
|
|
16
|
+
onError?: (context: string, error: unknown) => void;
|
|
17
|
+
}
|
|
18
|
+
export interface DatabricksTelemetry {
|
|
19
|
+
/** Wire into `createSubmissionRunner({ telemetry })`. */
|
|
20
|
+
sink: SubmissionTelemetrySink;
|
|
21
|
+
/** Wire into `withGovernance({ onLineage })` / the `databricks()` bundle. */
|
|
22
|
+
onLineage: (record: DatabricksLineageRecord) => void;
|
|
23
|
+
/** Idempotent schema setup (also runs lazily on first write). */
|
|
24
|
+
migrate(): Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
/** Create the telemetry tables when absent. Idempotent. */
|
|
27
|
+
export declare function ensureDatabricksTelemetryTables(client: LakebaseClient): Promise<void>;
|
|
28
|
+
export declare function databricksTelemetry(options: DatabricksTelemetryOptions): DatabricksTelemetry;
|
|
29
|
+
//# sourceMappingURL=telemetry-sink.d.ts.map
|