@farzanhossans/agentlens 0.2.0 → 0.2.1
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/index.d.mts +32 -4
- package/dist/index.d.ts +32 -4
- package/dist/index.js +99 -46
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +94 -46
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { getCurrentSpanId, getCurrentTrace, getCurrentTraceId, runWithTrace } from '@farzanhossans/agentlens-core';
|
|
2
2
|
|
|
3
3
|
type ParserName = 'openai' | 'anthropic' | 'gemini' | 'cohere' | 'mistral';
|
|
4
|
+
type SpanStatus = 'success' | 'error' | 'timeout';
|
|
4
5
|
interface LLMEndpoint {
|
|
5
6
|
provider: string;
|
|
6
7
|
parser: ParserName;
|
|
@@ -20,19 +21,46 @@ interface ParsedSpan {
|
|
|
20
21
|
}
|
|
21
22
|
interface AgentLensConfig {
|
|
22
23
|
apiKey: string;
|
|
24
|
+
/**
|
|
25
|
+
* Project UUID. Required by the AgentLens ingest contract — every span
|
|
26
|
+
* carries this so the server can route to the right project.
|
|
27
|
+
*/
|
|
28
|
+
projectId: string;
|
|
23
29
|
endpoint?: string;
|
|
24
30
|
debug?: boolean;
|
|
25
31
|
pii?: boolean;
|
|
26
32
|
flushIntervalMs?: number;
|
|
27
33
|
maxBatchSize?: number;
|
|
28
34
|
}
|
|
29
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Wire-format span sent to the AgentLens ingest endpoint. Matches the
|
|
37
|
+
* server-side Zod schema used by both the API controller and the
|
|
38
|
+
* Cloudflare ingest worker.
|
|
39
|
+
*/
|
|
40
|
+
interface OutboundSpan {
|
|
30
41
|
spanId: string;
|
|
31
42
|
traceId: string;
|
|
32
43
|
parentSpanId?: string;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
44
|
+
projectId: string;
|
|
45
|
+
/** Human-friendly span name (e.g. "openai.chat", "anthropic.messages"). */
|
|
46
|
+
name: string;
|
|
47
|
+
model: string;
|
|
48
|
+
provider: string;
|
|
49
|
+
/** Raw LLM prompt text (PII-scrubbed if pii=true). */
|
|
50
|
+
input: string;
|
|
51
|
+
/** Raw LLM completion text (PII-scrubbed if pii=true). */
|
|
52
|
+
output: string;
|
|
53
|
+
inputTokens: number;
|
|
54
|
+
outputTokens: number;
|
|
55
|
+
totalTokens: number;
|
|
56
|
+
costUsd: number;
|
|
57
|
+
latencyMs: number;
|
|
58
|
+
status: SpanStatus;
|
|
59
|
+
errorMessage?: string;
|
|
60
|
+
metadata: Record<string, unknown>;
|
|
61
|
+
startedAt: string;
|
|
62
|
+
endedAt: string;
|
|
63
|
+
isStream: boolean;
|
|
36
64
|
}
|
|
37
65
|
|
|
38
66
|
declare const LLM_REGISTRY: Record<string, LLMEndpoint>;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { getCurrentSpanId, getCurrentTrace, getCurrentTraceId, runWithTrace } from '@farzanhossans/agentlens-core';
|
|
2
2
|
|
|
3
3
|
type ParserName = 'openai' | 'anthropic' | 'gemini' | 'cohere' | 'mistral';
|
|
4
|
+
type SpanStatus = 'success' | 'error' | 'timeout';
|
|
4
5
|
interface LLMEndpoint {
|
|
5
6
|
provider: string;
|
|
6
7
|
parser: ParserName;
|
|
@@ -20,19 +21,46 @@ interface ParsedSpan {
|
|
|
20
21
|
}
|
|
21
22
|
interface AgentLensConfig {
|
|
22
23
|
apiKey: string;
|
|
24
|
+
/**
|
|
25
|
+
* Project UUID. Required by the AgentLens ingest contract — every span
|
|
26
|
+
* carries this so the server can route to the right project.
|
|
27
|
+
*/
|
|
28
|
+
projectId: string;
|
|
23
29
|
endpoint?: string;
|
|
24
30
|
debug?: boolean;
|
|
25
31
|
pii?: boolean;
|
|
26
32
|
flushIntervalMs?: number;
|
|
27
33
|
maxBatchSize?: number;
|
|
28
34
|
}
|
|
29
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Wire-format span sent to the AgentLens ingest endpoint. Matches the
|
|
37
|
+
* server-side Zod schema used by both the API controller and the
|
|
38
|
+
* Cloudflare ingest worker.
|
|
39
|
+
*/
|
|
40
|
+
interface OutboundSpan {
|
|
30
41
|
spanId: string;
|
|
31
42
|
traceId: string;
|
|
32
43
|
parentSpanId?: string;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
44
|
+
projectId: string;
|
|
45
|
+
/** Human-friendly span name (e.g. "openai.chat", "anthropic.messages"). */
|
|
46
|
+
name: string;
|
|
47
|
+
model: string;
|
|
48
|
+
provider: string;
|
|
49
|
+
/** Raw LLM prompt text (PII-scrubbed if pii=true). */
|
|
50
|
+
input: string;
|
|
51
|
+
/** Raw LLM completion text (PII-scrubbed if pii=true). */
|
|
52
|
+
output: string;
|
|
53
|
+
inputTokens: number;
|
|
54
|
+
outputTokens: number;
|
|
55
|
+
totalTokens: number;
|
|
56
|
+
costUsd: number;
|
|
57
|
+
latencyMs: number;
|
|
58
|
+
status: SpanStatus;
|
|
59
|
+
errorMessage?: string;
|
|
60
|
+
metadata: Record<string, unknown>;
|
|
61
|
+
startedAt: string;
|
|
62
|
+
endedAt: string;
|
|
63
|
+
isStream: boolean;
|
|
36
64
|
}
|
|
37
65
|
|
|
38
66
|
declare const LLM_REGISTRY: Record<string, LLMEndpoint>;
|
package/dist/index.js
CHANGED
|
@@ -2,13 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
var crypto = require('crypto');
|
|
4
4
|
var agentlensCore = require('@farzanhossans/agentlens-core');
|
|
5
|
+
var http = require('http');
|
|
6
|
+
var https = require('https');
|
|
5
7
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
|
+
|
|
10
|
+
var http__default = /*#__PURE__*/_interopDefault(http);
|
|
11
|
+
var https__default = /*#__PURE__*/_interopDefault(https);
|
|
12
|
+
|
|
13
|
+
// src/index.ts
|
|
12
14
|
|
|
13
15
|
// src/registry.ts
|
|
14
16
|
var LLM_REGISTRY = {
|
|
@@ -345,25 +347,40 @@ function patchFetch(transport2) {
|
|
|
345
347
|
patched.__agentlens_patched = true;
|
|
346
348
|
globalThis.fetch = patched;
|
|
347
349
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
+
var responseChunkRegistry = /* @__PURE__ */ new WeakMap();
|
|
351
|
+
var incomingMessagePatched = false;
|
|
350
352
|
function patchHttps(transport2) {
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
try {
|
|
355
|
-
patchModule("https", transport2);
|
|
356
|
-
patchModule("http", transport2);
|
|
357
|
-
} catch {
|
|
358
|
-
}
|
|
353
|
+
patchIncomingMessage();
|
|
354
|
+
patchModule(http__default.default, "http", transport2);
|
|
355
|
+
patchModule(https__default.default, "https", transport2);
|
|
359
356
|
}
|
|
360
|
-
function
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
357
|
+
function patchIncomingMessage() {
|
|
358
|
+
if (incomingMessagePatched) return;
|
|
359
|
+
const IncomingMessage = http__default.default.IncomingMessage;
|
|
360
|
+
if (!IncomingMessage?.prototype) return;
|
|
361
|
+
const proto = IncomingMessage.prototype;
|
|
362
|
+
const originalPush = proto.push;
|
|
363
|
+
proto.push = function patchedPush(chunk, encoding) {
|
|
364
|
+
if (chunk !== null) {
|
|
365
|
+
const chunks = responseChunkRegistry.get(this);
|
|
366
|
+
if (chunks) {
|
|
367
|
+
try {
|
|
368
|
+
if (Buffer.isBuffer(chunk)) {
|
|
369
|
+
chunks.push(chunk);
|
|
370
|
+
} else if (typeof chunk === "string") {
|
|
371
|
+
chunks.push(Buffer.from(chunk, encoding ?? "utf8"));
|
|
372
|
+
} else if (chunk && typeof chunk === "object") {
|
|
373
|
+
chunks.push(Buffer.from(chunk));
|
|
374
|
+
}
|
|
375
|
+
} catch {
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return originalPush.call(this, chunk, encoding);
|
|
380
|
+
};
|
|
381
|
+
incomingMessagePatched = true;
|
|
382
|
+
}
|
|
383
|
+
function patchModule(mod, name, transport2) {
|
|
367
384
|
if (mod.__agentlens_patched) return;
|
|
368
385
|
const original = mod.request.bind(mod);
|
|
369
386
|
mod.request = function patchedRequest(...args) {
|
|
@@ -371,7 +388,6 @@ function patchModule(name, transport2) {
|
|
|
371
388
|
if (!ctx) return original(...args);
|
|
372
389
|
const start = Date.now();
|
|
373
390
|
const requestChunks = [];
|
|
374
|
-
const responseChunks = [];
|
|
375
391
|
const req = original(...args);
|
|
376
392
|
const originalWrite = req.write.bind(req);
|
|
377
393
|
req.write = function(chunk, ...rest) {
|
|
@@ -383,16 +399,12 @@ function patchModule(name, transport2) {
|
|
|
383
399
|
};
|
|
384
400
|
req.on("response", (...resArgs) => {
|
|
385
401
|
const res = resArgs[0];
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
responseChunks.push(toBuffer(chunk));
|
|
389
|
-
} catch {
|
|
390
|
-
}
|
|
391
|
-
});
|
|
402
|
+
const chunks = [];
|
|
403
|
+
responseChunkRegistry.set(res, chunks);
|
|
392
404
|
res.on("end", () => {
|
|
393
405
|
const latency = Date.now() - start;
|
|
394
406
|
const requestText = Buffer.concat(requestChunks).toString("utf8");
|
|
395
|
-
const responseText = Buffer.concat(
|
|
407
|
+
const responseText = Buffer.concat(chunks).toString("utf8");
|
|
396
408
|
const requestBody = safeParse(requestText);
|
|
397
409
|
const responseBody = safeParse(responseText);
|
|
398
410
|
if (responseBody) {
|
|
@@ -406,6 +418,7 @@ function patchModule(name, transport2) {
|
|
|
406
418
|
isStream: requestBody?.stream === true
|
|
407
419
|
});
|
|
408
420
|
}
|
|
421
|
+
responseChunkRegistry.delete(res);
|
|
409
422
|
});
|
|
410
423
|
});
|
|
411
424
|
req.on("error", (...errArgs) => {
|
|
@@ -839,6 +852,7 @@ var Transport = class {
|
|
|
839
852
|
constructor(config) {
|
|
840
853
|
this.config = {
|
|
841
854
|
apiKey: config.apiKey,
|
|
855
|
+
projectId: config.projectId,
|
|
842
856
|
endpoint: config.endpoint,
|
|
843
857
|
flushIntervalMs: config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS,
|
|
844
858
|
maxBatchSize: config.maxBatchSize ?? DEFAULT_MAX_BATCH_SIZE,
|
|
@@ -849,6 +863,8 @@ var Transport = class {
|
|
|
849
863
|
this.registerExitHandler();
|
|
850
864
|
}
|
|
851
865
|
push(payload) {
|
|
866
|
+
const startedAt = new Date(Date.now() - payload.latency).toISOString();
|
|
867
|
+
const endedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
852
868
|
let parsed;
|
|
853
869
|
try {
|
|
854
870
|
parsed = parseSpan({
|
|
@@ -863,28 +879,34 @@ var Transport = class {
|
|
|
863
879
|
}
|
|
864
880
|
return;
|
|
865
881
|
}
|
|
866
|
-
const outbound = this.toOutbound(parsed, payload
|
|
882
|
+
const outbound = this.toOutbound(parsed, payload, startedAt, endedAt);
|
|
867
883
|
this.enqueue(outbound);
|
|
868
884
|
}
|
|
869
885
|
pushError(payload) {
|
|
870
886
|
const ids = this.resolveIds();
|
|
887
|
+
const startedAt = new Date(Date.now() - payload.latency).toISOString();
|
|
888
|
+
const endedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
871
889
|
const span = {
|
|
872
890
|
spanId: ids.spanId,
|
|
873
891
|
traceId: ids.traceId,
|
|
874
892
|
parentSpanId: ids.parentSpanId,
|
|
893
|
+
projectId: this.config.projectId,
|
|
894
|
+
name: `${payload.provider}.error`,
|
|
875
895
|
model: "unknown",
|
|
876
896
|
provider: payload.provider,
|
|
897
|
+
input: this.scrub(this.stringifyRequest(payload.request)),
|
|
898
|
+
output: "",
|
|
877
899
|
inputTokens: 0,
|
|
878
900
|
outputTokens: 0,
|
|
879
901
|
totalTokens: 0,
|
|
880
902
|
costUsd: 0,
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
903
|
+
latencyMs: payload.latency,
|
|
904
|
+
status: "error",
|
|
905
|
+
errorMessage: payload.error,
|
|
906
|
+
metadata: { httpStatus: 0 },
|
|
907
|
+
startedAt,
|
|
908
|
+
endedAt,
|
|
909
|
+
isStream: false
|
|
888
910
|
};
|
|
889
911
|
this.enqueue(span);
|
|
890
912
|
}
|
|
@@ -906,18 +928,30 @@ var Transport = class {
|
|
|
906
928
|
this.timer = null;
|
|
907
929
|
}
|
|
908
930
|
}
|
|
909
|
-
toOutbound(parsed,
|
|
931
|
+
toOutbound(parsed, payload, startedAt, endedAt) {
|
|
910
932
|
const ids = this.resolveIds();
|
|
933
|
+
const status = payload.status >= 200 && payload.status < 400 ? "success" : "error";
|
|
911
934
|
return {
|
|
912
|
-
...parsed,
|
|
913
935
|
spanId: ids.spanId,
|
|
914
936
|
traceId: ids.traceId,
|
|
915
937
|
parentSpanId: ids.parentSpanId,
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
938
|
+
projectId: this.config.projectId,
|
|
939
|
+
name: spanNameFor(payload),
|
|
940
|
+
model: parsed.model,
|
|
941
|
+
provider: parsed.provider,
|
|
942
|
+
input: this.scrub(parsed.inputText),
|
|
943
|
+
output: this.scrub(parsed.outputText),
|
|
944
|
+
inputTokens: parsed.inputTokens,
|
|
945
|
+
outputTokens: parsed.outputTokens,
|
|
946
|
+
totalTokens: parsed.totalTokens,
|
|
947
|
+
costUsd: parsed.costUsd,
|
|
948
|
+
latencyMs: payload.latency,
|
|
919
949
|
status,
|
|
920
|
-
|
|
950
|
+
errorMessage: parsed.error,
|
|
951
|
+
metadata: { httpStatus: payload.status },
|
|
952
|
+
startedAt,
|
|
953
|
+
endedAt,
|
|
954
|
+
isStream: parsed.isStream
|
|
921
955
|
};
|
|
922
956
|
}
|
|
923
957
|
/**
|
|
@@ -956,12 +990,13 @@ var Transport = class {
|
|
|
956
990
|
enqueue(span) {
|
|
957
991
|
if (this.config.debug) {
|
|
958
992
|
console.log("[agentlens] span", {
|
|
993
|
+
name: span.name,
|
|
959
994
|
model: span.model,
|
|
960
995
|
provider: span.provider,
|
|
961
996
|
inputTokens: span.inputTokens,
|
|
962
997
|
outputTokens: span.outputTokens,
|
|
963
998
|
costUsd: span.costUsd,
|
|
964
|
-
|
|
999
|
+
latencyMs: span.latencyMs,
|
|
965
1000
|
isStream: span.isStream
|
|
966
1001
|
});
|
|
967
1002
|
}
|
|
@@ -1022,6 +1057,9 @@ var Transport = class {
|
|
|
1022
1057
|
method: "POST",
|
|
1023
1058
|
headers: {
|
|
1024
1059
|
"content-type": "application/json",
|
|
1060
|
+
// Hosted ingest expects X-API-Key. Send Authorization too for any
|
|
1061
|
+
// gateways that may sit in front (some Cloudflare workers want it).
|
|
1062
|
+
"x-api-key": this.config.apiKey,
|
|
1025
1063
|
authorization: `Bearer ${this.config.apiKey}`
|
|
1026
1064
|
},
|
|
1027
1065
|
body
|
|
@@ -1029,6 +1067,17 @@ var Transport = class {
|
|
|
1029
1067
|
return { ok: res.ok, status: res.status };
|
|
1030
1068
|
}
|
|
1031
1069
|
};
|
|
1070
|
+
function spanNameFor(payload) {
|
|
1071
|
+
const req = payload.request;
|
|
1072
|
+
if (req && Array.isArray(req.messages)) return `${payload.provider}.chat`;
|
|
1073
|
+
if (req && (typeof req.prompt === "string" || typeof req.input === "string")) {
|
|
1074
|
+
return `${payload.provider}.completion`;
|
|
1075
|
+
}
|
|
1076
|
+
if (req && (req.input !== void 0 || req.contents !== void 0)) {
|
|
1077
|
+
return `${payload.provider}.embedding`;
|
|
1078
|
+
}
|
|
1079
|
+
return `${payload.provider}.call`;
|
|
1080
|
+
}
|
|
1032
1081
|
function sleep(ms) {
|
|
1033
1082
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1034
1083
|
}
|
|
@@ -1041,9 +1090,13 @@ var AgentLens = {
|
|
|
1041
1090
|
if (!config?.apiKey) {
|
|
1042
1091
|
throw new Error("AgentLens.init requires an apiKey");
|
|
1043
1092
|
}
|
|
1093
|
+
if (!config?.projectId) {
|
|
1094
|
+
throw new Error("AgentLens.init requires a projectId");
|
|
1095
|
+
}
|
|
1044
1096
|
initialized = true;
|
|
1045
1097
|
transport = new Transport({
|
|
1046
1098
|
apiKey: config.apiKey,
|
|
1099
|
+
projectId: config.projectId,
|
|
1047
1100
|
endpoint: config.endpoint ?? DEFAULT_ENDPOINT,
|
|
1048
1101
|
debug: config.debug ?? false,
|
|
1049
1102
|
pii: config.pii ?? true,
|