@autter/runtime-node 1.3.2 → 1.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/index.cjs +91 -43
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +49 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @autter/runtime-node
|
|
2
2
|
|
|
3
|
+
The server tracker exports per-instance RSS, heap, memory limit, and GC
|
|
4
|
+
metrics by default. See [memory pressure incidents](../../docs/MEMORY-PRESSURE.md)
|
|
5
|
+
for ECS/Kubernetes OOM event forwarding and optional heap profiles. Set
|
|
6
|
+
`memoryMetrics: false` only if another exporter already emits equivalent
|
|
7
|
+
process metrics.
|
|
8
|
+
|
|
3
9
|
Autter Runtime for Node.js — two halves in one package:
|
|
4
10
|
|
|
5
11
|
1. **Same-origin browser relay** for `@autter/runtime-browser`
|
package/dist/index.cjs
CHANGED
|
@@ -83,6 +83,7 @@ var EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
|
83
83
|
"track_event",
|
|
84
84
|
"outcome",
|
|
85
85
|
"request_failure",
|
|
86
|
+
"csp_violation",
|
|
86
87
|
"timing"
|
|
87
88
|
]);
|
|
88
89
|
var SEVERITIES = /* @__PURE__ */ new Set(["fatal", "error", "warning", "info"]);
|
|
@@ -255,7 +256,7 @@ function createBrowserRelayHandler(opts) {
|
|
|
255
256
|
// src/server.ts
|
|
256
257
|
var import_node_http = require("http");
|
|
257
258
|
var import_node_crypto = require("crypto");
|
|
258
|
-
var
|
|
259
|
+
var import_api2 = require("@opentelemetry/api");
|
|
259
260
|
var import_core2 = require("@opentelemetry/core");
|
|
260
261
|
var import_exporter_metrics_otlp_http = require("@opentelemetry/exporter-metrics-otlp-http");
|
|
261
262
|
var import_exporter_trace_otlp_http = require("@opentelemetry/exporter-trace-otlp-http");
|
|
@@ -707,6 +708,51 @@ function makeRedactor(options) {
|
|
|
707
708
|
return (attributes) => redactWith(attributes, r);
|
|
708
709
|
}
|
|
709
710
|
|
|
711
|
+
// src/memory.ts
|
|
712
|
+
var import_node_perf_hooks = require("perf_hooks");
|
|
713
|
+
var import_node_v8 = require("v8");
|
|
714
|
+
var import_api = require("@opentelemetry/api");
|
|
715
|
+
function startMemoryMetrics() {
|
|
716
|
+
const meter = import_api.metrics.getMeter("autter-process-memory");
|
|
717
|
+
const rss = meter.createObservableGauge("autter.process.memory.rss", { unit: "By" });
|
|
718
|
+
const heapUsed = meter.createObservableGauge("autter.process.memory.heap.used", { unit: "By" });
|
|
719
|
+
const heapLimit = meter.createObservableGauge("autter.process.memory.heap.limit", { unit: "By" });
|
|
720
|
+
const containerLimit = meter.createObservableGauge("autter.process.memory.limit", { unit: "By" });
|
|
721
|
+
const afterGc = meter.createObservableGauge("autter.process.memory.heap.after_gc", { unit: "By" });
|
|
722
|
+
const gcCount = meter.createObservableCounter("autter.process.gc.count", { unit: "{collection}" });
|
|
723
|
+
const gcDuration = meter.createObservableCounter("autter.process.gc.duration", { unit: "ms" });
|
|
724
|
+
let collections = 0;
|
|
725
|
+
let durationMs = 0;
|
|
726
|
+
let lastAfterGc = null;
|
|
727
|
+
const callbacks = [
|
|
728
|
+
[rss, (result) => result.observe(process.memoryUsage().rss)],
|
|
729
|
+
[heapUsed, (result) => result.observe(process.memoryUsage().heapUsed)],
|
|
730
|
+
[heapLimit, (result) => result.observe((0, import_node_v8.getHeapStatistics)().heap_size_limit)],
|
|
731
|
+
[containerLimit, (result) => {
|
|
732
|
+
const constrained = process.constrainedMemory?.() ?? 0;
|
|
733
|
+
if (constrained > 0) result.observe(constrained);
|
|
734
|
+
}],
|
|
735
|
+
[afterGc, (result) => {
|
|
736
|
+
if (lastAfterGc !== null) result.observe(lastAfterGc);
|
|
737
|
+
}],
|
|
738
|
+
[gcCount, (result) => result.observe(collections)],
|
|
739
|
+
[gcDuration, (result) => result.observe(durationMs)]
|
|
740
|
+
];
|
|
741
|
+
for (const [instrument, callback] of callbacks) instrument.addCallback(callback);
|
|
742
|
+
const observer = new import_node_perf_hooks.PerformanceObserver((list) => {
|
|
743
|
+
for (const entry of list.getEntries()) {
|
|
744
|
+
collections++;
|
|
745
|
+
durationMs += entry.duration;
|
|
746
|
+
lastAfterGc = process.memoryUsage().heapUsed;
|
|
747
|
+
}
|
|
748
|
+
});
|
|
749
|
+
observer.observe({ entryTypes: ["gc"] });
|
|
750
|
+
return () => {
|
|
751
|
+
observer.disconnect();
|
|
752
|
+
for (const [instrument, callback] of callbacks) instrument.removeCallback(callback);
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
|
|
710
756
|
// src/server.ts
|
|
711
757
|
var LLM_MARKER_ATTRIBUTES = [
|
|
712
758
|
"gen_ai.operation.name",
|
|
@@ -769,14 +815,14 @@ var RETENTION_MAX_BUFFERED_SPANS = 5e3;
|
|
|
769
815
|
var RETENTION_SWEEP_INTERVAL_MS = 1e4;
|
|
770
816
|
var RETENTION_TRACE_TTL_MS = 3e4;
|
|
771
817
|
function spanIndicatesError(span) {
|
|
772
|
-
if (span.status.code ===
|
|
818
|
+
if (span.status.code === import_api2.SpanStatusCode.ERROR) return true;
|
|
773
819
|
return span.events.some((event) => event.name === "exception");
|
|
774
820
|
}
|
|
775
821
|
var TAIL_RETAINED_ATTR = "autter.tail_retained";
|
|
776
822
|
function withSampledFlag(span) {
|
|
777
823
|
const spanContext = {
|
|
778
824
|
...span.spanContext(),
|
|
779
|
-
traceFlags: span.spanContext().traceFlags |
|
|
825
|
+
traceFlags: span.spanContext().traceFlags | import_api2.TraceFlags.SAMPLED
|
|
780
826
|
};
|
|
781
827
|
return {
|
|
782
828
|
name: span.name,
|
|
@@ -816,11 +862,11 @@ var ErrorTraceRetentionProcessor = class {
|
|
|
816
862
|
}
|
|
817
863
|
onEnd(span) {
|
|
818
864
|
const ctx = span.spanContext();
|
|
819
|
-
if ((ctx.traceFlags &
|
|
865
|
+
if ((ctx.traceFlags & import_api2.TraceFlags.SAMPLED) !== 0) return;
|
|
820
866
|
const entry = this.entryFor(ctx.traceId);
|
|
821
867
|
if (entry.retained) {
|
|
822
868
|
this.forward(span);
|
|
823
|
-
} else if (this.retainErrors && spanIndicatesError(span) || this.slowThresholdMs > 0 && span.kind ===
|
|
869
|
+
} else if (this.retainErrors && spanIndicatesError(span) || this.slowThresholdMs > 0 && span.kind === import_api2.SpanKind.SERVER && span.duration[0] * 1e3 + span.duration[1] / 1e6 >= this.slowThresholdMs) {
|
|
824
870
|
entry.retained = true;
|
|
825
871
|
this.flush(entry);
|
|
826
872
|
this.forward(span);
|
|
@@ -835,8 +881,8 @@ var ErrorTraceRetentionProcessor = class {
|
|
|
835
881
|
}
|
|
836
882
|
/** Promote the active trace, if any — called on captured errors. */
|
|
837
883
|
retainActiveTrace() {
|
|
838
|
-
const active2 =
|
|
839
|
-
if (!active2 || (active2.traceFlags &
|
|
884
|
+
const active2 = import_api2.trace.getActiveSpan()?.spanContext();
|
|
885
|
+
if (!active2 || (active2.traceFlags & import_api2.TraceFlags.SAMPLED) !== 0) return;
|
|
840
886
|
const entry = this.entryFor(active2.traceId);
|
|
841
887
|
if (entry.retained) return;
|
|
842
888
|
entry.retained = true;
|
|
@@ -925,7 +971,7 @@ function usageAttributes(usage) {
|
|
|
925
971
|
}
|
|
926
972
|
async function runLlmSpan(tracer, info, fn) {
|
|
927
973
|
const span = tracer.startSpan(`${info.operation ?? "chat"} ${info.model}`, {
|
|
928
|
-
kind:
|
|
974
|
+
kind: import_api2.SpanKind.CLIENT,
|
|
929
975
|
attributes: llmBaseAttributes(info)
|
|
930
976
|
});
|
|
931
977
|
const setResponseModel = (model) => span.setAttribute("gen_ai.response.model", model);
|
|
@@ -937,11 +983,11 @@ async function runLlmSpan(tracer, info, fn) {
|
|
|
937
983
|
setAttributes: (attributes) => span.setAttributes(attributes)
|
|
938
984
|
};
|
|
939
985
|
try {
|
|
940
|
-
const result = await
|
|
941
|
-
|
|
986
|
+
const result = await import_api2.context.with(
|
|
987
|
+
import_api2.trace.setSpan(import_api2.context.active(), span),
|
|
942
988
|
() => fn(handle)
|
|
943
989
|
);
|
|
944
|
-
span.setStatus({ code:
|
|
990
|
+
span.setStatus({ code: import_api2.SpanStatusCode.OK });
|
|
945
991
|
return result;
|
|
946
992
|
} catch (err) {
|
|
947
993
|
if (err instanceof Error) {
|
|
@@ -949,7 +995,7 @@ async function runLlmSpan(tracer, info, fn) {
|
|
|
949
995
|
span.setAttribute("error.type", err.name);
|
|
950
996
|
}
|
|
951
997
|
span.setStatus({
|
|
952
|
-
code:
|
|
998
|
+
code: import_api2.SpanStatusCode.ERROR,
|
|
953
999
|
message: err instanceof Error ? err.message : String(err)
|
|
954
1000
|
});
|
|
955
1001
|
throw err;
|
|
@@ -964,7 +1010,7 @@ function recordLlmCall(tracer, call) {
|
|
|
964
1010
|
const span = tracer.startSpan(
|
|
965
1011
|
`${call.operation ?? "chat"} ${call.model}`,
|
|
966
1012
|
{
|
|
967
|
-
kind:
|
|
1013
|
+
kind: import_api2.SpanKind.CLIENT,
|
|
968
1014
|
startTime,
|
|
969
1015
|
attributes: { ...llmBaseAttributes(call), ...usageAttributes(call) }
|
|
970
1016
|
}
|
|
@@ -975,27 +1021,27 @@ function recordLlmCall(tracer, call) {
|
|
|
975
1021
|
span.setAttribute("error.type", call.error.name);
|
|
976
1022
|
}
|
|
977
1023
|
span.setStatus({
|
|
978
|
-
code:
|
|
1024
|
+
code: import_api2.SpanStatusCode.ERROR,
|
|
979
1025
|
message: call.error instanceof Error ? call.error.message : String(call.error)
|
|
980
1026
|
});
|
|
981
1027
|
} else {
|
|
982
|
-
span.setStatus({ code:
|
|
1028
|
+
span.setStatus({ code: import_api2.SpanStatusCode.OK });
|
|
983
1029
|
}
|
|
984
1030
|
span.end(endTime);
|
|
985
1031
|
}
|
|
986
1032
|
async function runWithSpan(tracer, name, fn, attributes) {
|
|
987
1033
|
const span = tracer.startSpan(name, { attributes });
|
|
988
1034
|
try {
|
|
989
|
-
const result = await
|
|
990
|
-
|
|
1035
|
+
const result = await import_api2.context.with(
|
|
1036
|
+
import_api2.trace.setSpan(import_api2.context.active(), span),
|
|
991
1037
|
fn
|
|
992
1038
|
);
|
|
993
|
-
span.setStatus({ code:
|
|
1039
|
+
span.setStatus({ code: import_api2.SpanStatusCode.OK });
|
|
994
1040
|
return result;
|
|
995
1041
|
} catch (err) {
|
|
996
1042
|
if (err instanceof Error) span.recordException(err);
|
|
997
1043
|
span.setStatus({
|
|
998
|
-
code:
|
|
1044
|
+
code: import_api2.SpanStatusCode.ERROR,
|
|
999
1045
|
message: err instanceof Error ? err.message : String(err)
|
|
1000
1046
|
});
|
|
1001
1047
|
throw err;
|
|
@@ -1013,7 +1059,7 @@ function expressRouteOf(req) {
|
|
|
1013
1059
|
}
|
|
1014
1060
|
function captureExpressRoute(_span, response) {
|
|
1015
1061
|
if (!(response instanceof import_node_http.ServerResponse)) return;
|
|
1016
|
-
const rpcMetadata = (0, import_core2.getRPCMetadata)(
|
|
1062
|
+
const rpcMetadata = (0, import_core2.getRPCMetadata)(import_api2.context.active());
|
|
1017
1063
|
if (rpcMetadata?.type !== import_core2.RPCType.HTTP) return;
|
|
1018
1064
|
const setRoute = () => {
|
|
1019
1065
|
if (rpcMetadata.route) return;
|
|
@@ -1039,7 +1085,7 @@ function initAutterServer(options) {
|
|
|
1039
1085
|
activeRedactor = makeRedactor(options.redactAttributes ?? true);
|
|
1040
1086
|
const resource = new import_resources.Resource({
|
|
1041
1087
|
[import_semantic_conventions.ATTR_SERVICE_NAME]: options.service,
|
|
1042
|
-
"service.instance.id": (0, import_node_crypto.randomUUID)(),
|
|
1088
|
+
"service.instance.id": (options.instanceId || process.env.AUTTER_RUNTIME_INSTANCE_ID || (0, import_node_crypto.randomUUID)()).slice(0, 128),
|
|
1043
1089
|
...options.release ? { [import_semantic_conventions.ATTR_SERVICE_VERSION]: options.release } : {},
|
|
1044
1090
|
"deployment.environment": environment,
|
|
1045
1091
|
// Tells the ingester request metrics arrive on the metrics pipe, so
|
|
@@ -1104,6 +1150,7 @@ function initAutterServer(options) {
|
|
|
1104
1150
|
]
|
|
1105
1151
|
});
|
|
1106
1152
|
sdk.start();
|
|
1153
|
+
const stopMemoryMetrics = options.memoryMetrics === false ? null : startMemoryMetrics();
|
|
1107
1154
|
const alwaysOnProvider = new import_sdk_trace_base.BasicTracerProvider({
|
|
1108
1155
|
resource,
|
|
1109
1156
|
sampler: new import_sdk_trace_base.AlwaysOnSampler(),
|
|
@@ -1125,7 +1172,7 @@ function initAutterServer(options) {
|
|
|
1125
1172
|
const isError = error instanceof Error;
|
|
1126
1173
|
const message = isError ? error.message : String(error);
|
|
1127
1174
|
const redacted = activeRedactor(attributes);
|
|
1128
|
-
const activeSpan =
|
|
1175
|
+
const activeSpan = import_api2.trace.getActiveSpan();
|
|
1129
1176
|
const reuseActive = activeSpan?.isRecording() === true;
|
|
1130
1177
|
const span = reuseActive ? activeSpan : errorTracer.startSpan(isError ? error.name : "Error");
|
|
1131
1178
|
span.setAttributes({ "autter.severity": "error", ...redacted });
|
|
@@ -1150,7 +1197,7 @@ function initAutterServer(options) {
|
|
|
1150
1197
|
...redacted["autter.handled"] === true ? { "autter.handled": true, "autter.sampled": redacted["autter.sampled"] === true } : {}
|
|
1151
1198
|
});
|
|
1152
1199
|
}
|
|
1153
|
-
span.setStatus({ code:
|
|
1200
|
+
span.setStatus({ code: import_api2.SpanStatusCode.ERROR, message });
|
|
1154
1201
|
if (!reuseActive) span.end();
|
|
1155
1202
|
}
|
|
1156
1203
|
function captureMessage2(message, severity = "warning", attributes) {
|
|
@@ -1168,13 +1215,13 @@ function initAutterServer(options) {
|
|
|
1168
1215
|
...stack ? { "exception.stacktrace": stack } : {},
|
|
1169
1216
|
"autter.severity": severity
|
|
1170
1217
|
});
|
|
1171
|
-
span.setStatus({ code:
|
|
1218
|
+
span.setStatus({ code: import_api2.SpanStatusCode.ERROR, message });
|
|
1172
1219
|
span.end();
|
|
1173
1220
|
}
|
|
1174
1221
|
function reportOutcome2(name, message, attributes) {
|
|
1175
1222
|
errorTraceBuffer?.retainActiveTrace();
|
|
1176
1223
|
telemetryStats.markCaptured();
|
|
1177
|
-
const activeSpan =
|
|
1224
|
+
const activeSpan = import_api2.trace.getActiveSpan();
|
|
1178
1225
|
const reuseActive = activeSpan?.isRecording() === true;
|
|
1179
1226
|
const span = reuseActive ? activeSpan : errorTracer.startSpan("OutcomeFailure");
|
|
1180
1227
|
const safeOutcome = activeRedactor({ "autter.outcome.name": name.slice(0, 200), "autter.outcome.message": message.slice(0, 1e3) });
|
|
@@ -1186,7 +1233,7 @@ function initAutterServer(options) {
|
|
|
1186
1233
|
"autter.outcome.name": safeName,
|
|
1187
1234
|
"autter.outcome.message": safeMessage
|
|
1188
1235
|
});
|
|
1189
|
-
span.setStatus({ code:
|
|
1236
|
+
span.setStatus({ code: import_api2.SpanStatusCode.ERROR, message: safeMessage });
|
|
1190
1237
|
if (!reuseActive) span.end();
|
|
1191
1238
|
}
|
|
1192
1239
|
if (options.captureGlobalErrors !== false) {
|
|
@@ -1225,6 +1272,7 @@ function initAutterServer(options) {
|
|
|
1225
1272
|
withLlmCall: (info, fn) => runLlmSpan(llmTracer, info, fn),
|
|
1226
1273
|
trackLlmCall: (call) => recordLlmCall(llmTracer, call),
|
|
1227
1274
|
shutdown: async () => {
|
|
1275
|
+
stopMemoryMetrics?.();
|
|
1228
1276
|
active = null;
|
|
1229
1277
|
activeAlwaysOnProvider = null;
|
|
1230
1278
|
autoFlushHandle?.dispose();
|
|
@@ -1243,35 +1291,35 @@ function captureException(error, attributes) {
|
|
|
1243
1291
|
active.captureException(error, attributes);
|
|
1244
1292
|
return;
|
|
1245
1293
|
}
|
|
1246
|
-
const span =
|
|
1294
|
+
const span = import_api2.trace.getTracer("autter-errors").startSpan(error instanceof Error ? error.name : "Error", { attributes });
|
|
1247
1295
|
if (error instanceof Error) span.recordException(error);
|
|
1248
1296
|
span.setStatus({
|
|
1249
|
-
code:
|
|
1297
|
+
code: import_api2.SpanStatusCode.ERROR,
|
|
1250
1298
|
message: error instanceof Error ? error.message : String(error)
|
|
1251
1299
|
});
|
|
1252
1300
|
span.end();
|
|
1253
1301
|
}
|
|
1254
1302
|
function withProcessSpan(name, fn, attributes) {
|
|
1255
1303
|
if (active) return active.withProcessSpan(name, fn, attributes);
|
|
1256
|
-
return runWithSpan(
|
|
1304
|
+
return runWithSpan(import_api2.trace.getTracer("autter-processes"), name, fn, attributes);
|
|
1257
1305
|
}
|
|
1258
1306
|
function withLlmCall(info, fn) {
|
|
1259
1307
|
if (active) return active.withLlmCall(info, fn);
|
|
1260
|
-
return runLlmSpan(
|
|
1308
|
+
return runLlmSpan(import_api2.trace.getTracer("autter-llm"), info, fn);
|
|
1261
1309
|
}
|
|
1262
1310
|
function trackLlmCall(call) {
|
|
1263
1311
|
if (active) {
|
|
1264
1312
|
active.trackLlmCall(call);
|
|
1265
1313
|
return;
|
|
1266
1314
|
}
|
|
1267
|
-
recordLlmCall(
|
|
1315
|
+
recordLlmCall(import_api2.trace.getTracer("autter-llm"), call);
|
|
1268
1316
|
}
|
|
1269
1317
|
function captureMessage(message, severity = "warning", attributes) {
|
|
1270
1318
|
if (active) {
|
|
1271
1319
|
active.captureMessage(message, severity, attributes);
|
|
1272
1320
|
return;
|
|
1273
1321
|
}
|
|
1274
|
-
const span =
|
|
1322
|
+
const span = import_api2.trace.getTracer("autter-errors").startSpan("Message", {
|
|
1275
1323
|
attributes: { "autter.severity": severity, ...attributes }
|
|
1276
1324
|
});
|
|
1277
1325
|
span.addEvent("exception", {
|
|
@@ -1279,12 +1327,12 @@ function captureMessage(message, severity = "warning", attributes) {
|
|
|
1279
1327
|
"exception.message": message,
|
|
1280
1328
|
"autter.severity": severity
|
|
1281
1329
|
});
|
|
1282
|
-
span.setStatus({ code:
|
|
1330
|
+
span.setStatus({ code: import_api2.SpanStatusCode.ERROR, message });
|
|
1283
1331
|
span.end();
|
|
1284
1332
|
}
|
|
1285
1333
|
function reportOutcome(name, message, attributes) {
|
|
1286
1334
|
if (active) return active.reportOutcome(name, message, attributes);
|
|
1287
|
-
const span =
|
|
1335
|
+
const span = import_api2.trace.getTracer("autter-outcomes").startSpan("OutcomeFailure");
|
|
1288
1336
|
const safeOutcome = redactAttributes({ "autter.outcome.name": name.slice(0, 200), "autter.outcome.message": message.slice(0, 1e3) });
|
|
1289
1337
|
const safeName = String(safeOutcome["autter.outcome.name"] ?? "operation");
|
|
1290
1338
|
const safeMessage = String(safeOutcome["autter.outcome.message"] ?? "failed");
|
|
@@ -1294,7 +1342,7 @@ function reportOutcome(name, message, attributes) {
|
|
|
1294
1342
|
"autter.outcome.name": safeName,
|
|
1295
1343
|
"autter.outcome.message": safeMessage
|
|
1296
1344
|
});
|
|
1297
|
-
span.setStatus({ code:
|
|
1345
|
+
span.setStatus({ code: import_api2.SpanStatusCode.ERROR, message: safeMessage });
|
|
1298
1346
|
span.end();
|
|
1299
1347
|
}
|
|
1300
1348
|
function makeSafeCapture(options) {
|
|
@@ -1305,7 +1353,7 @@ function makeSafeCapture(options) {
|
|
|
1305
1353
|
};
|
|
1306
1354
|
}
|
|
1307
1355
|
function alwaysOnTracer(name) {
|
|
1308
|
-
return activeAlwaysOnProvider ? activeAlwaysOnProvider.getTracer(name) :
|
|
1356
|
+
return activeAlwaysOnProvider ? activeAlwaysOnProvider.getTracer(name) : import_api2.trace.getTracer(name);
|
|
1309
1357
|
}
|
|
1310
1358
|
function autterLlmTracer() {
|
|
1311
1359
|
return alwaysOnTracer("autter-llm");
|
|
@@ -1325,7 +1373,7 @@ async function emitLlmSelftestTrace() {
|
|
|
1325
1373
|
attributes: { "autter.selftest": true }
|
|
1326
1374
|
},
|
|
1327
1375
|
(llm) => {
|
|
1328
|
-
traceId =
|
|
1376
|
+
traceId = import_api2.trace.getActiveSpan()?.spanContext().traceId ?? "";
|
|
1329
1377
|
llm.setUsage({ inputTokens: 1, outputTokens: 1 });
|
|
1330
1378
|
llm.setCost(0);
|
|
1331
1379
|
}
|
|
@@ -1335,7 +1383,7 @@ async function emitLlmSelftestTrace() {
|
|
|
1335
1383
|
}
|
|
1336
1384
|
|
|
1337
1385
|
// src/llm-instrument.ts
|
|
1338
|
-
var
|
|
1386
|
+
var import_api3 = require("@opentelemetry/api");
|
|
1339
1387
|
var CALL_METHODS = /* @__PURE__ */ new Set([
|
|
1340
1388
|
"create",
|
|
1341
1389
|
// openai chat/completions/embeddings/responses, anthropic messages
|
|
@@ -1393,7 +1441,7 @@ function applyUsage(span, usage) {
|
|
|
1393
1441
|
function endWithError(span, err) {
|
|
1394
1442
|
if (err instanceof Error) span.recordException(err);
|
|
1395
1443
|
span.setStatus({
|
|
1396
|
-
code:
|
|
1444
|
+
code: import_api3.SpanStatusCode.ERROR,
|
|
1397
1445
|
message: err instanceof Error ? err.message : String(err)
|
|
1398
1446
|
});
|
|
1399
1447
|
span.end();
|
|
@@ -1413,7 +1461,7 @@ function wrapStream(stream, span) {
|
|
|
1413
1461
|
if (err !== void 0) {
|
|
1414
1462
|
endWithError(span, err);
|
|
1415
1463
|
} else {
|
|
1416
|
-
span.setStatus({ code:
|
|
1464
|
+
span.setStatus({ code: import_api3.SpanStatusCode.OK });
|
|
1417
1465
|
span.end();
|
|
1418
1466
|
}
|
|
1419
1467
|
};
|
|
@@ -1468,7 +1516,7 @@ function instrumentLlmClient(client, options) {
|
|
|
1468
1516
|
const operation = /embed/i.test(callPath) ? "embeddings" : "chat";
|
|
1469
1517
|
const userId = resolveUserId();
|
|
1470
1518
|
const span = autterLlmTracer().startSpan(`${operation} ${request.model}`, {
|
|
1471
|
-
kind:
|
|
1519
|
+
kind: import_api3.SpanKind.CLIENT,
|
|
1472
1520
|
attributes: {
|
|
1473
1521
|
"gen_ai.operation.name": operation,
|
|
1474
1522
|
"gen_ai.provider.name": provider,
|
|
@@ -1490,7 +1538,7 @@ function instrumentLlmClient(client, options) {
|
|
|
1490
1538
|
return wrapStream(result, span);
|
|
1491
1539
|
}
|
|
1492
1540
|
applyUsage(span, extractUsage(result));
|
|
1493
|
-
span.setStatus({ code:
|
|
1541
|
+
span.setStatus({ code: import_api3.SpanStatusCode.OK });
|
|
1494
1542
|
span.end();
|
|
1495
1543
|
return result;
|
|
1496
1544
|
}
|
|
@@ -1500,7 +1548,7 @@ function instrumentLlmClient(client, options) {
|
|
|
1500
1548
|
return wrapStream(resolved, span);
|
|
1501
1549
|
}
|
|
1502
1550
|
applyUsage(span, extractUsage(resolved));
|
|
1503
|
-
span.setStatus({ code:
|
|
1551
|
+
span.setStatus({ code: import_api3.SpanStatusCode.OK });
|
|
1504
1552
|
span.end();
|
|
1505
1553
|
return resolved;
|
|
1506
1554
|
},
|
package/dist/index.d.cts
CHANGED
|
@@ -100,6 +100,8 @@ interface AutterServerOptions {
|
|
|
100
100
|
/** Ingester base URL. Default: https://otlp.autter.dev */
|
|
101
101
|
endpoint?: string;
|
|
102
102
|
service: string;
|
|
103
|
+
/** Stable for this process lifetime; use an ECS task/container ID or pod UID plus restart count to correlate platform events. */
|
|
104
|
+
instanceId?: string;
|
|
103
105
|
environment?: string;
|
|
104
106
|
/** e.g. a git SHA. Maps to service.version / release. */
|
|
105
107
|
release?: string;
|
|
@@ -119,6 +121,8 @@ interface AutterServerOptions {
|
|
|
119
121
|
retainTracesAboveMs?: number;
|
|
120
122
|
/** Metric export interval. Default 60_000 ms. */
|
|
121
123
|
metricIntervalMs?: number;
|
|
124
|
+
/** Collect RSS, heap, limit, and GC signals per process instance. Default true. */
|
|
125
|
+
memoryMetrics?: boolean;
|
|
122
126
|
/** Capture crashing exceptions via process.uncaughtExceptionMonitor (default true). */
|
|
123
127
|
captureGlobalErrors?: boolean;
|
|
124
128
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -100,6 +100,8 @@ interface AutterServerOptions {
|
|
|
100
100
|
/** Ingester base URL. Default: https://otlp.autter.dev */
|
|
101
101
|
endpoint?: string;
|
|
102
102
|
service: string;
|
|
103
|
+
/** Stable for this process lifetime; use an ECS task/container ID or pod UID plus restart count to correlate platform events. */
|
|
104
|
+
instanceId?: string;
|
|
103
105
|
environment?: string;
|
|
104
106
|
/** e.g. a git SHA. Maps to service.version / release. */
|
|
105
107
|
release?: string;
|
|
@@ -119,6 +121,8 @@ interface AutterServerOptions {
|
|
|
119
121
|
retainTracesAboveMs?: number;
|
|
120
122
|
/** Metric export interval. Default 60_000 ms. */
|
|
121
123
|
metricIntervalMs?: number;
|
|
124
|
+
/** Collect RSS, heap, limit, and GC signals per process instance. Default true. */
|
|
125
|
+
memoryMetrics?: boolean;
|
|
122
126
|
/** Capture crashing exceptions via process.uncaughtExceptionMonitor (default true). */
|
|
123
127
|
captureGlobalErrors?: boolean;
|
|
124
128
|
/**
|
package/dist/index.js
CHANGED
|
@@ -32,6 +32,7 @@ var EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
|
32
32
|
"track_event",
|
|
33
33
|
"outcome",
|
|
34
34
|
"request_failure",
|
|
35
|
+
"csp_violation",
|
|
35
36
|
"timing"
|
|
36
37
|
]);
|
|
37
38
|
var SEVERITIES = /* @__PURE__ */ new Set(["fatal", "error", "warning", "info"]);
|
|
@@ -675,6 +676,51 @@ function makeRedactor(options) {
|
|
|
675
676
|
return (attributes) => redactWith(attributes, r);
|
|
676
677
|
}
|
|
677
678
|
|
|
679
|
+
// src/memory.ts
|
|
680
|
+
import { PerformanceObserver } from "perf_hooks";
|
|
681
|
+
import { getHeapStatistics } from "v8";
|
|
682
|
+
import { metrics } from "@opentelemetry/api";
|
|
683
|
+
function startMemoryMetrics() {
|
|
684
|
+
const meter = metrics.getMeter("autter-process-memory");
|
|
685
|
+
const rss = meter.createObservableGauge("autter.process.memory.rss", { unit: "By" });
|
|
686
|
+
const heapUsed = meter.createObservableGauge("autter.process.memory.heap.used", { unit: "By" });
|
|
687
|
+
const heapLimit = meter.createObservableGauge("autter.process.memory.heap.limit", { unit: "By" });
|
|
688
|
+
const containerLimit = meter.createObservableGauge("autter.process.memory.limit", { unit: "By" });
|
|
689
|
+
const afterGc = meter.createObservableGauge("autter.process.memory.heap.after_gc", { unit: "By" });
|
|
690
|
+
const gcCount = meter.createObservableCounter("autter.process.gc.count", { unit: "{collection}" });
|
|
691
|
+
const gcDuration = meter.createObservableCounter("autter.process.gc.duration", { unit: "ms" });
|
|
692
|
+
let collections = 0;
|
|
693
|
+
let durationMs = 0;
|
|
694
|
+
let lastAfterGc = null;
|
|
695
|
+
const callbacks = [
|
|
696
|
+
[rss, (result) => result.observe(process.memoryUsage().rss)],
|
|
697
|
+
[heapUsed, (result) => result.observe(process.memoryUsage().heapUsed)],
|
|
698
|
+
[heapLimit, (result) => result.observe(getHeapStatistics().heap_size_limit)],
|
|
699
|
+
[containerLimit, (result) => {
|
|
700
|
+
const constrained = process.constrainedMemory?.() ?? 0;
|
|
701
|
+
if (constrained > 0) result.observe(constrained);
|
|
702
|
+
}],
|
|
703
|
+
[afterGc, (result) => {
|
|
704
|
+
if (lastAfterGc !== null) result.observe(lastAfterGc);
|
|
705
|
+
}],
|
|
706
|
+
[gcCount, (result) => result.observe(collections)],
|
|
707
|
+
[gcDuration, (result) => result.observe(durationMs)]
|
|
708
|
+
];
|
|
709
|
+
for (const [instrument, callback] of callbacks) instrument.addCallback(callback);
|
|
710
|
+
const observer = new PerformanceObserver((list) => {
|
|
711
|
+
for (const entry of list.getEntries()) {
|
|
712
|
+
collections++;
|
|
713
|
+
durationMs += entry.duration;
|
|
714
|
+
lastAfterGc = process.memoryUsage().heapUsed;
|
|
715
|
+
}
|
|
716
|
+
});
|
|
717
|
+
observer.observe({ entryTypes: ["gc"] });
|
|
718
|
+
return () => {
|
|
719
|
+
observer.disconnect();
|
|
720
|
+
for (const [instrument, callback] of callbacks) instrument.removeCallback(callback);
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
|
|
678
724
|
// src/server.ts
|
|
679
725
|
var LLM_MARKER_ATTRIBUTES = [
|
|
680
726
|
"gen_ai.operation.name",
|
|
@@ -1007,7 +1053,7 @@ function initAutterServer(options) {
|
|
|
1007
1053
|
activeRedactor = makeRedactor(options.redactAttributes ?? true);
|
|
1008
1054
|
const resource = new Resource({
|
|
1009
1055
|
[ATTR_SERVICE_NAME]: options.service,
|
|
1010
|
-
"service.instance.id": randomUUID(),
|
|
1056
|
+
"service.instance.id": (options.instanceId || process.env.AUTTER_RUNTIME_INSTANCE_ID || randomUUID()).slice(0, 128),
|
|
1011
1057
|
...options.release ? { [ATTR_SERVICE_VERSION]: options.release } : {},
|
|
1012
1058
|
"deployment.environment": environment,
|
|
1013
1059
|
// Tells the ingester request metrics arrive on the metrics pipe, so
|
|
@@ -1072,6 +1118,7 @@ function initAutterServer(options) {
|
|
|
1072
1118
|
]
|
|
1073
1119
|
});
|
|
1074
1120
|
sdk.start();
|
|
1121
|
+
const stopMemoryMetrics = options.memoryMetrics === false ? null : startMemoryMetrics();
|
|
1075
1122
|
const alwaysOnProvider = new BasicTracerProvider({
|
|
1076
1123
|
resource,
|
|
1077
1124
|
sampler: new AlwaysOnSampler(),
|
|
@@ -1193,6 +1240,7 @@ function initAutterServer(options) {
|
|
|
1193
1240
|
withLlmCall: (info, fn) => runLlmSpan(llmTracer, info, fn),
|
|
1194
1241
|
trackLlmCall: (call) => recordLlmCall(llmTracer, call),
|
|
1195
1242
|
shutdown: async () => {
|
|
1243
|
+
stopMemoryMetrics?.();
|
|
1196
1244
|
active = null;
|
|
1197
1245
|
activeAlwaysOnProvider = null;
|
|
1198
1246
|
autoFlushHandle?.dispose();
|
package/package.json
CHANGED