@contractspec/lib.observability 3.0.0 → 3.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/AGENTS.md +34 -0
- package/CHANGELOG.md +37 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +52 -0
- package/dist/node/index.js +52 -0
- package/dist/node/telemetry/model-selection-telemetry.js +30 -0
- package/dist/node/tracing/middleware.js +22 -0
- package/dist/node/tracing/{index.js → model-selection.span.js} +23 -3
- package/dist/telemetry/model-selection-telemetry.d.ts +26 -0
- package/dist/telemetry/model-selection-telemetry.js +31 -0
- package/dist/tracing/index.d.ts +1 -0
- package/dist/tracing/middleware.js +22 -0
- package/dist/tracing/model-selection.span.d.ts +26 -0
- package/dist/tracing/{index.js → model-selection.span.js} +23 -3
- package/package.json +30 -6
package/AGENTS.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# AI Agent Guide — `@contractspec/lib.observability`
|
|
2
|
+
|
|
3
|
+
Scope: `packages/libs/observability/*`
|
|
4
|
+
|
|
5
|
+
OpenTelemetry-based observability primitives. Provides tracing, metrics, logging, and anomaly detection for contract-driven systems.
|
|
6
|
+
|
|
7
|
+
## Quick Context
|
|
8
|
+
|
|
9
|
+
- **Layer**: lib
|
|
10
|
+
- **Consumers**: evolution, progressive-delivery, bundles
|
|
11
|
+
|
|
12
|
+
## Public Exports
|
|
13
|
+
|
|
14
|
+
- `.` — main entry
|
|
15
|
+
- `./anomaly/*` — anomaly detection sub-modules
|
|
16
|
+
- `./intent/*` — intent tracking sub-modules
|
|
17
|
+
- `./logging` — structured logging integration
|
|
18
|
+
- `./metrics` — metric collection helpers
|
|
19
|
+
- `./pipeline/*` — telemetry pipeline interfaces
|
|
20
|
+
- `./telemetry/*` — telemetry primitives
|
|
21
|
+
- `./tracing` — distributed tracing helpers
|
|
22
|
+
|
|
23
|
+
## Guardrails
|
|
24
|
+
|
|
25
|
+
- OTel span and metric naming conventions must stay consistent across the platform
|
|
26
|
+
- Pipeline interfaces are adapter boundaries — do not leak vendor-specific details
|
|
27
|
+
- Anomaly detection thresholds affect alerting; changes require validation
|
|
28
|
+
|
|
29
|
+
## Local Commands
|
|
30
|
+
|
|
31
|
+
- Build: `bun run build`
|
|
32
|
+
- Test: `bun test`
|
|
33
|
+
- Lint: `bun run lint`
|
|
34
|
+
- Dev: `bun run dev`
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,42 @@
|
|
|
1
1
|
# @contractspec/lib.observability
|
|
2
2
|
|
|
3
|
+
## 3.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- a281fc5: fix: missing dependencies
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [a281fc5]
|
|
12
|
+
- @contractspec/lib.contracts-integrations@3.2.0
|
|
13
|
+
- @contractspec/lib.contracts-spec@3.2.0
|
|
14
|
+
- @contractspec/lib.lifecycle@3.2.0
|
|
15
|
+
|
|
16
|
+
## 3.1.1
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- Updated dependencies [02c0cc5]
|
|
21
|
+
- @contractspec/lib.contracts-integrations@3.1.1
|
|
22
|
+
- @contractspec/lib.contracts-spec@3.1.1
|
|
23
|
+
- @contractspec/lib.lifecycle@3.1.1
|
|
24
|
+
|
|
25
|
+
## 3.1.0
|
|
26
|
+
|
|
27
|
+
### Minor Changes
|
|
28
|
+
|
|
29
|
+
- 28987eb: chore: upgrade dependencies
|
|
30
|
+
|
|
31
|
+
### Patch Changes
|
|
32
|
+
|
|
33
|
+
- Updated dependencies [f2a4faf]
|
|
34
|
+
- Updated dependencies [28987eb]
|
|
35
|
+
- Updated dependencies [28987eb]
|
|
36
|
+
- @contractspec/lib.contracts-spec@3.1.0
|
|
37
|
+
- @contractspec/lib.contracts-integrations@3.1.0
|
|
38
|
+
- @contractspec/lib.lifecycle@3.1.0
|
|
39
|
+
|
|
3
40
|
## 3.0.0
|
|
4
41
|
|
|
5
42
|
### Major Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { getTracer, traceAsync, traceSync } from './tracing';
|
|
2
|
+
export { traceModelSelection, type ModelSelectionSpanInput, type ModelSelectionSpanAttributes, } from './tracing/model-selection.span';
|
|
3
|
+
export { ModelSelectionTelemetry, type ModelSelectionEventProperties, } from './telemetry/model-selection-telemetry';
|
|
2
4
|
export { getMeter, createCounter, createUpDownCounter, createHistogram, standardMetrics, } from './metrics';
|
|
3
5
|
export { Logger, logger } from './logging';
|
|
4
6
|
export { createTracingMiddleware, type TracingMiddlewareOptions, } from './tracing/middleware';
|
package/dist/index.js
CHANGED
|
@@ -196,6 +196,56 @@ function traceSync(name, fn, tracerName) {
|
|
|
196
196
|
});
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
+
// src/tracing/model-selection.span.ts
|
|
200
|
+
async function traceModelSelection(fn, input) {
|
|
201
|
+
const startMs = performance.now();
|
|
202
|
+
return traceAsync("model.selection", async (span) => {
|
|
203
|
+
const result = await fn();
|
|
204
|
+
const durationMs = performance.now() - startMs;
|
|
205
|
+
span.setAttribute("model.selected", input.modelId);
|
|
206
|
+
span.setAttribute("model.provider", input.providerKey);
|
|
207
|
+
span.setAttribute("model.score", input.score);
|
|
208
|
+
span.setAttribute("model.alternatives_count", input.alternativesCount);
|
|
209
|
+
span.setAttribute("model.selection_duration_ms", durationMs);
|
|
210
|
+
span.setAttribute("model.reason", input.reason);
|
|
211
|
+
if (input.dimension) {
|
|
212
|
+
span.setAttribute("model.dimension", input.dimension);
|
|
213
|
+
}
|
|
214
|
+
if (input.constraints) {
|
|
215
|
+
span.setAttribute("model.constraints", JSON.stringify(input.constraints));
|
|
216
|
+
}
|
|
217
|
+
return result;
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// src/telemetry/model-selection-telemetry.ts
|
|
222
|
+
class ModelSelectionTelemetry {
|
|
223
|
+
provider;
|
|
224
|
+
eventName;
|
|
225
|
+
constructor(provider, options) {
|
|
226
|
+
this.provider = provider;
|
|
227
|
+
this.eventName = options?.eventName ?? "$model_selection";
|
|
228
|
+
}
|
|
229
|
+
async trackSelection(distinctId, properties) {
|
|
230
|
+
await this.provider.capture({
|
|
231
|
+
distinctId,
|
|
232
|
+
event: this.eventName,
|
|
233
|
+
timestamp: new Date,
|
|
234
|
+
properties: {
|
|
235
|
+
$model_id: properties.modelId,
|
|
236
|
+
$model_provider: properties.providerKey,
|
|
237
|
+
$model_score: properties.score,
|
|
238
|
+
$model_dimension: properties.dimension ?? null,
|
|
239
|
+
$model_reason: properties.reason,
|
|
240
|
+
$model_alternatives_count: properties.alternativesCount,
|
|
241
|
+
$model_cost_estimate_input: properties.costEstimateInput ?? null,
|
|
242
|
+
$model_cost_estimate_output: properties.costEstimateOutput ?? null,
|
|
243
|
+
$model_selection_duration_ms: properties.selectionDurationMs ?? null
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
199
249
|
// src/metrics/index.ts
|
|
200
250
|
import {
|
|
201
251
|
metrics
|
|
@@ -1055,6 +1105,7 @@ function isRecord(value) {
|
|
|
1055
1105
|
}
|
|
1056
1106
|
export {
|
|
1057
1107
|
traceSync,
|
|
1108
|
+
traceModelSelection,
|
|
1058
1109
|
traceAsync,
|
|
1059
1110
|
standardMetrics,
|
|
1060
1111
|
logger,
|
|
@@ -1067,6 +1118,7 @@ export {
|
|
|
1067
1118
|
RootCauseAnalyzer,
|
|
1068
1119
|
PosthogTelemetryProvider,
|
|
1069
1120
|
PosthogBaselineReader,
|
|
1121
|
+
ModelSelectionTelemetry,
|
|
1070
1122
|
Logger,
|
|
1071
1123
|
LifecycleKpiPipeline,
|
|
1072
1124
|
IntentDetector,
|
package/dist/node/index.js
CHANGED
|
@@ -195,6 +195,56 @@ function traceSync(name, fn, tracerName) {
|
|
|
195
195
|
});
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
+
// src/tracing/model-selection.span.ts
|
|
199
|
+
async function traceModelSelection(fn, input) {
|
|
200
|
+
const startMs = performance.now();
|
|
201
|
+
return traceAsync("model.selection", async (span) => {
|
|
202
|
+
const result = await fn();
|
|
203
|
+
const durationMs = performance.now() - startMs;
|
|
204
|
+
span.setAttribute("model.selected", input.modelId);
|
|
205
|
+
span.setAttribute("model.provider", input.providerKey);
|
|
206
|
+
span.setAttribute("model.score", input.score);
|
|
207
|
+
span.setAttribute("model.alternatives_count", input.alternativesCount);
|
|
208
|
+
span.setAttribute("model.selection_duration_ms", durationMs);
|
|
209
|
+
span.setAttribute("model.reason", input.reason);
|
|
210
|
+
if (input.dimension) {
|
|
211
|
+
span.setAttribute("model.dimension", input.dimension);
|
|
212
|
+
}
|
|
213
|
+
if (input.constraints) {
|
|
214
|
+
span.setAttribute("model.constraints", JSON.stringify(input.constraints));
|
|
215
|
+
}
|
|
216
|
+
return result;
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// src/telemetry/model-selection-telemetry.ts
|
|
221
|
+
class ModelSelectionTelemetry {
|
|
222
|
+
provider;
|
|
223
|
+
eventName;
|
|
224
|
+
constructor(provider, options) {
|
|
225
|
+
this.provider = provider;
|
|
226
|
+
this.eventName = options?.eventName ?? "$model_selection";
|
|
227
|
+
}
|
|
228
|
+
async trackSelection(distinctId, properties) {
|
|
229
|
+
await this.provider.capture({
|
|
230
|
+
distinctId,
|
|
231
|
+
event: this.eventName,
|
|
232
|
+
timestamp: new Date,
|
|
233
|
+
properties: {
|
|
234
|
+
$model_id: properties.modelId,
|
|
235
|
+
$model_provider: properties.providerKey,
|
|
236
|
+
$model_score: properties.score,
|
|
237
|
+
$model_dimension: properties.dimension ?? null,
|
|
238
|
+
$model_reason: properties.reason,
|
|
239
|
+
$model_alternatives_count: properties.alternativesCount,
|
|
240
|
+
$model_cost_estimate_input: properties.costEstimateInput ?? null,
|
|
241
|
+
$model_cost_estimate_output: properties.costEstimateOutput ?? null,
|
|
242
|
+
$model_selection_duration_ms: properties.selectionDurationMs ?? null
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
198
248
|
// src/metrics/index.ts
|
|
199
249
|
import {
|
|
200
250
|
metrics
|
|
@@ -1054,6 +1104,7 @@ function isRecord(value) {
|
|
|
1054
1104
|
}
|
|
1055
1105
|
export {
|
|
1056
1106
|
traceSync,
|
|
1107
|
+
traceModelSelection,
|
|
1057
1108
|
traceAsync,
|
|
1058
1109
|
standardMetrics,
|
|
1059
1110
|
logger,
|
|
@@ -1066,6 +1117,7 @@ export {
|
|
|
1066
1117
|
RootCauseAnalyzer,
|
|
1067
1118
|
PosthogTelemetryProvider,
|
|
1068
1119
|
PosthogBaselineReader,
|
|
1120
|
+
ModelSelectionTelemetry,
|
|
1069
1121
|
Logger,
|
|
1070
1122
|
LifecycleKpiPipeline,
|
|
1071
1123
|
IntentDetector,
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// src/telemetry/model-selection-telemetry.ts
|
|
2
|
+
class ModelSelectionTelemetry {
|
|
3
|
+
provider;
|
|
4
|
+
eventName;
|
|
5
|
+
constructor(provider, options) {
|
|
6
|
+
this.provider = provider;
|
|
7
|
+
this.eventName = options?.eventName ?? "$model_selection";
|
|
8
|
+
}
|
|
9
|
+
async trackSelection(distinctId, properties) {
|
|
10
|
+
await this.provider.capture({
|
|
11
|
+
distinctId,
|
|
12
|
+
event: this.eventName,
|
|
13
|
+
timestamp: new Date,
|
|
14
|
+
properties: {
|
|
15
|
+
$model_id: properties.modelId,
|
|
16
|
+
$model_provider: properties.providerKey,
|
|
17
|
+
$model_score: properties.score,
|
|
18
|
+
$model_dimension: properties.dimension ?? null,
|
|
19
|
+
$model_reason: properties.reason,
|
|
20
|
+
$model_alternatives_count: properties.alternativesCount,
|
|
21
|
+
$model_cost_estimate_input: properties.costEstimateInput ?? null,
|
|
22
|
+
$model_cost_estimate_output: properties.costEstimateOutput ?? null,
|
|
23
|
+
$model_selection_duration_ms: properties.selectionDurationMs ?? null
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export {
|
|
29
|
+
ModelSelectionTelemetry
|
|
30
|
+
};
|
|
@@ -46,6 +46,28 @@ function traceSync(name, fn, tracerName) {
|
|
|
46
46
|
});
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
// src/tracing/model-selection.span.ts
|
|
50
|
+
async function traceModelSelection(fn, input) {
|
|
51
|
+
const startMs = performance.now();
|
|
52
|
+
return traceAsync("model.selection", async (span) => {
|
|
53
|
+
const result = await fn();
|
|
54
|
+
const durationMs = performance.now() - startMs;
|
|
55
|
+
span.setAttribute("model.selected", input.modelId);
|
|
56
|
+
span.setAttribute("model.provider", input.providerKey);
|
|
57
|
+
span.setAttribute("model.score", input.score);
|
|
58
|
+
span.setAttribute("model.alternatives_count", input.alternativesCount);
|
|
59
|
+
span.setAttribute("model.selection_duration_ms", durationMs);
|
|
60
|
+
span.setAttribute("model.reason", input.reason);
|
|
61
|
+
if (input.dimension) {
|
|
62
|
+
span.setAttribute("model.dimension", input.dimension);
|
|
63
|
+
}
|
|
64
|
+
if (input.constraints) {
|
|
65
|
+
span.setAttribute("model.constraints", JSON.stringify(input.constraints));
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
49
71
|
// src/metrics/index.ts
|
|
50
72
|
import {
|
|
51
73
|
metrics
|
|
@@ -45,8 +45,28 @@ function traceSync(name, fn, tracerName) {
|
|
|
45
45
|
}
|
|
46
46
|
});
|
|
47
47
|
}
|
|
48
|
+
|
|
49
|
+
// src/tracing/model-selection.span.ts
|
|
50
|
+
async function traceModelSelection(fn, input) {
|
|
51
|
+
const startMs = performance.now();
|
|
52
|
+
return traceAsync("model.selection", async (span) => {
|
|
53
|
+
const result = await fn();
|
|
54
|
+
const durationMs = performance.now() - startMs;
|
|
55
|
+
span.setAttribute("model.selected", input.modelId);
|
|
56
|
+
span.setAttribute("model.provider", input.providerKey);
|
|
57
|
+
span.setAttribute("model.score", input.score);
|
|
58
|
+
span.setAttribute("model.alternatives_count", input.alternativesCount);
|
|
59
|
+
span.setAttribute("model.selection_duration_ms", durationMs);
|
|
60
|
+
span.setAttribute("model.reason", input.reason);
|
|
61
|
+
if (input.dimension) {
|
|
62
|
+
span.setAttribute("model.dimension", input.dimension);
|
|
63
|
+
}
|
|
64
|
+
if (input.constraints) {
|
|
65
|
+
span.setAttribute("model.constraints", JSON.stringify(input.constraints));
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
68
|
+
});
|
|
69
|
+
}
|
|
48
70
|
export {
|
|
49
|
-
|
|
50
|
-
traceAsync,
|
|
51
|
-
getTracer
|
|
71
|
+
traceModelSelection
|
|
52
72
|
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { AnalyticsProvider } from '@contractspec/lib.contracts-integrations';
|
|
2
|
+
export interface ModelSelectionEventProperties {
|
|
3
|
+
modelId: string;
|
|
4
|
+
providerKey: string;
|
|
5
|
+
score: number;
|
|
6
|
+
dimension?: string;
|
|
7
|
+
reason: string;
|
|
8
|
+
alternativesCount: number;
|
|
9
|
+
costEstimateInput?: number;
|
|
10
|
+
costEstimateOutput?: number;
|
|
11
|
+
selectionDurationMs?: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Track model selection decisions via PostHog analytics.
|
|
15
|
+
*
|
|
16
|
+
* Captures a `$model_selection` event with the selection result properties,
|
|
17
|
+
* enabling analytics dashboards for model usage patterns and cost tracking.
|
|
18
|
+
*/
|
|
19
|
+
export declare class ModelSelectionTelemetry {
|
|
20
|
+
private readonly provider;
|
|
21
|
+
private readonly eventName;
|
|
22
|
+
constructor(provider: AnalyticsProvider, options?: {
|
|
23
|
+
eventName?: string;
|
|
24
|
+
});
|
|
25
|
+
trackSelection(distinctId: string, properties: ModelSelectionEventProperties): Promise<void>;
|
|
26
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/telemetry/model-selection-telemetry.ts
|
|
3
|
+
class ModelSelectionTelemetry {
|
|
4
|
+
provider;
|
|
5
|
+
eventName;
|
|
6
|
+
constructor(provider, options) {
|
|
7
|
+
this.provider = provider;
|
|
8
|
+
this.eventName = options?.eventName ?? "$model_selection";
|
|
9
|
+
}
|
|
10
|
+
async trackSelection(distinctId, properties) {
|
|
11
|
+
await this.provider.capture({
|
|
12
|
+
distinctId,
|
|
13
|
+
event: this.eventName,
|
|
14
|
+
timestamp: new Date,
|
|
15
|
+
properties: {
|
|
16
|
+
$model_id: properties.modelId,
|
|
17
|
+
$model_provider: properties.providerKey,
|
|
18
|
+
$model_score: properties.score,
|
|
19
|
+
$model_dimension: properties.dimension ?? null,
|
|
20
|
+
$model_reason: properties.reason,
|
|
21
|
+
$model_alternatives_count: properties.alternativesCount,
|
|
22
|
+
$model_cost_estimate_input: properties.costEstimateInput ?? null,
|
|
23
|
+
$model_cost_estimate_output: properties.costEstimateOutput ?? null,
|
|
24
|
+
$model_selection_duration_ms: properties.selectionDurationMs ?? null
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export {
|
|
30
|
+
ModelSelectionTelemetry
|
|
31
|
+
};
|
package/dist/tracing/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type Span, type Tracer } from '@opentelemetry/api';
|
|
2
|
+
export * from './model-selection.span';
|
|
2
3
|
export declare function getTracer(name?: string): Tracer;
|
|
3
4
|
export declare function traceAsync<T>(name: string, fn: (span: Span) => Promise<T>, tracerName?: string): Promise<T>;
|
|
4
5
|
export declare function traceSync<T>(name: string, fn: (span: Span) => T, tracerName?: string): T;
|
|
@@ -47,6 +47,28 @@ function traceSync(name, fn, tracerName) {
|
|
|
47
47
|
});
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
// src/tracing/model-selection.span.ts
|
|
51
|
+
async function traceModelSelection(fn, input) {
|
|
52
|
+
const startMs = performance.now();
|
|
53
|
+
return traceAsync("model.selection", async (span) => {
|
|
54
|
+
const result = await fn();
|
|
55
|
+
const durationMs = performance.now() - startMs;
|
|
56
|
+
span.setAttribute("model.selected", input.modelId);
|
|
57
|
+
span.setAttribute("model.provider", input.providerKey);
|
|
58
|
+
span.setAttribute("model.score", input.score);
|
|
59
|
+
span.setAttribute("model.alternatives_count", input.alternativesCount);
|
|
60
|
+
span.setAttribute("model.selection_duration_ms", durationMs);
|
|
61
|
+
span.setAttribute("model.reason", input.reason);
|
|
62
|
+
if (input.dimension) {
|
|
63
|
+
span.setAttribute("model.dimension", input.dimension);
|
|
64
|
+
}
|
|
65
|
+
if (input.constraints) {
|
|
66
|
+
span.setAttribute("model.constraints", JSON.stringify(input.constraints));
|
|
67
|
+
}
|
|
68
|
+
return result;
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
50
72
|
// src/metrics/index.ts
|
|
51
73
|
import {
|
|
52
74
|
metrics
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface ModelSelectionSpanAttributes {
|
|
2
|
+
'model.selected': string;
|
|
3
|
+
'model.provider': string;
|
|
4
|
+
'model.score': number;
|
|
5
|
+
'model.dimension'?: string;
|
|
6
|
+
'model.alternatives_count': number;
|
|
7
|
+
'model.constraints'?: string;
|
|
8
|
+
'model.selection_duration_ms': number;
|
|
9
|
+
'model.reason': string;
|
|
10
|
+
}
|
|
11
|
+
export interface ModelSelectionSpanInput {
|
|
12
|
+
modelId: string;
|
|
13
|
+
providerKey: string;
|
|
14
|
+
score: number;
|
|
15
|
+
dimension?: string;
|
|
16
|
+
alternativesCount: number;
|
|
17
|
+
constraints?: Record<string, unknown>;
|
|
18
|
+
reason: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Trace a model selection decision as an OpenTelemetry span.
|
|
22
|
+
*
|
|
23
|
+
* Wraps an async operation (typically the selector call) and records
|
|
24
|
+
* the selection result as span attributes for distributed tracing.
|
|
25
|
+
*/
|
|
26
|
+
export declare function traceModelSelection<T>(fn: () => Promise<T>, input: ModelSelectionSpanInput): Promise<T>;
|
|
@@ -46,8 +46,28 @@ function traceSync(name, fn, tracerName) {
|
|
|
46
46
|
}
|
|
47
47
|
});
|
|
48
48
|
}
|
|
49
|
+
|
|
50
|
+
// src/tracing/model-selection.span.ts
|
|
51
|
+
async function traceModelSelection(fn, input) {
|
|
52
|
+
const startMs = performance.now();
|
|
53
|
+
return traceAsync("model.selection", async (span) => {
|
|
54
|
+
const result = await fn();
|
|
55
|
+
const durationMs = performance.now() - startMs;
|
|
56
|
+
span.setAttribute("model.selected", input.modelId);
|
|
57
|
+
span.setAttribute("model.provider", input.providerKey);
|
|
58
|
+
span.setAttribute("model.score", input.score);
|
|
59
|
+
span.setAttribute("model.alternatives_count", input.alternativesCount);
|
|
60
|
+
span.setAttribute("model.selection_duration_ms", durationMs);
|
|
61
|
+
span.setAttribute("model.reason", input.reason);
|
|
62
|
+
if (input.dimension) {
|
|
63
|
+
span.setAttribute("model.dimension", input.dimension);
|
|
64
|
+
}
|
|
65
|
+
if (input.constraints) {
|
|
66
|
+
span.setAttribute("model.constraints", JSON.stringify(input.constraints));
|
|
67
|
+
}
|
|
68
|
+
return result;
|
|
69
|
+
});
|
|
70
|
+
}
|
|
49
71
|
export {
|
|
50
|
-
|
|
51
|
-
traceAsync,
|
|
52
|
-
getTracer
|
|
72
|
+
traceModelSelection
|
|
53
73
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contractspec/lib.observability",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"description": "OpenTelemetry-based observability primitives",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"contractspec",
|
|
@@ -27,17 +27,17 @@
|
|
|
27
27
|
"typecheck": "tsc --noEmit"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@contractspec/lib.lifecycle": "3.
|
|
31
|
-
"@contractspec/lib.contracts-spec": "3.
|
|
32
|
-
"@contractspec/lib.contracts-integrations": "3.
|
|
30
|
+
"@contractspec/lib.lifecycle": "3.2.0",
|
|
31
|
+
"@contractspec/lib.contracts-spec": "3.2.0",
|
|
32
|
+
"@contractspec/lib.contracts-integrations": "3.2.0"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
35
35
|
"@opentelemetry/api": "*"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@contractspec/tool.typescript": "3.
|
|
38
|
+
"@contractspec/tool.typescript": "3.2.0",
|
|
39
39
|
"typescript": "^5.9.3",
|
|
40
|
-
"@contractspec/tool.bun": "3.
|
|
40
|
+
"@contractspec/tool.bun": "3.2.0"
|
|
41
41
|
},
|
|
42
42
|
"exports": {
|
|
43
43
|
".": {
|
|
@@ -118,6 +118,12 @@
|
|
|
118
118
|
"node": "./dist/node/pipeline/lifecycle-pipeline.js",
|
|
119
119
|
"default": "./dist/pipeline/lifecycle-pipeline.js"
|
|
120
120
|
},
|
|
121
|
+
"./telemetry/model-selection-telemetry": {
|
|
122
|
+
"types": "./dist/telemetry/model-selection-telemetry.d.ts",
|
|
123
|
+
"bun": "./dist/telemetry/model-selection-telemetry.js",
|
|
124
|
+
"node": "./dist/node/telemetry/model-selection-telemetry.js",
|
|
125
|
+
"default": "./dist/telemetry/model-selection-telemetry.js"
|
|
126
|
+
},
|
|
121
127
|
"./telemetry/posthog-baseline-reader": {
|
|
122
128
|
"types": "./dist/telemetry/posthog-baseline-reader.d.ts",
|
|
123
129
|
"bun": "./dist/telemetry/posthog-baseline-reader.js",
|
|
@@ -147,6 +153,12 @@
|
|
|
147
153
|
"bun": "./dist/tracing/middleware.js",
|
|
148
154
|
"node": "./dist/node/tracing/middleware.js",
|
|
149
155
|
"default": "./dist/tracing/middleware.js"
|
|
156
|
+
},
|
|
157
|
+
"./tracing/model-selection.span": {
|
|
158
|
+
"types": "./dist/tracing/model-selection.span.d.ts",
|
|
159
|
+
"bun": "./dist/tracing/model-selection.span.js",
|
|
160
|
+
"node": "./dist/node/tracing/model-selection.span.js",
|
|
161
|
+
"default": "./dist/tracing/model-selection.span.js"
|
|
150
162
|
}
|
|
151
163
|
},
|
|
152
164
|
"publishConfig": {
|
|
@@ -230,6 +242,12 @@
|
|
|
230
242
|
"node": "./dist/node/pipeline/lifecycle-pipeline.js",
|
|
231
243
|
"default": "./dist/pipeline/lifecycle-pipeline.js"
|
|
232
244
|
},
|
|
245
|
+
"./telemetry/model-selection-telemetry": {
|
|
246
|
+
"types": "./dist/telemetry/model-selection-telemetry.d.ts",
|
|
247
|
+
"bun": "./dist/telemetry/model-selection-telemetry.js",
|
|
248
|
+
"node": "./dist/node/telemetry/model-selection-telemetry.js",
|
|
249
|
+
"default": "./dist/telemetry/model-selection-telemetry.js"
|
|
250
|
+
},
|
|
233
251
|
"./telemetry/posthog-baseline-reader": {
|
|
234
252
|
"types": "./dist/telemetry/posthog-baseline-reader.d.ts",
|
|
235
253
|
"bun": "./dist/telemetry/posthog-baseline-reader.js",
|
|
@@ -259,6 +277,12 @@
|
|
|
259
277
|
"bun": "./dist/tracing/middleware.js",
|
|
260
278
|
"node": "./dist/node/tracing/middleware.js",
|
|
261
279
|
"default": "./dist/tracing/middleware.js"
|
|
280
|
+
},
|
|
281
|
+
"./tracing/model-selection.span": {
|
|
282
|
+
"types": "./dist/tracing/model-selection.span.d.ts",
|
|
283
|
+
"bun": "./dist/tracing/model-selection.span.js",
|
|
284
|
+
"node": "./dist/node/tracing/model-selection.span.js",
|
|
285
|
+
"default": "./dist/tracing/model-selection.span.js"
|
|
262
286
|
}
|
|
263
287
|
},
|
|
264
288
|
"registry": "https://registry.npmjs.org/"
|