@mlx-node/agent 0.0.7 → 0.0.8
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mlx-node/agent",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"homepage": "https://github.com/mlx-node/mlx-node",
|
|
5
5
|
"bugs": {
|
|
6
6
|
"url": "https://github.com/mlx-node/mlx-node/issues"
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@earendil-works/pi-ai": "0.80.6",
|
|
31
31
|
"@earendil-works/pi-coding-agent": "0.80.6",
|
|
32
|
-
"@mlx-node/lm": "0.0.
|
|
33
|
-
"@mlx-node/server": "0.0.
|
|
32
|
+
"@mlx-node/lm": "0.0.8",
|
|
33
|
+
"@mlx-node/server": "0.0.8",
|
|
34
34
|
"typebox": "1.3.6"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Privacy-safe, best-effort inference tracing for the in-process mlx agent.
|
|
3
|
-
*
|
|
4
|
-
* The trace is deliberately metadata-only. Callers can only supply fields from
|
|
5
|
-
* the allowlist below; prompt/output text, tool payloads, paths, token ids, and
|
|
6
|
-
* error messages have no serialization path. Tracing is enabled only when
|
|
7
|
-
* `MLX_INFERENCE_TRACE` is explicitly truthy and
|
|
8
|
-
* `MLX_INFERENCE_TRACE_FILE` is non-empty.
|
|
9
|
-
*
|
|
10
|
-
* Writes are synchronous because this is a bounded, high-level lifecycle trace
|
|
11
|
-
* (roughly a dozen records per request), not a token/layer trace. Every write is
|
|
12
|
-
* swallowed on failure so observability can never change inference behavior.
|
|
13
|
-
*/
|
|
14
|
-
export type InferenceTraceEvent = 'request.created' | 'host.queue.enter' | 'host.start' | 'host.resident' | 'host.load.start' | 'host.load.end' | 'host.callback.end' | 'session.reset' | 'session.prime' | 'session.config' | 'native.stream.start' | 'native.stream.first_delta' | 'request.terminal';
|
|
15
|
-
export type InferenceTraceStatus = 'ok' | 'error' | 'aborted';
|
|
16
|
-
export interface InferenceTraceFields {
|
|
17
|
-
status?: InferenceTraceStatus;
|
|
18
|
-
resident?: 'hit' | 'load' | 'swap';
|
|
19
|
-
resetMode?: 'preserve' | 'full';
|
|
20
|
-
finishReason?: 'stop' | 'length' | 'cancelled' | 'error' | 'other';
|
|
21
|
-
reasoningEffort?: 'none' | 'low' | 'medium' | 'high';
|
|
22
|
-
modelType?: 'gemma4' | 'qwen3' | 'qwen3_5' | 'qwen3_5_moe' | 'lfm2' | 'lfm2_moe' | 'other';
|
|
23
|
-
streaming?: boolean;
|
|
24
|
-
reportPerformance?: boolean;
|
|
25
|
-
isReasoning?: boolean;
|
|
26
|
-
durationMs?: number;
|
|
27
|
-
queueMs?: number;
|
|
28
|
-
messageCount?: number;
|
|
29
|
-
toolCount?: number;
|
|
30
|
-
maxNewTokens?: number;
|
|
31
|
-
promptTokens?: number;
|
|
32
|
-
cachedTokens?: number;
|
|
33
|
-
generatedTokens?: number;
|
|
34
|
-
reasoningTokens?: number;
|
|
35
|
-
ttftMs?: number;
|
|
36
|
-
prefillTokensPerSecond?: number;
|
|
37
|
-
decodeTokensPerSecond?: number;
|
|
38
|
-
mtpMeanAcceptedTokens?: number;
|
|
39
|
-
mtpMeanAcceptedTokensTotal?: number;
|
|
40
|
-
mtpCycles?: number;
|
|
41
|
-
mtpMeanDepth?: number;
|
|
42
|
-
mtpAcceptanceByPosition?: readonly number[];
|
|
43
|
-
}
|
|
44
|
-
export interface InferenceTraceSink {
|
|
45
|
-
write(line: string): void;
|
|
46
|
-
}
|
|
47
|
-
export declare class InferenceTrace {
|
|
48
|
-
private readonly sink;
|
|
49
|
-
readonly requestId: string;
|
|
50
|
-
private terminalWritten;
|
|
51
|
-
constructor(sink?: InferenceTraceSink | undefined);
|
|
52
|
-
emit(event: InferenceTraceEvent, fields?: InferenceTraceFields): void;
|
|
53
|
-
terminal(status: InferenceTraceStatus, fields?: InferenceTraceFields): void;
|
|
54
|
-
}
|
|
55
|
-
export declare function createInferenceTrace(): InferenceTrace;
|
|
56
|
-
export declare function classifyFinishReason(value: string): NonNullable<InferenceTraceFields['finishReason']>;
|
|
57
|
-
export declare function classifyModelType(value: string): NonNullable<InferenceTraceFields['modelType']>;
|
|
58
|
-
//# sourceMappingURL=inference-trace.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"inference-trace.d.ts","sourceRoot":"","sources":["../../src/provider/inference-trace.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,MAAM,MAAM,mBAAmB,GAC3B,iBAAiB,GACjB,kBAAkB,GAClB,YAAY,GACZ,eAAe,GACf,iBAAiB,GACjB,eAAe,GACf,mBAAmB,GACnB,eAAe,GACf,eAAe,GACf,gBAAgB,GAChB,qBAAqB,GACrB,2BAA2B,GAC3B,kBAAkB,CAAC;AAEvB,MAAM,MAAM,oBAAoB,GAAG,IAAI,GAAG,OAAO,GAAG,SAAS,CAAC;AAE9D,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B,QAAQ,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;IACnC,SAAS,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;IAChC,YAAY,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,WAAW,GAAG,OAAO,GAAG,OAAO,CAAC;IACnE,eAAe,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IACrD,SAAS,CAAC,EACN,QAAQ,GACR,OAAO,GACP,SAAS,GACT,aAAa,GACb,MAAM,GACN,UAAU,GACV,OAAO,CAAC;IACZ,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uBAAuB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7C;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AA+ID,qBAAa,cAAc;IAIb,OAAO,CAAC,QAAQ,CAAC,IAAI;IAHjC,QAAQ,CAAC,SAAS,SAAmB;IACrC,OAAO,CAAC,eAAe,CAAS;IAEhC,YAA6B,IAAI,GAAE,kBAAkB,GAAG,SAAyB,EAAI;IAErF,IAAI,CAAC,KAAK,EAAE,mBAAmB,EAAE,MAAM,GAAE,oBAAyB,GAAG,IAAI,CAiBxE;IAED,QAAQ,CAAC,MAAM,EAAE,oBAAoB,EAAE,MAAM,GAAE,oBAAyB,GAAG,IAAI,CAI9E;CACF;AAED,wBAAgB,oBAAoB,IAAI,cAAc,CAErD;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CAAC,oBAAoB,CAAC,cAAc,CAAC,CAAC,CAUrG;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CAAC,oBAAoB,CAAC,WAAW,CAAC,CAAC,CAY/F"}
|
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Privacy-safe, best-effort inference tracing for the in-process mlx agent.
|
|
3
|
-
*
|
|
4
|
-
* The trace is deliberately metadata-only. Callers can only supply fields from
|
|
5
|
-
* the allowlist below; prompt/output text, tool payloads, paths, token ids, and
|
|
6
|
-
* error messages have no serialization path. Tracing is enabled only when
|
|
7
|
-
* `MLX_INFERENCE_TRACE` is explicitly truthy and
|
|
8
|
-
* `MLX_INFERENCE_TRACE_FILE` is non-empty.
|
|
9
|
-
*
|
|
10
|
-
* Writes are synchronous because this is a bounded, high-level lifecycle trace
|
|
11
|
-
* (roughly a dozen records per request), not a token/layer trace. Every write is
|
|
12
|
-
* swallowed on failure so observability can never change inference behavior.
|
|
13
|
-
*/
|
|
14
|
-
import { appendFileSync } from 'node:fs';
|
|
15
|
-
const EVENTS = new Set([
|
|
16
|
-
'request.created',
|
|
17
|
-
'host.queue.enter',
|
|
18
|
-
'host.start',
|
|
19
|
-
'host.resident',
|
|
20
|
-
'host.load.start',
|
|
21
|
-
'host.load.end',
|
|
22
|
-
'host.callback.end',
|
|
23
|
-
'session.reset',
|
|
24
|
-
'session.prime',
|
|
25
|
-
'session.config',
|
|
26
|
-
'native.stream.start',
|
|
27
|
-
'native.stream.first_delta',
|
|
28
|
-
'request.terminal',
|
|
29
|
-
]);
|
|
30
|
-
const STATUS = new Set(['ok', 'error', 'aborted']);
|
|
31
|
-
const RESIDENT = new Set(['hit', 'load', 'swap']);
|
|
32
|
-
const RESET_MODE = new Set(['preserve', 'full']);
|
|
33
|
-
const FINISH_REASON = new Set([
|
|
34
|
-
'stop',
|
|
35
|
-
'length',
|
|
36
|
-
'cancelled',
|
|
37
|
-
'error',
|
|
38
|
-
'other',
|
|
39
|
-
]);
|
|
40
|
-
const REASONING_EFFORT = new Set([
|
|
41
|
-
'none',
|
|
42
|
-
'low',
|
|
43
|
-
'medium',
|
|
44
|
-
'high',
|
|
45
|
-
]);
|
|
46
|
-
const MODEL_TYPE = new Set([
|
|
47
|
-
'gemma4',
|
|
48
|
-
'qwen3',
|
|
49
|
-
'qwen3_5',
|
|
50
|
-
'qwen3_5_moe',
|
|
51
|
-
'lfm2',
|
|
52
|
-
'lfm2_moe',
|
|
53
|
-
'other',
|
|
54
|
-
]);
|
|
55
|
-
let requestSequence = 0;
|
|
56
|
-
let eventSequence = 0;
|
|
57
|
-
function envFlagEnabled(value) {
|
|
58
|
-
if (value == null)
|
|
59
|
-
return false;
|
|
60
|
-
return ['1', 'true', 'yes', 'on'].includes(value.trim().toLowerCase());
|
|
61
|
-
}
|
|
62
|
-
function defaultSink() {
|
|
63
|
-
if (!envFlagEnabled(process.env.MLX_INFERENCE_TRACE))
|
|
64
|
-
return undefined;
|
|
65
|
-
const path = process.env.MLX_INFERENCE_TRACE_FILE?.trim();
|
|
66
|
-
if (!path)
|
|
67
|
-
return undefined;
|
|
68
|
-
return {
|
|
69
|
-
write(line) {
|
|
70
|
-
appendFileSync(path, line, { encoding: 'utf8', flag: 'a', mode: 0o600 });
|
|
71
|
-
},
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
function nextRequestId() {
|
|
75
|
-
requestSequence += 1;
|
|
76
|
-
return `agent-${process.pid.toString(36)}-${requestSequence.toString(36)}`;
|
|
77
|
-
}
|
|
78
|
-
function nonNegativeFinite(value) {
|
|
79
|
-
return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : undefined;
|
|
80
|
-
}
|
|
81
|
-
function nonNegativeInteger(value) {
|
|
82
|
-
const n = nonNegativeFinite(value);
|
|
83
|
-
return n !== undefined && Number.isSafeInteger(n) ? n : undefined;
|
|
84
|
-
}
|
|
85
|
-
function copyNumber(out, fields, key, integer) {
|
|
86
|
-
const value = integer ? nonNegativeInteger(fields[key]) : nonNegativeFinite(fields[key]);
|
|
87
|
-
if (value !== undefined)
|
|
88
|
-
out[key] = value;
|
|
89
|
-
}
|
|
90
|
-
/** Runtime allowlist: protects the file even if an untyped caller supplies extras. */
|
|
91
|
-
function sanitizeFields(fields) {
|
|
92
|
-
const out = {};
|
|
93
|
-
if (fields.status !== undefined && STATUS.has(fields.status))
|
|
94
|
-
out.status = fields.status;
|
|
95
|
-
if (fields.resident !== undefined && RESIDENT.has(fields.resident))
|
|
96
|
-
out.resident = fields.resident;
|
|
97
|
-
if (fields.resetMode !== undefined && RESET_MODE.has(fields.resetMode))
|
|
98
|
-
out.resetMode = fields.resetMode;
|
|
99
|
-
if (fields.finishReason !== undefined && FINISH_REASON.has(fields.finishReason)) {
|
|
100
|
-
out.finishReason = fields.finishReason;
|
|
101
|
-
}
|
|
102
|
-
if (fields.reasoningEffort !== undefined && REASONING_EFFORT.has(fields.reasoningEffort)) {
|
|
103
|
-
out.reasoningEffort = fields.reasoningEffort;
|
|
104
|
-
}
|
|
105
|
-
if (fields.modelType !== undefined && MODEL_TYPE.has(fields.modelType))
|
|
106
|
-
out.modelType = fields.modelType;
|
|
107
|
-
for (const key of ['streaming', 'reportPerformance', 'isReasoning']) {
|
|
108
|
-
if (typeof fields[key] === 'boolean')
|
|
109
|
-
out[key] = fields[key];
|
|
110
|
-
}
|
|
111
|
-
for (const key of [
|
|
112
|
-
'durationMs',
|
|
113
|
-
'queueMs',
|
|
114
|
-
'ttftMs',
|
|
115
|
-
'prefillTokensPerSecond',
|
|
116
|
-
'decodeTokensPerSecond',
|
|
117
|
-
'mtpMeanAcceptedTokens',
|
|
118
|
-
'mtpMeanAcceptedTokensTotal',
|
|
119
|
-
'mtpMeanDepth',
|
|
120
|
-
]) {
|
|
121
|
-
copyNumber(out, fields, key, false);
|
|
122
|
-
}
|
|
123
|
-
for (const key of [
|
|
124
|
-
'messageCount',
|
|
125
|
-
'toolCount',
|
|
126
|
-
'maxNewTokens',
|
|
127
|
-
'promptTokens',
|
|
128
|
-
'cachedTokens',
|
|
129
|
-
'generatedTokens',
|
|
130
|
-
'reasoningTokens',
|
|
131
|
-
'mtpCycles',
|
|
132
|
-
]) {
|
|
133
|
-
copyNumber(out, fields, key, true);
|
|
134
|
-
}
|
|
135
|
-
if (Array.isArray(fields.mtpAcceptanceByPosition)) {
|
|
136
|
-
const values = fields.mtpAcceptanceByPosition
|
|
137
|
-
.slice(0, 16)
|
|
138
|
-
.map(nonNegativeFinite)
|
|
139
|
-
.filter((value) => value !== undefined);
|
|
140
|
-
if (values.length > 0)
|
|
141
|
-
out.mtpAcceptanceByPosition = values;
|
|
142
|
-
}
|
|
143
|
-
return out;
|
|
144
|
-
}
|
|
145
|
-
export class InferenceTrace {
|
|
146
|
-
sink;
|
|
147
|
-
requestId = nextRequestId();
|
|
148
|
-
terminalWritten = false;
|
|
149
|
-
constructor(sink = defaultSink()) {
|
|
150
|
-
this.sink = sink;
|
|
151
|
-
}
|
|
152
|
-
emit(event, fields = {}) {
|
|
153
|
-
if (!this.sink || !EVENTS.has(event))
|
|
154
|
-
return;
|
|
155
|
-
try {
|
|
156
|
-
eventSequence += 1;
|
|
157
|
-
const record = {
|
|
158
|
-
timestamp: new Date().toISOString(),
|
|
159
|
-
pid: process.pid,
|
|
160
|
-
seq: eventSequence,
|
|
161
|
-
requestId: this.requestId,
|
|
162
|
-
component: 'agent',
|
|
163
|
-
event,
|
|
164
|
-
...sanitizeFields(fields),
|
|
165
|
-
};
|
|
166
|
-
this.sink.write(`${JSON.stringify(record)}\n`);
|
|
167
|
-
}
|
|
168
|
-
catch {
|
|
169
|
-
// Diagnostics are best-effort and must never affect inference.
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
terminal(status, fields = {}) {
|
|
173
|
-
if (this.terminalWritten)
|
|
174
|
-
return;
|
|
175
|
-
this.terminalWritten = true;
|
|
176
|
-
this.emit('request.terminal', { ...fields, status });
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
export function createInferenceTrace() {
|
|
180
|
-
return new InferenceTrace();
|
|
181
|
-
}
|
|
182
|
-
export function classifyFinishReason(value) {
|
|
183
|
-
switch (value) {
|
|
184
|
-
case 'stop':
|
|
185
|
-
case 'length':
|
|
186
|
-
case 'cancelled':
|
|
187
|
-
case 'error':
|
|
188
|
-
return value;
|
|
189
|
-
default:
|
|
190
|
-
return 'other';
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
export function classifyModelType(value) {
|
|
194
|
-
switch (value) {
|
|
195
|
-
case 'gemma4':
|
|
196
|
-
case 'qwen3':
|
|
197
|
-
case 'qwen3_5':
|
|
198
|
-
case 'qwen3_5_moe':
|
|
199
|
-
case 'lfm2':
|
|
200
|
-
case 'lfm2_moe':
|
|
201
|
-
return value;
|
|
202
|
-
default:
|
|
203
|
-
return 'other';
|
|
204
|
-
}
|
|
205
|
-
}
|