@mlx-node/agent 0.0.8 → 0.0.10
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/catalog.d.ts +15 -0
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +15 -0
- package/dist/cold-tier.d.ts +99 -0
- package/dist/cold-tier.d.ts.map +1 -0
- package/dist/cold-tier.js +155 -0
- package/dist/extensions/local-image-input.d.ts +24 -0
- package/dist/extensions/local-image-input.d.ts.map +1 -0
- package/dist/extensions/local-image-input.js +114 -0
- package/dist/extensions/subagent.d.ts +20 -1
- package/dist/extensions/subagent.d.ts.map +1 -1
- package/dist/extensions/subagent.js +46 -6
- package/dist/paths.d.ts +13 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +18 -0
- package/dist/provider/chat-config.d.ts +1 -1
- package/dist/provider/chat-config.d.ts.map +1 -1
- package/dist/provider/chat-config.js +15 -3
- package/dist/provider/events.d.ts +9 -0
- package/dist/provider/events.d.ts.map +1 -1
- package/dist/provider/events.js +15 -0
- package/dist/provider/index.d.ts +12 -1
- package/dist/provider/index.d.ts.map +1 -1
- package/dist/provider/index.js +153 -7
- package/dist/provider/metrics-trace.d.ts +274 -0
- package/dist/provider/metrics-trace.d.ts.map +1 -0
- package/dist/provider/metrics-trace.js +174 -0
- package/dist/provider/mlx-identity.d.ts +16 -0
- package/dist/provider/mlx-identity.d.ts.map +1 -0
- package/dist/provider/mlx-identity.js +15 -0
- package/dist/provider/model-host.d.ts +45 -3
- package/dist/provider/model-host.d.ts.map +1 -1
- package/dist/provider/model-host.js +34 -2
- package/dist/provider/model-registry-filter.d.ts +74 -18
- package/dist/provider/model-registry-filter.d.ts.map +1 -1
- package/dist/provider/model-registry-filter.js +229 -38
- package/dist/provider/models.d.ts +2 -3
- package/dist/provider/models.d.ts.map +1 -1
- package/dist/provider/models.js +46 -20
- package/dist/provider/stream-adapter.d.ts +37 -4
- package/dist/provider/stream-adapter.d.ts.map +1 -1
- package/dist/provider/stream-adapter.js +82 -7
- package/dist/provider/warm-reuse.d.ts +11 -8
- package/dist/provider/warm-reuse.d.ts.map +1 -1
- package/dist/provider/warm-reuse.js +14 -7
- package/dist/run-agent.d.ts +12 -3
- package/dist/run-agent.d.ts.map +1 -1
- package/dist/run-agent.js +31 -5
- package/package.json +10 -5
|
@@ -54,6 +54,9 @@ const THINKING_LEVEL_TO_EFFORT = {
|
|
|
54
54
|
xhigh: 'high',
|
|
55
55
|
max: 'high',
|
|
56
56
|
};
|
|
57
|
+
function isPositiveSafeInteger(value) {
|
|
58
|
+
return typeof value === 'number' && Number.isSafeInteger(value) && value > 0;
|
|
59
|
+
}
|
|
57
60
|
/**
|
|
58
61
|
* Resolve Pi's thinking level once for both native config and persisted replay
|
|
59
62
|
* provenance. Keeping these values together prevents a low/minimal turn from
|
|
@@ -67,7 +70,7 @@ export function resolveReasoningMode(reasoning) {
|
|
|
67
70
|
thinkingEnabled: reasoningEffort === 'medium' || reasoningEffort === 'high',
|
|
68
71
|
};
|
|
69
72
|
}
|
|
70
|
-
export function buildChatConfig(modelType, options, tools, rootCacheOwnerId, resolvedReasoning = resolveReasoningMode(options?.reasoning)) {
|
|
73
|
+
export function buildChatConfig(modelType, options, tools, rootCacheOwnerId, resolvedReasoning = resolveReasoningMode(options?.reasoning), modelMaxTokens) {
|
|
71
74
|
const preset = launchPresetFor(modelType);
|
|
72
75
|
if (!preset) {
|
|
73
76
|
const known = [...new Set([...Object.keys(LAUNCH_PRESETS), ...Object.keys(AGENT_LAUNCH_PRESETS)])].join(', ');
|
|
@@ -92,8 +95,17 @@ export function buildChatConfig(modelType, options, tools, rootCacheOwnerId, res
|
|
|
92
95
|
// which branch the bounded GDN sidecar store protects from child eviction.
|
|
93
96
|
if (rootCacheOwnerId !== undefined)
|
|
94
97
|
config.cacheRootOwnerId = rootCacheOwnerId;
|
|
95
|
-
|
|
96
|
-
|
|
98
|
+
const explicitMaxTokens = options?.maxTokens;
|
|
99
|
+
if (isPositiveSafeInteger(explicitMaxTokens)) {
|
|
100
|
+
// A valid per-call provider option is the topmost layer.
|
|
101
|
+
config.maxNewTokens = explicitMaxTokens;
|
|
102
|
+
}
|
|
103
|
+
else if (isPositiveSafeInteger(modelMaxTokens)) {
|
|
104
|
+
// Normal Pi agent turns omit SimpleStreamOptions.maxTokens. Honor the
|
|
105
|
+
// composed Model metadata (including models.json modelOverrides) without
|
|
106
|
+
// allowing malformed/hostile metadata to replace the family preset.
|
|
107
|
+
config.maxNewTokens = modelMaxTokens;
|
|
108
|
+
}
|
|
97
109
|
if (options?.temperature !== undefined)
|
|
98
110
|
config.temperature = options.temperature;
|
|
99
111
|
if (tools && tools.length > 0)
|
|
@@ -21,6 +21,15 @@ import type { ChatStreamDelta, ChatStreamFinal, PerformanceMetrics } from '@mlx-
|
|
|
21
21
|
export declare function emptyUsage(): Usage;
|
|
22
22
|
export declare class TurnEmitter {
|
|
23
23
|
private readonly onPerformance?;
|
|
24
|
+
/**
|
|
25
|
+
* Correlation id for this turn. Minted here, stamped on the partial message
|
|
26
|
+
* as the custom `mlxTraceId` field (survives pi's JSONL round-trip like
|
|
27
|
+
* `mlxThinkingEnabled`), and surfaced so the stream adapter can join a
|
|
28
|
+
* `MetricsTrace` record to the persisted pi message. The pi entry id is not
|
|
29
|
+
* knowable in-turn (pi emits `message_end` before persisting), so this
|
|
30
|
+
* minted id is the durable join key.
|
|
31
|
+
*/
|
|
32
|
+
readonly traceId: string;
|
|
24
33
|
private readonly stream;
|
|
25
34
|
private readonly partial;
|
|
26
35
|
private readonly textBuffer;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/provider/events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/provider/events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,KAAK,EACV,GAAG,EACH,gBAAgB,EAChB,2BAA2B,EAC3B,KAAK,EAIL,KAAK,EACN,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,kBAAkB,EAAkB,MAAM,cAAc,CAAC;AAMzG;;;GAGG;AACH,wBAAgB,UAAU,IAAI,KAAK,CASlC;AA2CD,qBAAa,WAAW;IA2BpB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC;IA1BjC;;;;;;;OAOG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA8B;IACrD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmB;IAC3C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA2B;IACtD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA4B;IAC3D;;;;;OAKG;IACH,OAAO,CAAC,wBAAwB,CAAM;IACtC,OAAO,CAAC,SAAS,CAA8C;IAC/D,OAAO,CAAC,QAAQ,CAAS;IAEzB,YACE,MAAM,EAAE,2BAA2B,EACnC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EACA,aAAa,CAAC,GAAE,CAAC,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,kBAAkB,KAAK,IAAI,aAAA,EACrG,eAAe,CAAC,EAAE,OAAO,EA4B1B;IAED,OAAO,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAqBpC;IAED,OAAO,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CA8CpC;IAED;;;;;OAKG;IACH,SAAS,IAAI,IAAI,CAGhB;IAED;;;;OAIG;IACH,OAAO,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAG1B;IAED;;;;;;;OAOG;IACH,OAAO,CAAC,eAAe;IAqBvB,OAAO,CAAC,cAAc;IAmBtB,OAAO,CAAC,iBAAiB;IA6BzB,OAAO,CAAC,cAAc;IAqBtB,OAAO,CAAC,UAAU;CAGnB"}
|
package/dist/provider/events.js
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* - Every method is throw-safe: the pi StreamFn contract does not allow
|
|
13
13
|
* the emitter to throw, so internal failures route to {@link onError}.
|
|
14
14
|
*/
|
|
15
|
+
import { randomUUID } from 'node:crypto';
|
|
15
16
|
import { coerceErrorMessage } from './error-coercion.js';
|
|
16
17
|
import { ReasoningTagBuffer } from './reasoning-tag-buffer.js';
|
|
17
18
|
import { ToolCallTagBuffer } from './tool-call-buffer.js';
|
|
@@ -69,6 +70,15 @@ function toPiToolCall(call) {
|
|
|
69
70
|
}
|
|
70
71
|
export class TurnEmitter {
|
|
71
72
|
onPerformance;
|
|
73
|
+
/**
|
|
74
|
+
* Correlation id for this turn. Minted here, stamped on the partial message
|
|
75
|
+
* as the custom `mlxTraceId` field (survives pi's JSONL round-trip like
|
|
76
|
+
* `mlxThinkingEnabled`), and surfaced so the stream adapter can join a
|
|
77
|
+
* `MetricsTrace` record to the persisted pi message. The pi entry id is not
|
|
78
|
+
* knowable in-turn (pi emits `message_end` before persisting), so this
|
|
79
|
+
* minted id is the durable join key.
|
|
80
|
+
*/
|
|
81
|
+
traceId;
|
|
72
82
|
stream;
|
|
73
83
|
partial;
|
|
74
84
|
textBuffer = new ToolCallTagBuffer();
|
|
@@ -85,6 +95,8 @@ export class TurnEmitter {
|
|
|
85
95
|
constructor(stream, model, onPerformance, thinkingEnabled) {
|
|
86
96
|
this.onPerformance = onPerformance;
|
|
87
97
|
this.stream = stream;
|
|
98
|
+
const traceId = randomUUID();
|
|
99
|
+
this.traceId = traceId;
|
|
88
100
|
this.partial = {
|
|
89
101
|
role: 'assistant',
|
|
90
102
|
content: [],
|
|
@@ -104,6 +116,9 @@ export class TurnEmitter {
|
|
|
104
116
|
if (thinkingEnabled !== undefined) {
|
|
105
117
|
this.partial.mlxThinkingEnabled = thinkingEnabled;
|
|
106
118
|
}
|
|
119
|
+
// Same custom-field mechanism: stamp the trace id so a dashboard can join
|
|
120
|
+
// the durable MetricsTrace record back to this persisted pi message.
|
|
121
|
+
this.partial.mlxTraceId = traceId;
|
|
107
122
|
stream.push({ type: 'start', partial: this.partial });
|
|
108
123
|
}
|
|
109
124
|
onDelta(delta) {
|
package/dist/provider/index.d.ts
CHANGED
|
@@ -15,8 +15,19 @@
|
|
|
15
15
|
* `ExtensionAPI` value arrives as the factory argument.
|
|
16
16
|
*/
|
|
17
17
|
import type { InlineExtension } from '@earendil-works/pi-coding-agent';
|
|
18
|
+
import { type ColdCacheStats, type ColdSidecarStats } from '@mlx-node/core';
|
|
19
|
+
import { MetricsTrace } from './metrics-trace.js';
|
|
18
20
|
import { MlxModelHost } from './model-host.js';
|
|
19
21
|
import type { MlxModelInfo } from './models.js';
|
|
22
|
+
/** Injectable seams for {@link createMlxProviderExtension} (unit tests). */
|
|
23
|
+
export interface MlxProviderExtensionDeps {
|
|
24
|
+
/** Process-wide cold-tier reader; defaults to the native addon (absent in unit tests). */
|
|
25
|
+
coldStats?: () => ColdCacheStats | undefined;
|
|
26
|
+
/** Process-wide sidecar-counter reader; defaults to the native addon. */
|
|
27
|
+
sidecarStats?: () => ColdSidecarStats | undefined;
|
|
28
|
+
/** Durable per-turn telemetry sink; defaults to a fresh {@link MetricsTrace}. */
|
|
29
|
+
metricsTrace?: MetricsTrace;
|
|
30
|
+
}
|
|
20
31
|
/**
|
|
21
32
|
* Build the `mlx-provider` inline extension serving `models`. The host
|
|
22
33
|
* (one per process — it owns the single GPU-resident model) is created
|
|
@@ -24,5 +35,5 @@ import type { MlxModelInfo } from './models.js';
|
|
|
24
35
|
* host, but stays lazy about weights: nothing loads until the first
|
|
25
36
|
* `streamSimple` call.
|
|
26
37
|
*/
|
|
27
|
-
export declare function createMlxProviderExtension(models: MlxModelInfo[], host?: MlxModelHost): InlineExtension;
|
|
38
|
+
export declare function createMlxProviderExtension(models: MlxModelInfo[], host?: MlxModelHost, deps?: MlxProviderExtensionDeps): InlineExtension;
|
|
28
39
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/provider/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAgB,eAAe,EAAE,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/provider/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAgB,eAAe,EAAE,MAAM,iCAAiC,CAAC;AACrF,OAAO,EAAoC,KAAK,cAAc,EAAE,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAG9G,OAAO,EAAE,YAAY,EAA2B,MAAM,oBAAoB,CAAC;AAE3E,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AA4BhD,4EAA4E;AAC5E,MAAM,WAAW,wBAAwB;IACvC,0FAA0F;IAC1F,SAAS,CAAC,EAAE,MAAM,cAAc,GAAG,SAAS,CAAC;IAC7C,yEAAyE;IACzE,YAAY,CAAC,EAAE,MAAM,gBAAgB,GAAG,SAAS,CAAC;IAClD,iFAAiF;IACjF,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,YAAY,EAAE,EACtB,IAAI,CAAC,EAAE,YAAY,EACnB,IAAI,GAAE,wBAA6B,GAClC,eAAe,CA6KjB"}
|
package/dist/provider/index.js
CHANGED
|
@@ -14,9 +14,37 @@
|
|
|
14
14
|
* at module top level. Only type-only pi imports appear here; the
|
|
15
15
|
* `ExtensionAPI` value arrives as the factory argument.
|
|
16
16
|
*/
|
|
17
|
+
import { coldCacheStats, coldSidecarStats } from '@mlx-node/core';
|
|
18
|
+
import { canonicalCacheRoot } from '../cold-tier.js';
|
|
19
|
+
import { MetricsTrace } from './metrics-trace.js';
|
|
20
|
+
import { MLX_API, MLX_API_KEY, MLX_BASE_URL, MLX_PROVIDER_ID } from './mlx-identity.js';
|
|
17
21
|
import { MlxModelHost } from './model-host.js';
|
|
18
22
|
import { PerformanceStatus } from './performance-status.js';
|
|
19
23
|
import { makeMlxStreamSimple } from './stream-adapter.js';
|
|
24
|
+
/** Read the process-wide cold-tier snapshot; the native addon may be absent (unit tests). */
|
|
25
|
+
function safeColdStats() {
|
|
26
|
+
try {
|
|
27
|
+
return coldCacheStats();
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Read the process-wide sidecar counters. Separate from {@link safeColdStats}
|
|
35
|
+
* because it reads a different native struct that has no `enabled` gate: the
|
|
36
|
+
* counters live in a plain static, not in the tier, so they are valid even
|
|
37
|
+
* when the tier never opened — which is exactly the case a run with zero
|
|
38
|
+
* sidecars needs distinguished.
|
|
39
|
+
*/
|
|
40
|
+
function safeSidecarStats() {
|
|
41
|
+
try {
|
|
42
|
+
return coldSidecarStats();
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
20
48
|
/**
|
|
21
49
|
* Build the `mlx-provider` inline extension serving `models`. The host
|
|
22
50
|
* (one per process — it owns the single GPU-resident model) is created
|
|
@@ -24,27 +52,145 @@ import { makeMlxStreamSimple } from './stream-adapter.js';
|
|
|
24
52
|
* host, but stays lazy about weights: nothing loads until the first
|
|
25
53
|
* `streamSimple` call.
|
|
26
54
|
*/
|
|
27
|
-
export function createMlxProviderExtension(models, host) {
|
|
55
|
+
export function createMlxProviderExtension(models, host, deps = {}) {
|
|
28
56
|
const resolvedHost = host ?? new MlxModelHost(models.map((m) => m.discovered));
|
|
29
57
|
const performanceStatus = new PerformanceStatus();
|
|
58
|
+
const metricsTrace = deps.metricsTrace ?? new MetricsTrace();
|
|
59
|
+
const readColdStats = deps.coldStats ?? safeColdStats;
|
|
60
|
+
const readSidecarStats = deps.sidecarStats ?? safeSidecarStats;
|
|
30
61
|
// This closure outlives Pi runtime replacement. Pi creates a replacement
|
|
31
62
|
// runtime for /new and /resume and reruns inline extension factories; each
|
|
32
63
|
// new factory's session_start updates the root while child sessions keep
|
|
33
|
-
// using this registered stream.
|
|
64
|
+
// using this registered stream. The root id doubles as the cache owner and
|
|
65
|
+
// the metrics-trace root; the JSONL path travels alongside it for metrics.
|
|
34
66
|
let rootCacheOwnerId;
|
|
35
|
-
|
|
67
|
+
let rootSessionFile;
|
|
68
|
+
// Cold-tier counters are cumulative since the tier opened. Snapshot them at
|
|
69
|
+
// each turn's native start (`onTurnStart`, fired inside the serialized host
|
|
70
|
+
// closure) and diff at the success terminal, so a turn that aborted or
|
|
71
|
+
// errored — which never reaches `onTurnRecord` — can't leak its restores into
|
|
72
|
+
// the next successful turn's delta. Inference is serialized per process (host
|
|
73
|
+
// promise chain), so by the time a turn snapshots, any prior turn has fully
|
|
74
|
+
// drained. The SYNCHRONOUS counters (hits/misses/bytesRestored/enqueued/
|
|
75
|
+
// corruptions/queueDrops/restoreDeclines) are exact per-turn; bytesWritten,
|
|
76
|
+
// evictions and writeErrors advance on the async writer thread, so those
|
|
77
|
+
// deltas are approximate (documented per field on the record).
|
|
78
|
+
//
|
|
79
|
+
// The SIDECAR counters are snapshotted the same way but are exact without
|
|
80
|
+
// exception: every one of them is recorded inside the native turn finalize,
|
|
81
|
+
// on the calling thread, before the turn returns. They also have no `enabled`
|
|
82
|
+
// gate — they live in a plain process static rather than in the tier — so
|
|
83
|
+
// they read honestly on a run where the tier never opened, which is the run
|
|
84
|
+
// that most needs them.
|
|
85
|
+
let turnStartCold = readColdStats();
|
|
86
|
+
let turnStartSidecar = readSidecarStats();
|
|
87
|
+
const onTurnStart = () => {
|
|
88
|
+
turnStartCold = readColdStats();
|
|
89
|
+
turnStartSidecar = readSidecarStats();
|
|
90
|
+
};
|
|
91
|
+
const onTurnRecord = ({ traceId, sessionId, rootSessionId, rootSessionFile, model, final, durationMs, queueMs, resident, }) => {
|
|
92
|
+
const rec = {
|
|
93
|
+
traceId,
|
|
94
|
+
ts: Date.now(),
|
|
95
|
+
sessionId,
|
|
96
|
+
rootSessionId,
|
|
97
|
+
rootSessionFile,
|
|
98
|
+
model,
|
|
99
|
+
durationMs,
|
|
100
|
+
queueMs,
|
|
101
|
+
resident,
|
|
102
|
+
finishReason: final.finishReason,
|
|
103
|
+
promptTokens: final.promptTokens,
|
|
104
|
+
cachedTokens: final.cachedTokens ?? 0,
|
|
105
|
+
outputTokens: final.numTokens,
|
|
106
|
+
reasoningTokens: final.reasoningTokens,
|
|
107
|
+
};
|
|
108
|
+
const perf = final.performance;
|
|
109
|
+
if (perf) {
|
|
110
|
+
rec.ttftMs = perf.ttftMs;
|
|
111
|
+
rec.prefillTps = perf.prefillTokensPerSecond;
|
|
112
|
+
rec.decodeTps = perf.decodeTokensPerSecond;
|
|
113
|
+
rec.mtpCycles = perf.mtpCycles;
|
|
114
|
+
// mlx-vlm-comparable headline accept rate (committed tokens per cycle).
|
|
115
|
+
rec.mtpMeanAccepted = perf.mtpMeanAcceptedTokensTotal;
|
|
116
|
+
}
|
|
117
|
+
const cold = readColdStats();
|
|
118
|
+
if (cold && turnStartCold) {
|
|
119
|
+
rec.coldHits = cold.hits - turnStartCold.hits;
|
|
120
|
+
rec.coldMisses = cold.misses - turnStartCold.misses;
|
|
121
|
+
rec.coldBytesWritten = cold.bytesWritten - turnStartCold.bytesWritten;
|
|
122
|
+
rec.coldBytesRestored = cold.bytesRestored - turnStartCold.bytesRestored;
|
|
123
|
+
rec.coldEnqueued = cold.enqueued - turnStartCold.enqueued;
|
|
124
|
+
rec.coldQueueDrops = cold.queueDrops - turnStartCold.queueDrops;
|
|
125
|
+
rec.coldEvictions = cold.evictions - turnStartCold.evictions;
|
|
126
|
+
rec.coldCorruptions = cold.corruptions - turnStartCold.corruptions;
|
|
127
|
+
rec.coldWriteErrors = cold.writeErrors - turnStartCold.writeErrors;
|
|
128
|
+
rec.coldRestoreDeclines = cold.restoreDeclines - turnStartCold.restoreDeclines;
|
|
129
|
+
// Absolutes, not deltas: an aborted/errored turn never reaches this
|
|
130
|
+
// recorder, so a corruption or a dropped write during one lands in NO
|
|
131
|
+
// delta. The cumulative counter observed by the next successful turn
|
|
132
|
+
// still carries it, which is what makes "corruptions must be 0"
|
|
133
|
+
// checkable at all (`MAX(total) > 0` over any window).
|
|
134
|
+
rec.coldCorruptionsTotal = cold.corruptions;
|
|
135
|
+
rec.coldQueueDropsTotal = cold.queueDrops;
|
|
136
|
+
// Same latch, and the counter that needs it most: a write error is
|
|
137
|
+
// raised on the background writer, so the one covering the LAST turn
|
|
138
|
+
// before a crash lands in no delta at all — and "is my cache root
|
|
139
|
+
// broken?" is a question about ever, not about this turn.
|
|
140
|
+
rec.coldWriteErrorsTotal = cold.writeErrors;
|
|
141
|
+
// Cache IDENTITY comes from the END-of-turn snapshot, never the baseline:
|
|
142
|
+
// the tier opens LAZILY on first use, so on a process's first turn
|
|
143
|
+
// `turnStartCold` is the all-zero default with `enabled: false` and an
|
|
144
|
+
// empty root. Canonicalized here — in the writer — so the dashboard
|
|
145
|
+
// never has to match whatever spelling Rust happened to construct.
|
|
146
|
+
rec.coldEnabled = cold.enabled;
|
|
147
|
+
// ONE emptiness test, applied to the CANONICAL value. Gating on the raw
|
|
148
|
+
// native string used a DIFFERENT test from the one `canonicalCacheRoot`
|
|
149
|
+
// applies (it trims), so a whitespace-only root passed `length > 0` here
|
|
150
|
+
// and canonicalized to `''` — which `MetricsTrace.record` then dropped,
|
|
151
|
+
// leaving a row that says the tier was ON while carrying no root at all.
|
|
152
|
+
if (cold.enabled) {
|
|
153
|
+
const canonicalRoot = canonicalCacheRoot(cold.root);
|
|
154
|
+
if (canonicalRoot.length > 0)
|
|
155
|
+
rec.coldRoot = canonicalRoot;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// Its own guard, deliberately not folded into the block above: the sidecar
|
|
159
|
+
// reader never consults the tier, so it keeps reporting when `cold` is
|
|
160
|
+
// undefined or the tier failed to open. A run with `coldHits: 0` and no
|
|
161
|
+
// tier is exactly when "did the capture even run?" is the question, and
|
|
162
|
+
// gating these on `cold` would blank them precisely then.
|
|
163
|
+
const sidecar = readSidecarStats();
|
|
164
|
+
if (sidecar && turnStartSidecar) {
|
|
165
|
+
rec.coldSidecarCaptureReached = sidecar.captureReached - turnStartSidecar.captureReached;
|
|
166
|
+
rec.coldSidecarChainEmpty = sidecar.chainEmpty - turnStartSidecar.chainEmpty;
|
|
167
|
+
rec.coldSidecarBoundarySkips = sidecar.boundarySkips - turnStartSidecar.boundarySkips;
|
|
168
|
+
rec.coldSidecarAlreadyPersisted = sidecar.alreadyPersisted - turnStartSidecar.alreadyPersisted;
|
|
169
|
+
rec.coldSidecarEnqueued = sidecar.enqueued - turnStartSidecar.enqueued;
|
|
170
|
+
rec.coldSidecarQueueDrops = sidecar.queueDrops - turnStartSidecar.queueDrops;
|
|
171
|
+
rec.coldSidecarInstalled = sidecar.installed - turnStartSidecar.installed;
|
|
172
|
+
rec.coldSidecarRestoreSuppressed = sidecar.restoreSuppressed - turnStartSidecar.restoreSuppressed;
|
|
173
|
+
}
|
|
174
|
+
metricsTrace.record(rec);
|
|
175
|
+
};
|
|
176
|
+
const streamSimple = makeMlxStreamSimple(resolvedHost, performanceStatus.record, () => rootCacheOwnerId, onTurnRecord, onTurnStart, () => rootSessionFile);
|
|
36
177
|
return {
|
|
37
178
|
name: 'mlx-provider',
|
|
38
179
|
factory: (pi) => {
|
|
39
|
-
pi.registerProvider(
|
|
40
|
-
api:
|
|
41
|
-
baseUrl:
|
|
42
|
-
apiKey:
|
|
180
|
+
pi.registerProvider(MLX_PROVIDER_ID, {
|
|
181
|
+
api: MLX_API,
|
|
182
|
+
baseUrl: MLX_BASE_URL,
|
|
183
|
+
apiKey: MLX_API_KEY,
|
|
43
184
|
streamSimple,
|
|
44
185
|
models: models.map((m) => m.piModel),
|
|
45
186
|
});
|
|
46
187
|
pi.on('session_start', (_event, ctx) => {
|
|
47
188
|
rootCacheOwnerId = ctx.sessionManager.getSessionId();
|
|
189
|
+
// Snapshot the root JSONL path so a turn submitted under this root is
|
|
190
|
+
// correlated to it at completion — even for subagent turns, which have
|
|
191
|
+
// no session file of their own. `getSessionFile` is optional-chained so
|
|
192
|
+
// a minimal test/mock session manager without it still works.
|
|
193
|
+
rootSessionFile = ctx.sessionManager.getSessionFile?.();
|
|
48
194
|
});
|
|
49
195
|
pi.on('message_end', (event, ctx) => {
|
|
50
196
|
performanceStatus.showMessage(event, ctx);
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `MetricsTrace` — always-on per-turn inference telemetry, appended as JSON
|
|
3
|
+
* Lines to `$HOME/.mlx-node/metrics/traces/<YYYY-MM-DD>-<pid>.jsonl`.
|
|
4
|
+
*
|
|
5
|
+
* This is a durable sink that complements the transient in-memory
|
|
6
|
+
* {@link ./performance-status.ts} WeakMap (which only feeds the live TUI
|
|
7
|
+
* footer). One record is written per successful inference turn so the
|
|
8
|
+
* dashboard can correlate throughput, cache reuse, and cold-tier deltas back
|
|
9
|
+
* to the pi session that produced them via `mlxTraceId`.
|
|
10
|
+
*
|
|
11
|
+
* Contract:
|
|
12
|
+
* - Default-on; the `MLX_AGENT_METRICS` env var set to `0` / `false` / `off`
|
|
13
|
+
* (case-insensitive) is the only kill switch.
|
|
14
|
+
* - `record()` NEVER throws: every field is allowlisted (no free text ever
|
|
15
|
+
* lands on disk) and all fs work is wrapped — telemetry must never break
|
|
16
|
+
* an inference turn.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Per-turn delta of every COUNTER on the native `ColdCacheStats` — the paged
|
|
20
|
+
* K/V block traffic. One entry per native counter field, named
|
|
21
|
+
* `cold` + PascalCase(nativeKey); the three non-counter fields (`enabled`,
|
|
22
|
+
* `root`, `quotaBytes`) describe the tier's identity rather than a turn and are
|
|
23
|
+
* carried separately as {@link MetricsTraceRecord.coldEnabled} /
|
|
24
|
+
* {@link MetricsTraceRecord.coldRoot}.
|
|
25
|
+
*
|
|
26
|
+
* This is the TS half of a cross-language invariant.
|
|
27
|
+
* `__test__/cold-counter-fields.test.ts` derives the same names from
|
|
28
|
+
* `coldCacheStats()` at runtime and demands an exact match, so a counter added
|
|
29
|
+
* natively but not here — or, the failure this list exists for, one quietly
|
|
30
|
+
* dropped from here — is a red test rather than an empty dashboard column.
|
|
31
|
+
*/
|
|
32
|
+
export declare const COLD_COUNTER_FIELDS: readonly ['coldHits', 'coldMisses', 'coldEnqueued', 'coldQueueDrops', 'coldBytesWritten', 'coldBytesRestored', 'coldEvictions', 'coldCorruptions', 'coldWriteErrors', 'coldRestoreDeclines'];
|
|
33
|
+
/**
|
|
34
|
+
* Per-turn delta of every counter on the native `ColdSidecarStats` — the
|
|
35
|
+
* recurrent / sliding-window state that lives OUTSIDE the paged pool. Named
|
|
36
|
+
* `coldSidecar` + PascalCase(nativeKey).
|
|
37
|
+
*
|
|
38
|
+
* Deliberately a second, differently-prefixed list rather than a merge: both
|
|
39
|
+
* native structs carry `enqueued` and `queueDrops`, and they count different
|
|
40
|
+
* objects (blocks vs sidecars). Flattening them into one namespace would make
|
|
41
|
+
* two unrelated numbers collide on one column.
|
|
42
|
+
*/
|
|
43
|
+
export declare const COLD_SIDECAR_FIELDS: readonly ['coldSidecarCaptureReached', 'coldSidecarChainEmpty', 'coldSidecarBoundarySkips', 'coldSidecarAlreadyPersisted', 'coldSidecarEnqueued', 'coldSidecarQueueDrops', 'coldSidecarInstalled', 'coldSidecarRestoreSuppressed'];
|
|
44
|
+
/** Every cold-tier per-turn delta field, in native-struct order. */
|
|
45
|
+
export declare const COLD_DELTA_FIELDS: readonly ["coldHits", "coldMisses", "coldEnqueued", "coldQueueDrops", "coldBytesWritten", "coldBytesRestored", "coldEvictions", "coldCorruptions", "coldWriteErrors", "coldRestoreDeclines", "coldSidecarCaptureReached", "coldSidecarChainEmpty", "coldSidecarBoundarySkips", "coldSidecarAlreadyPersisted", "coldSidecarEnqueued", "coldSidecarQueueDrops", "coldSidecarInstalled", "coldSidecarRestoreSuppressed"];
|
|
46
|
+
/** A key of {@link COLD_DELTA_FIELDS}; every one is an optional `number`. */
|
|
47
|
+
export type ColdDeltaField = (typeof COLD_DELTA_FIELDS)[number];
|
|
48
|
+
/**
|
|
49
|
+
* One inference turn's telemetry. Every field is a number, a small
|
|
50
|
+
* enumerated string (`finishReason`), or an identifier — never model output
|
|
51
|
+
* text, tool arguments, or prompt content.
|
|
52
|
+
*/
|
|
53
|
+
export interface MetricsTraceRecord extends Partial<Record<ColdDeltaField, number>> {
|
|
54
|
+
/** Schema version. */
|
|
55
|
+
v: 1;
|
|
56
|
+
/** Join key minted by `TurnEmitter`; also stamped on the pi message as `mlxTraceId`. */
|
|
57
|
+
traceId: string;
|
|
58
|
+
/** Wall-clock write time (ms since epoch). */
|
|
59
|
+
ts: number;
|
|
60
|
+
/** pi per-request session id (parent vs each subagent differ). */
|
|
61
|
+
sessionId?: string;
|
|
62
|
+
/** Root pi session id the turn was SUBMITTED under (snapshotted at submit). */
|
|
63
|
+
rootSessionId?: string;
|
|
64
|
+
/** Root pi session JSONL file path the turn was SUBMITTED under (snapshotted at submit). */
|
|
65
|
+
rootSessionFile?: string;
|
|
66
|
+
/** Model id served this turn (`mlx/<dir-name>`'s `<dir-name>`). */
|
|
67
|
+
model: string;
|
|
68
|
+
/** Turn duration (ms) bracketing resident selection + prefill + decode. */
|
|
69
|
+
durationMs: number;
|
|
70
|
+
/**
|
|
71
|
+
* Queue + cold-load wait (ms) before native work began this turn — the gap
|
|
72
|
+
* between turn submission and the serialized inference callback firing.
|
|
73
|
+
* Subtract from `durationMs` to isolate execution-only latency.
|
|
74
|
+
*/
|
|
75
|
+
queueMs?: number;
|
|
76
|
+
/** `true` when the model was already warm/resident, `false` on a cold load/swap. */
|
|
77
|
+
resident?: boolean;
|
|
78
|
+
/** Native finish reason (`stop` / `length` / `tool_calls` / …). */
|
|
79
|
+
finishReason: string;
|
|
80
|
+
promptTokens: number;
|
|
81
|
+
cachedTokens: number;
|
|
82
|
+
outputTokens: number;
|
|
83
|
+
reasoningTokens: number;
|
|
84
|
+
ttftMs?: number;
|
|
85
|
+
prefillTps?: number;
|
|
86
|
+
decodeTps?: number;
|
|
87
|
+
mtpCycles?: number;
|
|
88
|
+
mtpMeanAccepted?: number;
|
|
89
|
+
/** Cold-tier restore hits accrued this turn (synchronous counter — exact). */
|
|
90
|
+
coldHits?: number;
|
|
91
|
+
/** Cold-tier lookup misses accrued this turn (synchronous counter — exact). */
|
|
92
|
+
coldMisses?: number;
|
|
93
|
+
/**
|
|
94
|
+
* Cold-tier bytes that LANDED this turn — credited natively only after the
|
|
95
|
+
* payload sync, the commit rename and the directory fsync all succeeded, so
|
|
96
|
+
* a write that failed contributes nothing here and one {@link
|
|
97
|
+
* coldWriteErrors} instead.
|
|
98
|
+
*
|
|
99
|
+
* Advances on an async writer thread, so this delta is APPROXIMATE — it may
|
|
100
|
+
* attribute a prior turn's flush to the turn that observes it, and a write
|
|
101
|
+
* still in flight at the end of the turn lands in a later one. That is the
|
|
102
|
+
* only sense in which it lags; it is never an enqueue-time estimate.
|
|
103
|
+
*/
|
|
104
|
+
coldBytesWritten?: number;
|
|
105
|
+
/** Cold-tier bytes read back on validated hits this turn (synchronous — exact). */
|
|
106
|
+
coldBytesRestored?: number;
|
|
107
|
+
/**
|
|
108
|
+
* Canonical cold-cache root this turn's tier was operating in, as produced by
|
|
109
|
+
* `canonicalCacheRoot` — the JOIN KEY the dashboard scopes its hit-rate and
|
|
110
|
+
* trend to. Written ONLY when the tier was actually open; a turn that ran
|
|
111
|
+
* with the tier off carries `coldEnabled: false` and NO root, and a record
|
|
112
|
+
* written by a build that predates this field carries neither. Those two
|
|
113
|
+
* states are deliberately distinguishable: the dashboard reports "recorded
|
|
114
|
+
* before cache attribution existed" separately from "the tier was off".
|
|
115
|
+
*/
|
|
116
|
+
coldRoot?: string;
|
|
117
|
+
/** Whether the cold tier was open for this turn (`ColdCacheStats.enabled`). */
|
|
118
|
+
coldEnabled?: boolean;
|
|
119
|
+
/**
|
|
120
|
+
* Cold-tier write-queue submissions accrued this turn. Incremented on the
|
|
121
|
+
* calling thread, so this delta is EXACT.
|
|
122
|
+
*/
|
|
123
|
+
coldEnqueued?: number;
|
|
124
|
+
/**
|
|
125
|
+
* Cold-tier writes REFUSED at admission this turn because the bounded queue
|
|
126
|
+
* was full. Incremented on the calling thread, so this delta is EXACT.
|
|
127
|
+
*
|
|
128
|
+
* Now admission refusals only: the commit-rename-failure arm used to be
|
|
129
|
+
* counted here too, which put one event under a name describing a different
|
|
130
|
+
* cause. It is a {@link coldWriteErrors} instead, and the two are disjoint —
|
|
131
|
+
* never sum them.
|
|
132
|
+
*/
|
|
133
|
+
coldQueueDrops?: number;
|
|
134
|
+
/**
|
|
135
|
+
* Cold-tier objects evicted by quota enforcement this turn. Advances on the
|
|
136
|
+
* background writer thread — APPROXIMATE, same caveat as `coldBytesWritten`.
|
|
137
|
+
*/
|
|
138
|
+
coldEvictions?: number;
|
|
139
|
+
/**
|
|
140
|
+
* Cold-tier objects that failed validation on restore this turn. Incremented
|
|
141
|
+
* on the calling thread, so this delta is EXACT. Always accompanied by a
|
|
142
|
+
* miss, i.e. corruptions are a SUBSET of `coldMisses` — never add the two.
|
|
143
|
+
*/
|
|
144
|
+
coldCorruptions?: number;
|
|
145
|
+
/**
|
|
146
|
+
* CUMULATIVE corruptions since the tier opened in this process, not a delta.
|
|
147
|
+
* Carried because the acceptance bar is "corruptions must be 0" and a delta
|
|
148
|
+
* alone cannot prove it: a turn that aborts or errors never reaches
|
|
149
|
+
* `record()`, so a corruption during it lands in no delta at all. The next
|
|
150
|
+
* successful turn's absolute total still includes it, so `MAX(total) > 0`
|
|
151
|
+
* over any window is the sound "did this ever happen" latch.
|
|
152
|
+
*/
|
|
153
|
+
coldCorruptionsTotal?: number;
|
|
154
|
+
/**
|
|
155
|
+
* CUMULATIVE queue drops since the tier opened in this process, for the same
|
|
156
|
+
* reason as {@link coldCorruptionsTotal}: an errored turn's dropped writes
|
|
157
|
+
* would otherwise be invisible.
|
|
158
|
+
*/
|
|
159
|
+
coldQueueDropsTotal?: number;
|
|
160
|
+
/**
|
|
161
|
+
* Cold-tier writes this turn that the queue ACCEPTED and that never reached
|
|
162
|
+
* disk — a read-only, full or unmounted cache root, a quota the object
|
|
163
|
+
* cannot fit, a failed rename, a failed fsync.
|
|
164
|
+
*
|
|
165
|
+
* The native writer is deliberately fail-open (a broken cache root must not
|
|
166
|
+
* change a single emitted token) and it returns its error to nobody, so
|
|
167
|
+
* before this counter existed the entire class was invisible: a turn against
|
|
168
|
+
* an unwritable root reported `coldQueueDrops 0`, `coldCorruptions 0`, an
|
|
169
|
+
* empty stderr and a clean exit. Advances on the background writer thread,
|
|
170
|
+
* so APPROXIMATE per turn in the same way {@link coldBytesWritten} is —
|
|
171
|
+
* non-zero over a window is the signal, not the exact attribution.
|
|
172
|
+
*/
|
|
173
|
+
coldWriteErrors?: number;
|
|
174
|
+
/**
|
|
175
|
+
* Restores REFUSED this turn: the tier held candidate blocks and the walk
|
|
176
|
+
* handed back none of them.
|
|
177
|
+
*
|
|
178
|
+
* Neither a hit nor a miss, because both of those count per-block lookups
|
|
179
|
+
* and a refusal happens instead of one — which is why a refused restore
|
|
180
|
+
* reported `coldHits 0, coldMisses 0`, exactly the row a turn that never
|
|
181
|
+
* consulted the tier produces. Incremented on the calling thread, so EXACT.
|
|
182
|
+
* The reason (`no_backed_boundary`, `parent_chain_unavailable`,
|
|
183
|
+
* `restore_short_of_boundary`) and the block geometry go to the
|
|
184
|
+
* `[MLX_TRACE] paged cold_restore_declined` line.
|
|
185
|
+
*/
|
|
186
|
+
coldRestoreDeclines?: number;
|
|
187
|
+
/**
|
|
188
|
+
* CUMULATIVE write errors since the tier opened in this process, for the
|
|
189
|
+
* same reason as {@link coldCorruptionsTotal} — and more sharply, since this
|
|
190
|
+
* counter advances on the background writer: the error covering the last
|
|
191
|
+
* turn before a crash reaches no delta at all.
|
|
192
|
+
*
|
|
193
|
+
* No such total exists for {@link coldRestoreDeclines} on purpose. A "did it
|
|
194
|
+
* ever happen" latch is only meaningful for a counter whose healthy value is
|
|
195
|
+
* zero; the first turn of any new prompt legitimately declines, so a latch
|
|
196
|
+
* there would be pinned on from the first minute and mean nothing.
|
|
197
|
+
*/
|
|
198
|
+
coldWriteErrorsTotal?: number;
|
|
199
|
+
/**
|
|
200
|
+
* Turns this turn's process spent reaching a family's sidecar capture. Every
|
|
201
|
+
* other `coldSidecar*` counter is a sub-count of this one, so `0` here while
|
|
202
|
+
* blocks were written means the finalize path never called the capture at
|
|
203
|
+
* all — a different bug from a capture that ran and declined.
|
|
204
|
+
*/
|
|
205
|
+
coldSidecarCaptureReached?: number;
|
|
206
|
+
/**
|
|
207
|
+
* Sidecar captures that found no whole persisted block to anchor recurrent
|
|
208
|
+
* state under.
|
|
209
|
+
*/
|
|
210
|
+
coldSidecarChainEmpty?: number;
|
|
211
|
+
/**
|
|
212
|
+
* Sidecar captures whose chain covered blocks but where no retained
|
|
213
|
+
* checkpoint sat at or below its reach — the "ladder collapsed to its
|
|
214
|
+
* deepest rung" signature.
|
|
215
|
+
*/
|
|
216
|
+
coldSidecarBoundarySkips?: number;
|
|
217
|
+
/**
|
|
218
|
+
* Sidecar captures that selected a boundary already on disk. The STEADY
|
|
219
|
+
* STATE of a repeated prompt, not a fault: without it a healthy run
|
|
220
|
+
* (`coldSidecarEnqueued == 0` because the first turn already wrote it) is
|
|
221
|
+
* indistinguishable from a broken one.
|
|
222
|
+
*/
|
|
223
|
+
coldSidecarAlreadyPersisted?: number;
|
|
224
|
+
/**
|
|
225
|
+
* Sidecars handed to the writer queue this turn. A SUBSET of
|
|
226
|
+
* {@link coldEnqueued}, which counts every object that took a queue slot —
|
|
227
|
+
* so summing them double-counts each sidecar.
|
|
228
|
+
*/
|
|
229
|
+
coldSidecarEnqueued?: number;
|
|
230
|
+
/**
|
|
231
|
+
* Sidecars the writer queue refused because it was full. Likewise a subset
|
|
232
|
+
* of {@link coldQueueDrops}, not a separate population.
|
|
233
|
+
*/
|
|
234
|
+
coldSidecarQueueDrops?: number;
|
|
235
|
+
/**
|
|
236
|
+
* Restored sidecars a family INSTALLED as its live per-turn state. The one
|
|
237
|
+
* read-side sidecar counter, and the only thing that separates "restored and
|
|
238
|
+
* used" from "restored, then silently re-derived by a full O(prefix) replay":
|
|
239
|
+
* the replay produces correct state, so `cachedTokens`, `coldHits`,
|
|
240
|
+
* `coldCorruptions` and the output text are all identical either way.
|
|
241
|
+
*/
|
|
242
|
+
coldSidecarInstalled?: number;
|
|
243
|
+
/**
|
|
244
|
+
* Restored prefixes a family THREW AWAY this turn, releasing the request and
|
|
245
|
+
* restarting the turn cold (gemma4's large-sliding-restore suppression).
|
|
246
|
+
*
|
|
247
|
+
* A different event from {@link coldRestoreDeclines}: there the walk refused
|
|
248
|
+
* to serve anything, here it served and the family discarded it. This one is
|
|
249
|
+
* therefore preceded by real `coldHits` and `coldBytesRestored`, so the turn
|
|
250
|
+
* reads as successful reuse right up to the point where it recomputes the
|
|
251
|
+
* whole prompt anyway.
|
|
252
|
+
*/
|
|
253
|
+
coldSidecarRestoreSuppressed?: number;
|
|
254
|
+
}
|
|
255
|
+
type MetricsTraceInput = Omit<MetricsTraceRecord, 'v'>;
|
|
256
|
+
export declare class MetricsTrace {
|
|
257
|
+
readonly enabled: boolean;
|
|
258
|
+
private readonly dir;
|
|
259
|
+
private readonly now;
|
|
260
|
+
constructor(opts?: {
|
|
261
|
+
dir?: string;
|
|
262
|
+
now?: () => number;
|
|
263
|
+
});
|
|
264
|
+
/** `<dir>/<YYYY-MM-DD>-<pid>.jsonl` — UTC date so rotation is timezone-stable. */
|
|
265
|
+
currentFile(): string;
|
|
266
|
+
/**
|
|
267
|
+
* Append one allowlisted JSON line. Never throws: a broken sink must not
|
|
268
|
+
* surface into the inference path. Excess input properties are dropped — the
|
|
269
|
+
* record is rebuilt field by field so free text can never reach disk.
|
|
270
|
+
*/
|
|
271
|
+
record(rec: MetricsTraceInput): void;
|
|
272
|
+
}
|
|
273
|
+
export {};
|
|
274
|
+
//# sourceMappingURL=metrics-trace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics-trace.d.ts","sourceRoot":"","sources":["../../src/provider/metrics-trace.ts"],"names":[],"mappings":"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"}
|