@lmnr-ai/lmnr 0.8.50 → 0.8.52
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/cli.cjs +2 -2
- package/dist/cli.d.cts +1 -1
- package/dist/cli.d.mts +1 -1
- package/dist/cli.mjs +2 -2
- package/dist/{evaluations-C8cI_XIE.d.cts → evaluations-CgcBhNjN.d.cts} +43 -4
- package/dist/{evaluations-BVaPPf0j.d.mts → evaluations-GnmUSvoK.d.mts} +43 -4
- package/dist/{file-utils-RXJzJyiI.cjs → file-utils-C_q3Dsf2.cjs} +2 -2
- package/dist/{file-utils-RXJzJyiI.cjs.map → file-utils-C_q3Dsf2.cjs.map} +1 -1
- package/dist/{file-utils-DOEv6ccL.mjs → file-utils-DaZefKLb.mjs} +2 -2
- package/dist/{file-utils-DOEv6ccL.mjs.map → file-utils-DaZefKLb.mjs.map} +1 -1
- package/dist/index.cjs +2750 -768
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12120 -10285
- package/dist/index.d.mts +12120 -10285
- package/dist/index.mjs +2747 -765
- package/dist/index.mjs.map +1 -1
- package/dist/{utils-DRrXCfun.mjs → utils-C6gt-rSg.mjs} +17 -3
- package/dist/utils-C6gt-rSg.mjs.map +1 -0
- package/dist/{utils-CNR1tzh5.cjs → utils-vnm5E0kk.cjs} +17 -3
- package/dist/utils-vnm5E0kk.cjs.map +1 -0
- package/package.json +16 -15
- package/dist/utils-CNR1tzh5.cjs.map +0 -1
- package/dist/utils-DRrXCfun.mjs.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_rolldown_runtime = require("./rolldown-runtime-Ctb5zv51.cjs");
|
|
3
|
-
const require_utils = require("./utils-
|
|
3
|
+
const require_utils = require("./utils-vnm5E0kk.cjs");
|
|
4
4
|
const require_consts = require("./consts-9rSS0oeR.cjs");
|
|
5
5
|
let path = require("path");
|
|
6
6
|
path = require_rolldown_runtime.__toESM(path);
|
|
@@ -10,12 +10,9 @@ let _opentelemetry_instrumentation = require("@opentelemetry/instrumentation");
|
|
|
10
10
|
let _opentelemetry_sdk_trace_base = require("@opentelemetry/sdk-trace-base");
|
|
11
11
|
let _opentelemetry_sdk_trace_node = require("@opentelemetry/sdk-trace-node");
|
|
12
12
|
let _opentelemetry_semantic_conventions = require("@opentelemetry/semantic-conventions");
|
|
13
|
-
let _traceloop_instrumentation_anthropic = require("@traceloop/instrumentation-anthropic");
|
|
14
13
|
let _traceloop_instrumentation_bedrock = require("@traceloop/instrumentation-bedrock");
|
|
15
14
|
let _traceloop_instrumentation_chromadb = require("@traceloop/instrumentation-chromadb");
|
|
16
|
-
let _traceloop_instrumentation_cohere = require("@traceloop/instrumentation-cohere");
|
|
17
15
|
let _traceloop_instrumentation_langchain = require("@traceloop/instrumentation-langchain");
|
|
18
|
-
let _traceloop_instrumentation_openai = require("@traceloop/instrumentation-openai");
|
|
19
16
|
let _traceloop_instrumentation_pinecone = require("@traceloop/instrumentation-pinecone");
|
|
20
17
|
let _traceloop_instrumentation_qdrant = require("@traceloop/instrumentation-qdrant");
|
|
21
18
|
let _traceloop_instrumentation_together = require("@traceloop/instrumentation-together");
|
|
@@ -68,6 +65,10 @@ var EvaluationDataset = class {
|
|
|
68
65
|
for (let i = Math.max(start, 0); i < Math.min(end, await this.size()); i++) result.push(await this.get(i));
|
|
69
66
|
return result;
|
|
70
67
|
}
|
|
68
|
+
async *[Symbol.asyncIterator]() {
|
|
69
|
+
const size = await this.size();
|
|
70
|
+
for (let i = 0; i < size; i++) yield await this.get(i);
|
|
71
|
+
}
|
|
71
72
|
sourceDataset() {}
|
|
72
73
|
take(n) {
|
|
73
74
|
return new Transformed(this, async (base) => {
|
|
@@ -144,14 +145,14 @@ var LaminarDataset = class extends EvaluationDataset {
|
|
|
144
145
|
fetchPage(offset) {
|
|
145
146
|
const existing = this.pages.get(offset);
|
|
146
147
|
if (existing) return existing;
|
|
147
|
-
const pending = this.
|
|
148
|
+
const pending = this.pullPage(offset).catch((err) => {
|
|
148
149
|
this.pages.delete(offset);
|
|
149
150
|
throw err;
|
|
150
151
|
});
|
|
151
152
|
this.pages.set(offset, pending);
|
|
152
153
|
return pending;
|
|
153
154
|
}
|
|
154
|
-
async
|
|
155
|
+
async pullPage(offset) {
|
|
155
156
|
if (!this.client) throw new Error("Client not set");
|
|
156
157
|
const identifier = this.id ? { id: this.id } : { name: this.name };
|
|
157
158
|
const resp = await this.client.datasets.pull({
|
|
@@ -175,6 +176,17 @@ var LaminarDataset = class extends EvaluationDataset {
|
|
|
175
176
|
if (local >= page.length) throw new Error(`Index ${index} is out of range for dataset of size ${this.len ?? "unknown"}`);
|
|
176
177
|
return page[local];
|
|
177
178
|
}
|
|
179
|
+
async *[Symbol.asyncIterator]() {
|
|
180
|
+
let offset = 0;
|
|
181
|
+
let yielded = 0;
|
|
182
|
+
while (this.len === null || yielded < this.len) {
|
|
183
|
+
const page = await (this.pages.get(offset) ?? this.pullPage(offset));
|
|
184
|
+
if (page.length === 0) return;
|
|
185
|
+
yield* page;
|
|
186
|
+
yielded += page.length;
|
|
187
|
+
offset += this.fetchSize;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
178
190
|
/**
|
|
179
191
|
* Push data from files to this dataset.
|
|
180
192
|
*
|
|
@@ -183,7 +195,7 @@ var LaminarDataset = class extends EvaluationDataset {
|
|
|
183
195
|
*/
|
|
184
196
|
async push(paths, recursive = false) {
|
|
185
197
|
if (!this.client) throw new Error("Client not set");
|
|
186
|
-
const { loadFromPaths } = await Promise.resolve().then(() => require("./file-utils-
|
|
198
|
+
const { loadFromPaths } = await Promise.resolve().then(() => require("./file-utils-C_q3Dsf2.cjs")).then((n) => n.file_utils_exports);
|
|
187
199
|
const data = await loadFromPaths(Array.isArray(paths) ? paths : [paths], recursive);
|
|
188
200
|
if (data.length === 0) {
|
|
189
201
|
console.warn("No data to push. Skipping");
|
|
@@ -199,7 +211,7 @@ var LaminarDataset = class extends EvaluationDataset {
|
|
|
199
211
|
};
|
|
200
212
|
//#endregion
|
|
201
213
|
//#region src/opentelemetry-lib/tracing/compat.ts
|
|
202
|
-
const logger$
|
|
214
|
+
const logger$30 = require_utils.initializeLogger();
|
|
203
215
|
const makeSpanOtelV2Compatible = (span) => {
|
|
204
216
|
const spanAny = span;
|
|
205
217
|
if (spanAny.instrumentationScope && !spanAny.instrumentationLibrary) Object.assign(span, { instrumentationLibrary: spanAny.instrumentationScope });
|
|
@@ -245,7 +257,7 @@ const createResource = (attributes) => {
|
|
|
245
257
|
if (resources.Resource) return new resources.Resource(attributes);
|
|
246
258
|
throw new Error("Unable to create Resource: neither v1 nor v2 API found");
|
|
247
259
|
} catch {
|
|
248
|
-
logger$
|
|
260
|
+
logger$30.debug("Laminar failed to @opentelemetry/resources, continuing without it");
|
|
249
261
|
return;
|
|
250
262
|
}
|
|
251
263
|
};
|
|
@@ -447,7 +459,7 @@ const consumeResponse = async (response) => {
|
|
|
447
459
|
};
|
|
448
460
|
//#endregion
|
|
449
461
|
//#region src/opentelemetry-lib/tracing/stream-utils.ts
|
|
450
|
-
const logger$
|
|
462
|
+
const logger$29 = require_utils.initializeLogger();
|
|
451
463
|
const pendingStreamProcessing = /* @__PURE__ */ new Set();
|
|
452
464
|
/**
|
|
453
465
|
* Wait for all pending stream processing to complete with a timeout
|
|
@@ -455,7 +467,7 @@ const pendingStreamProcessing = /* @__PURE__ */ new Set();
|
|
|
455
467
|
*/
|
|
456
468
|
const waitForPendingStreams = async (timeoutMs = 5e3) => {
|
|
457
469
|
if (pendingStreamProcessing.size === 0) return;
|
|
458
|
-
logger$
|
|
470
|
+
logger$29.debug(`Waiting for ${pendingStreamProcessing.size} pending stream(s)`);
|
|
459
471
|
const initialCount = pendingStreamProcessing.size;
|
|
460
472
|
const pendingPromises = Array.from(pendingStreamProcessing);
|
|
461
473
|
let timedOut = false;
|
|
@@ -466,7 +478,7 @@ const waitForPendingStreams = async (timeoutMs = 5e3) => {
|
|
|
466
478
|
}, timeoutMs);
|
|
467
479
|
});
|
|
468
480
|
await Promise.race([Promise.allSettled(pendingPromises), timeoutPromise]);
|
|
469
|
-
if (timedOut && pendingStreamProcessing.size > 0) logger$
|
|
481
|
+
if (timedOut && pendingStreamProcessing.size > 0) logger$29.warn(`Timeout waiting for ${pendingStreamProcessing.size} pending stream(s) after ${timeoutMs}ms (started with ${initialCount})`);
|
|
470
482
|
};
|
|
471
483
|
/**
|
|
472
484
|
* Track a background promise and ensure cleanup
|
|
@@ -482,34 +494,34 @@ const trackStreamProcessing = (task) => {
|
|
|
482
494
|
* while passing through the original result to the caller
|
|
483
495
|
*/
|
|
484
496
|
const handleStreamResult = (originalResult, streamInfo, span, ignoreOutput, serialize) => {
|
|
485
|
-
logger$
|
|
497
|
+
logger$29.debug(`Handling stream result of type: ${streamInfo.type}`);
|
|
486
498
|
if (streamInfo.type === "aisdk-result") {
|
|
487
499
|
trackStreamProcessing(async () => {
|
|
488
|
-
logger$
|
|
500
|
+
logger$29.debug("Starting background processing for AI SDK result");
|
|
489
501
|
let partialOutput = {};
|
|
490
502
|
try {
|
|
491
503
|
partialOutput = await consumeAISDKResult(streamInfo.result);
|
|
492
504
|
if (shouldSendTraces() && !ignoreOutput) try {
|
|
493
505
|
span.setAttribute(require_utils.SPAN_OUTPUT, serialize(partialOutput));
|
|
494
506
|
} catch (error) {
|
|
495
|
-
logger$
|
|
507
|
+
logger$29.warn("Failed to serialize AI SDK stream output: " + require_utils.errorMessage(error));
|
|
496
508
|
}
|
|
497
509
|
} catch (error) {
|
|
498
510
|
if (Object.keys(partialOutput).length > 0 && shouldSendTraces() && !ignoreOutput) try {
|
|
499
511
|
span.setAttribute(require_utils.SPAN_OUTPUT, serialize(partialOutput));
|
|
500
512
|
} catch (serError) {
|
|
501
|
-
logger$
|
|
513
|
+
logger$29.warn("Failed to serialize partial AI SDK output: " + require_utils.errorMessage(serError));
|
|
502
514
|
}
|
|
503
515
|
span.recordException(error);
|
|
504
516
|
} finally {
|
|
505
|
-
logger$
|
|
517
|
+
logger$29.debug("Ending span for AI SDK result");
|
|
506
518
|
span.end();
|
|
507
519
|
}
|
|
508
520
|
});
|
|
509
521
|
return originalResult;
|
|
510
522
|
}
|
|
511
523
|
if (streamInfo.type === "readable-stream") {
|
|
512
|
-
logger$
|
|
524
|
+
logger$29.debug("Setting up background processing for ReadableStream");
|
|
513
525
|
const { stream, dataPromise } = consumeAndTeeReadableStream(streamInfo.stream);
|
|
514
526
|
trackStreamProcessing(async () => {
|
|
515
527
|
try {
|
|
@@ -521,18 +533,18 @@ const handleStreamResult = (originalResult, streamInfo, span, ignoreOutput, seri
|
|
|
521
533
|
};
|
|
522
534
|
span.setAttribute(require_utils.SPAN_OUTPUT, serialize(output));
|
|
523
535
|
} catch (serError) {
|
|
524
|
-
logger$
|
|
536
|
+
logger$29.warn("Failed to serialize stream output: " + require_utils.errorMessage(serError));
|
|
525
537
|
}
|
|
526
538
|
if (error) span.recordException(error);
|
|
527
539
|
} finally {
|
|
528
|
-
logger$
|
|
540
|
+
logger$29.debug("Ending span for ReadableStream");
|
|
529
541
|
span.end();
|
|
530
542
|
}
|
|
531
543
|
});
|
|
532
544
|
return stream;
|
|
533
545
|
}
|
|
534
546
|
if (streamInfo.type === "async-iterable") {
|
|
535
|
-
logger$
|
|
547
|
+
logger$29.debug("Setting up background processing for AsyncIterable");
|
|
536
548
|
const { iterable, dataPromise } = consumeAndTeeAsyncIterable(streamInfo.iterable);
|
|
537
549
|
trackStreamProcessing(async () => {
|
|
538
550
|
try {
|
|
@@ -544,18 +556,18 @@ const handleStreamResult = (originalResult, streamInfo, span, ignoreOutput, seri
|
|
|
544
556
|
};
|
|
545
557
|
span.setAttribute(require_utils.SPAN_OUTPUT, serialize(output));
|
|
546
558
|
} catch (serError) {
|
|
547
|
-
logger$
|
|
559
|
+
logger$29.warn("Failed to serialize async iterable output: " + require_utils.errorMessage(serError));
|
|
548
560
|
}
|
|
549
561
|
if (error) span.recordException(error);
|
|
550
562
|
} finally {
|
|
551
|
-
logger$
|
|
563
|
+
logger$29.debug("Ending span for AsyncIterable");
|
|
552
564
|
span.end();
|
|
553
565
|
}
|
|
554
566
|
});
|
|
555
567
|
return iterable;
|
|
556
568
|
}
|
|
557
569
|
if (streamInfo.type === "response") {
|
|
558
|
-
logger$
|
|
570
|
+
logger$29.debug("Setting up background processing for Response");
|
|
559
571
|
const originalResponse = streamInfo.response;
|
|
560
572
|
if (!originalResponse.body) {
|
|
561
573
|
span.end();
|
|
@@ -578,19 +590,19 @@ const handleStreamResult = (originalResult, streamInfo, span, ignoreOutput, seri
|
|
|
578
590
|
};
|
|
579
591
|
span.setAttribute(require_utils.SPAN_OUTPUT, serialize(output));
|
|
580
592
|
} catch (serError) {
|
|
581
|
-
logger$
|
|
593
|
+
logger$29.warn("Failed to serialize response output: " + require_utils.errorMessage(serError));
|
|
582
594
|
}
|
|
583
595
|
if (error) span.recordException(error);
|
|
584
596
|
} catch (err) {
|
|
585
597
|
span.recordException(err);
|
|
586
598
|
} finally {
|
|
587
|
-
logger$
|
|
599
|
+
logger$29.debug("Ending span for Response");
|
|
588
600
|
span.end();
|
|
589
601
|
}
|
|
590
602
|
});
|
|
591
603
|
return newResponse;
|
|
592
604
|
}
|
|
593
|
-
logger$
|
|
605
|
+
logger$29.warn("Stream type not handled, ending span immediately");
|
|
594
606
|
span.end();
|
|
595
607
|
return originalResult;
|
|
596
608
|
};
|
|
@@ -668,7 +680,7 @@ const writeDebugSessionFile = (file, dir = process.cwd()) => {
|
|
|
668
680
|
* contract. This file is part of the cross-language parity surface — keep its
|
|
669
681
|
* logic line-comparable with the Python `config.py`.
|
|
670
682
|
*/
|
|
671
|
-
const logger$
|
|
683
|
+
const logger$28 = require_utils.initializeLogger();
|
|
672
684
|
const TRUTHY = /* @__PURE__ */ new Set([
|
|
673
685
|
"true",
|
|
674
686
|
"1",
|
|
@@ -704,7 +716,7 @@ const parseCacheUntil = (value) => {
|
|
|
704
716
|
if (value === void 0 || value === "") return null;
|
|
705
717
|
const spanId = normalizeSpanId(value);
|
|
706
718
|
if (spanId !== null) return spanId;
|
|
707
|
-
logger$
|
|
719
|
+
logger$28.warn(`LMNR_DEBUG_CACHE_UNTIL=${JSON.stringify(value)} is not a span id; ignoring`);
|
|
708
720
|
return null;
|
|
709
721
|
};
|
|
710
722
|
/**
|
|
@@ -1058,7 +1070,7 @@ const resetDebugRuntime = () => {
|
|
|
1058
1070
|
};
|
|
1059
1071
|
//#endregion
|
|
1060
1072
|
//#region src/opentelemetry-lib/tracing/span.ts
|
|
1061
|
-
const logger$
|
|
1073
|
+
const logger$27 = require_utils.initializeLogger();
|
|
1062
1074
|
/**
|
|
1063
1075
|
* Build the debug block to propagate, or undefined when debug mode is off.
|
|
1064
1076
|
*
|
|
@@ -1208,7 +1220,7 @@ var LaminarSpan = class LaminarSpan {
|
|
|
1208
1220
|
} catch {}
|
|
1209
1221
|
metadata[key.replace(`${require_utils.ASSOCIATION_PROPERTIES}.metadata.`, "")] = value;
|
|
1210
1222
|
}
|
|
1211
|
-
} else logger$
|
|
1223
|
+
} else logger$27.warn("Attributes object is not available. Most likely the span is not a LaminarSpan and not an OpenTelemetry default SDK Span. Span path and ids path will be empty.");
|
|
1212
1224
|
return {
|
|
1213
1225
|
spanId: require_utils.otelSpanIdToUUID(this._span.spanContext().spanId),
|
|
1214
1226
|
traceId: require_utils.otelTraceIdToUUID(this._span.spanContext().traceId),
|
|
@@ -1235,14 +1247,14 @@ var LaminarSpan = class LaminarSpan {
|
|
|
1235
1247
|
}
|
|
1236
1248
|
get tags() {
|
|
1237
1249
|
if (!this._span.attributes) {
|
|
1238
|
-
logger$
|
|
1250
|
+
logger$27.warn("[LaminarSpan.tags] WARNING. Current span does not have attributes object. Possibly, the span was created with a custom OTel SDK. Returning an empty list. Help: OpenTelemetry API does not guarantee reading attributes from a span, but OTel SDK allows it by default. Laminar SDK allows to read attributes too.");
|
|
1239
1251
|
return [];
|
|
1240
1252
|
}
|
|
1241
1253
|
return this._span.attributes[`lmnr.association.properties.tags`] ?? [];
|
|
1242
1254
|
}
|
|
1243
1255
|
get laminarAssociationProperties() {
|
|
1244
1256
|
if (!this._span.attributes) {
|
|
1245
|
-
logger$
|
|
1257
|
+
logger$27.warn("[LaminarSpan.laminarAssociationProperties] WARNING. Current span does not have attributes object. Possibly, the span was created with a custom OTel SDK. Returning an empty object. Help: OpenTelemetry API does not guarantee reading attributes from a span, but OTel SDK allows it by default. Laminar SDK allows to read attributes too.");
|
|
1246
1258
|
return {};
|
|
1247
1259
|
}
|
|
1248
1260
|
try {
|
|
@@ -1438,7 +1450,7 @@ var LaminarContextManager = class {
|
|
|
1438
1450
|
};
|
|
1439
1451
|
//#endregion
|
|
1440
1452
|
//#region src/opentelemetry-lib/instrumentation/claude-agent-sdk/proxy.ts
|
|
1441
|
-
const logger$
|
|
1453
|
+
const logger$26 = require_utils.initializeLogger();
|
|
1442
1454
|
const DEFAULT_ANTHROPIC_BASE_URL = "https://api.anthropic.com";
|
|
1443
1455
|
const DEFAULT_CC_PROXY_PORT = 45667;
|
|
1444
1456
|
const CC_PROXY_PORT_ATTEMPTS = 50;
|
|
@@ -1692,7 +1704,7 @@ const resolveTargetUrlFromEnv = (envDict, fallback = DEFAULT_ANTHROPIC_BASE_URL,
|
|
|
1692
1704
|
if (foundryBaseUrl) return foundryBaseUrl.replace(/\/$/, "");
|
|
1693
1705
|
const foundryResource = getEnvValue(FOUNDRY_RESOURCE_ENV);
|
|
1694
1706
|
if (foundryResource) return `https://${foundryResource}.services.ai.azure.com/anthropic`;
|
|
1695
|
-
logger$
|
|
1707
|
+
logger$26.error(`${FOUNDRY_USE_ENV} is set but neither ${FOUNDRY_BASE_URL_ENV} nor ${FOUNDRY_RESOURCE_ENV} is configured. Microsoft Foundry requires one of these values.`);
|
|
1696
1708
|
return null;
|
|
1697
1709
|
}
|
|
1698
1710
|
if (isTruthyEnv(getEnvValue(BEDROCK_USE_ENV))) {
|
|
@@ -1704,7 +1716,7 @@ const resolveTargetUrlFromEnv = (envDict, fallback = DEFAULT_ANTHROPIC_BASE_URL,
|
|
|
1704
1716
|
region = getRegionFromAwsConfig(awsProfile) ?? void 0;
|
|
1705
1717
|
}
|
|
1706
1718
|
if (region) return `https://bedrock-runtime.${region}.amazonaws.com`;
|
|
1707
|
-
logger$
|
|
1719
|
+
logger$26.error(`${BEDROCK_USE_ENV} is set but could not determine AWS region. Set ${BEDROCK_AWS_REGION_ENV} or configure a region in ~/.aws/config for the active profile.`);
|
|
1708
1720
|
return null;
|
|
1709
1721
|
}
|
|
1710
1722
|
if (isTruthyEnv(getEnvValue(VERTEX_USE_ENV))) {
|
|
@@ -1787,11 +1799,11 @@ const waitForPort = (port, timeoutMs = 5e3) => new Promise((resolve) => {
|
|
|
1787
1799
|
const registerGlobalProxyShutdown = () => {
|
|
1788
1800
|
if (!globalShutdownRegistered) {
|
|
1789
1801
|
process.on("exit", () => {
|
|
1790
|
-
logger$
|
|
1802
|
+
logger$26.debug("process.on(\"exit\") called - stopping all active proxies");
|
|
1791
1803
|
for (const proxyServer of activeProxyServers) try {
|
|
1792
1804
|
proxyServer.stopServer();
|
|
1793
1805
|
} catch (e) {
|
|
1794
|
-
logger$
|
|
1806
|
+
logger$26.debug(`Failed to stop proxy: ${require_utils.errorMessage(e)}`);
|
|
1795
1807
|
}
|
|
1796
1808
|
activeProxyServers.clear();
|
|
1797
1809
|
});
|
|
@@ -1806,28 +1818,28 @@ const createProxyInstance = async ({ env, cwd, settingSources, settings, targetU
|
|
|
1806
1818
|
try {
|
|
1807
1819
|
const port = await findAvailablePort(DEFAULT_CC_PROXY_PORT, CC_PROXY_PORT_ATTEMPTS);
|
|
1808
1820
|
if (port === null) {
|
|
1809
|
-
logger$
|
|
1821
|
+
logger$26.warn("Unable to allocate port for cc-proxy.");
|
|
1810
1822
|
return null;
|
|
1811
1823
|
}
|
|
1812
1824
|
const targetUrl = resolvedTargetUrl ?? resolveTargetUrlFromEnv(env, void 0, cwd, settingSources, settings);
|
|
1813
1825
|
if (!targetUrl) {
|
|
1814
|
-
logger$
|
|
1826
|
+
logger$26.warn("Unable to resolve target URL for cc-proxy (provider misconfigured).");
|
|
1815
1827
|
return null;
|
|
1816
1828
|
}
|
|
1817
|
-
logger$
|
|
1829
|
+
logger$26.debug(`Creating proxy instance on port ${port} targeting: ${targetUrl}`);
|
|
1818
1830
|
try {
|
|
1819
1831
|
const { ProxyServer } = require("@lmnr-ai/claude-code-proxy");
|
|
1820
1832
|
const proxyServer = new ProxyServer(port);
|
|
1821
1833
|
proxyServer.runServer(targetUrl);
|
|
1822
1834
|
if (!await waitForPort(port)) {
|
|
1823
|
-
logger$
|
|
1835
|
+
logger$26.warn(`cc-proxy failed to start on port ${port}`);
|
|
1824
1836
|
proxyServer.stopServer();
|
|
1825
1837
|
return null;
|
|
1826
1838
|
}
|
|
1827
1839
|
const proxyBaseUrl = `http://127.0.0.1:${port}`;
|
|
1828
1840
|
activeProxyServers.add(proxyServer);
|
|
1829
1841
|
registerGlobalProxyShutdown();
|
|
1830
|
-
logger$
|
|
1842
|
+
logger$26.info(`Started claude proxy server on: ${proxyBaseUrl}`);
|
|
1831
1843
|
return {
|
|
1832
1844
|
server: proxyServer,
|
|
1833
1845
|
baseUrl: proxyBaseUrl,
|
|
@@ -1835,11 +1847,11 @@ const createProxyInstance = async ({ env, cwd, settingSources, settings, targetU
|
|
|
1835
1847
|
targetUrl
|
|
1836
1848
|
};
|
|
1837
1849
|
} catch (e) {
|
|
1838
|
-
logger$
|
|
1850
|
+
logger$26.warn(`Unable to start cc-proxy: ${require_utils.errorMessage(e)}`);
|
|
1839
1851
|
return null;
|
|
1840
1852
|
}
|
|
1841
1853
|
} catch (e) {
|
|
1842
|
-
logger$
|
|
1854
|
+
logger$26.warn(`Failed to create proxy instance: ${require_utils.errorMessage(e)}`);
|
|
1843
1855
|
return null;
|
|
1844
1856
|
}
|
|
1845
1857
|
};
|
|
@@ -1849,11 +1861,11 @@ const createProxyInstance = async ({ env, cwd, settingSources, settings, targetU
|
|
|
1849
1861
|
const stopProxyInstance = (instance) => {
|
|
1850
1862
|
if (!instance) return;
|
|
1851
1863
|
try {
|
|
1852
|
-
logger$
|
|
1864
|
+
logger$26.debug(`Stopping proxy instance on port ${instance.port}`);
|
|
1853
1865
|
instance.server.stopServer();
|
|
1854
1866
|
activeProxyServers.delete(instance.server);
|
|
1855
1867
|
} catch (e) {
|
|
1856
|
-
logger$
|
|
1868
|
+
logger$26.debug(`Failed to stop proxy instance: ${require_utils.errorMessage(e)}`);
|
|
1857
1869
|
}
|
|
1858
1870
|
};
|
|
1859
1871
|
/**
|
|
@@ -1861,11 +1873,11 @@ const stopProxyInstance = (instance) => {
|
|
|
1861
1873
|
* Used during shutdown to ensure cleanup
|
|
1862
1874
|
*/
|
|
1863
1875
|
const forceReleaseProxy = () => {
|
|
1864
|
-
logger$
|
|
1876
|
+
logger$26.debug(`Force stopping all ${activeProxyServers.size} active proxy servers`);
|
|
1865
1877
|
for (const proxyServer of activeProxyServers) try {
|
|
1866
1878
|
proxyServer.stopServer();
|
|
1867
1879
|
} catch (e) {
|
|
1868
|
-
logger$
|
|
1880
|
+
logger$26.debug(`Failed to stop proxy: ${require_utils.errorMessage(e)}`);
|
|
1869
1881
|
}
|
|
1870
1882
|
activeProxyServers.clear();
|
|
1871
1883
|
};
|
|
@@ -1915,14 +1927,14 @@ const setTraceToProxyInstance = async (instance) => {
|
|
|
1915
1927
|
spanPath: payload.span_path,
|
|
1916
1928
|
laminarUrl: payload.laminar_url
|
|
1917
1929
|
});
|
|
1918
|
-
logger$
|
|
1930
|
+
logger$26.debug(`Set trace context to proxy on port ${instance.port}`);
|
|
1919
1931
|
} catch (e) {
|
|
1920
|
-
logger$
|
|
1932
|
+
logger$26.debug(`Unable to set trace context to proxy on port ${instance.port}: ` + require_utils.errorMessage(e));
|
|
1921
1933
|
}
|
|
1922
1934
|
};
|
|
1923
1935
|
//#endregion
|
|
1924
1936
|
//#region src/opentelemetry-lib/instrumentation/claude-agent-sdk/index.ts
|
|
1925
|
-
const logger$
|
|
1937
|
+
const logger$25 = require_utils.initializeLogger();
|
|
1926
1938
|
/**
|
|
1927
1939
|
* Create an instrumented version of the claude-agent-sdk query function.
|
|
1928
1940
|
* This can be used when importing the query function before Laminar initialization.
|
|
@@ -1958,9 +1970,9 @@ function instrumentClaudeAgentQuery(originalQuery) {
|
|
|
1958
1970
|
targetUrl
|
|
1959
1971
|
});
|
|
1960
1972
|
if (proxyInstance && targetUrl) {
|
|
1961
|
-
logger$
|
|
1973
|
+
logger$25.debug(`Using proxy on port ${proxyInstance.port}`);
|
|
1962
1974
|
await Laminar.withSpan(span, async () => {
|
|
1963
|
-
logger$
|
|
1975
|
+
logger$25.debug("Setting trace to proxy instance...");
|
|
1964
1976
|
await setTraceToProxyInstance(proxyInstance);
|
|
1965
1977
|
});
|
|
1966
1978
|
const varsToRemove = getEnvVarsToRemove(mergedEnv, sessionCwd, settingSources);
|
|
@@ -1975,13 +1987,13 @@ function instrumentClaudeAgentQuery(originalQuery) {
|
|
|
1975
1987
|
if (isProviderEnabledInEnv(subprocessEnv, "CLAUDE_CODE_USE_BEDROCK")) subprocessEnv.ANTHROPIC_BEDROCK_BASE_URL = proxyInstance.baseUrl;
|
|
1976
1988
|
if (isProviderEnabledInEnv(subprocessEnv, "CLAUDE_CODE_USE_VERTEX")) subprocessEnv.ANTHROPIC_VERTEX_BASE_URL = proxyInstance.baseUrl;
|
|
1977
1989
|
const flagSettings = buildProxyFlagSettings(callerSettings, proxyInstance.baseUrl, sessionCwd, settingSources);
|
|
1978
|
-
if (flagSettings === null) logger$
|
|
1990
|
+
if (flagSettings === null) logger$25.warn(`Could not read options.settings ${JSON.stringify(callerSettings)}; Claude Code settings that define a base URL will bypass the Laminar proxy and produce no LLM spans.`);
|
|
1979
1991
|
else {
|
|
1980
1992
|
params.options.settings = flagSettings;
|
|
1981
1993
|
const conflicting = Object.entries(readClaudeSettingsEnv(sessionCwd, settingSources)).filter(([key, value]) => PROXY_BASE_URL_ENV_KEYS.includes(key) && value !== "" && value !== proxyInstance?.baseUrl).map(([key]) => key);
|
|
1982
|
-
if (conflicting.length > 0) logger$
|
|
1994
|
+
if (conflicting.length > 0) logger$25.info(`Claude Code settings define ${conflicting.sort().join(", ")}, which outranks the subprocess environment. Injected settings so the Laminar proxy at ${proxyInstance.baseUrl} still intercepts API traffic; your settings files were not modified.`);
|
|
1983
1995
|
}
|
|
1984
|
-
} else logger$
|
|
1996
|
+
} else logger$25.debug("No claude proxy server available. Proceeding without proxy.");
|
|
1985
1997
|
const originalGenerator = originalQuery(params);
|
|
1986
1998
|
for await (const message of originalGenerator) {
|
|
1987
1999
|
collected.push(message);
|
|
@@ -2010,7 +2022,7 @@ var ClaudeAgentSDKInstrumentation = class extends _opentelemetry_instrumentation
|
|
|
2010
2022
|
}
|
|
2011
2023
|
manuallyInstrument(claudeAgentModule) {
|
|
2012
2024
|
if (claudeAgentModule.query && typeof claudeAgentModule.query === "function") this._wrap(claudeAgentModule, "query", this.patchQuery());
|
|
2013
|
-
else logger$
|
|
2025
|
+
else logger$25.debug("query function not found in claudeAgentSDK module, skipping instrumentation");
|
|
2014
2026
|
}
|
|
2015
2027
|
patchQuery() {
|
|
2016
2028
|
return (original) => instrumentClaudeAgentQuery(original);
|
|
@@ -2028,7 +2040,7 @@ var ClaudeAgentSDKInstrumentation = class extends _opentelemetry_instrumentation
|
|
|
2028
2040
|
//#endregion
|
|
2029
2041
|
//#region src/laminar.ts
|
|
2030
2042
|
require_utils.loadEnv();
|
|
2031
|
-
const logger$
|
|
2043
|
+
const logger$24 = require_utils.initializeLogger();
|
|
2032
2044
|
var Laminar = class Laminar {
|
|
2033
2045
|
static {
|
|
2034
2046
|
this.isInitialized = false;
|
|
@@ -2121,7 +2133,7 @@ var Laminar = class Laminar {
|
|
|
2121
2133
|
*/
|
|
2122
2134
|
static initialize({ projectApiKey, baseUrl, baseHttpUrl, httpPort, grpcPort, instrumentModules, disableBatch, traceExportTimeoutMillis, logLevel, maxExportBatchSize, maxExportBatchSizeBytes, flushBySize, forceHttp, sessionRecordingOptions, metadata, inheritGlobalContext, spanProcessor } = {}) {
|
|
2123
2135
|
if (this.isInitialized) {
|
|
2124
|
-
logger$
|
|
2136
|
+
logger$24.warn("Laminar has already been initialized. Skipping initialization.");
|
|
2125
2137
|
return;
|
|
2126
2138
|
}
|
|
2127
2139
|
const key = projectApiKey ?? process?.env?.LMNR_PROJECT_API_KEY;
|
|
@@ -2145,7 +2157,7 @@ var Laminar = class Laminar {
|
|
|
2145
2157
|
...metadata ?? {}
|
|
2146
2158
|
};
|
|
2147
2159
|
if (inheritGlobalContext) LaminarContextManager.inheritGlobalContext = true;
|
|
2148
|
-
if (spanProcessor && !(spanProcessor instanceof LaminarSpanProcessor)) logger$
|
|
2160
|
+
if (spanProcessor && !(spanProcessor instanceof LaminarSpanProcessor)) logger$24.warn("Span processor is not a LaminarSpanProcessor. Some functionality may be impaired.");
|
|
2149
2161
|
this.isInitialized = true;
|
|
2150
2162
|
const urlWithoutSlash = url?.replace(/\/$/, "").replace(/:\d{1,5}$/g, "");
|
|
2151
2163
|
const httpUrlWithoutSlash = httpUrl?.replace(/\/$/, "").replace(/:\d{1,5}$/g, "");
|
|
@@ -2192,11 +2204,11 @@ var Laminar = class Laminar {
|
|
|
2192
2204
|
try {
|
|
2193
2205
|
getRuntime()?.recordTraceId(require_utils.otelTraceIdToUUID(otelSpanContext.traceId));
|
|
2194
2206
|
} catch (e) {
|
|
2195
|
-
logger$
|
|
2207
|
+
logger$24.debug("Failed to record debug trace id from env: " + require_utils.errorMessage(e));
|
|
2196
2208
|
}
|
|
2197
|
-
logger$
|
|
2209
|
+
logger$24.debug("Initialized Laminar parent context from LMNR_SPAN_CONTEXT.");
|
|
2198
2210
|
} catch (e) {
|
|
2199
|
-
logger$
|
|
2211
|
+
logger$24.warn("LMNR_SPAN_CONTEXT is set but could not be used: " + require_utils.errorMessage(e));
|
|
2200
2212
|
}
|
|
2201
2213
|
}
|
|
2202
2214
|
/**
|
|
@@ -2240,7 +2252,7 @@ var Laminar = class Laminar {
|
|
|
2240
2252
|
if (projectId) {
|
|
2241
2253
|
runtime.recordProjectId(projectId);
|
|
2242
2254
|
const sessionUrl = runtime.debuggerSessionUrl();
|
|
2243
|
-
logger$
|
|
2255
|
+
logger$24.info(`Laminar debugger session: ${sessionUrl}`);
|
|
2244
2256
|
if (runtime.shouldOpenBrowser) {
|
|
2245
2257
|
const opener = process.platform === "win32" ? "start" : process.platform === "darwin" ? "open" : "xdg-open";
|
|
2246
2258
|
(0, node_child_process.spawn)(opener, [sessionUrl], {
|
|
@@ -2250,7 +2262,7 @@ var Laminar = class Laminar {
|
|
|
2250
2262
|
}
|
|
2251
2263
|
}
|
|
2252
2264
|
}).catch((e) => {
|
|
2253
|
-
logger$
|
|
2265
|
+
logger$24.warn("Failed to register debug session: " + require_utils.errorMessage(e));
|
|
2254
2266
|
});
|
|
2255
2267
|
this.globalMetadata = {
|
|
2256
2268
|
...this.globalMetadata,
|
|
@@ -2260,7 +2272,7 @@ var Laminar = class Laminar {
|
|
|
2260
2272
|
this.debugExitHook = () => runtime.emitPointer();
|
|
2261
2273
|
process.once("exit", this.debugExitHook);
|
|
2262
2274
|
} catch (e) {
|
|
2263
|
-
logger$
|
|
2275
|
+
logger$24.warn("Failed to initialize debug runtime: " + require_utils.errorMessage(e));
|
|
2264
2276
|
}
|
|
2265
2277
|
}
|
|
2266
2278
|
/**
|
|
@@ -2307,7 +2319,7 @@ var Laminar = class Laminar {
|
|
|
2307
2319
|
runtime.rolloutSessions.register({ sessionId: runtime.sessionId }).then((projectId) => {
|
|
2308
2320
|
if (projectId) runtime.recordProjectId(projectId);
|
|
2309
2321
|
}).catch((e) => {
|
|
2310
|
-
logger$
|
|
2322
|
+
logger$24.debug("Failed to register downstream debug session: " + require_utils.errorMessage(e));
|
|
2311
2323
|
});
|
|
2312
2324
|
this.globalMetadata = {
|
|
2313
2325
|
...this.globalMetadata,
|
|
@@ -2316,7 +2328,7 @@ var Laminar = class Laminar {
|
|
|
2316
2328
|
LaminarContextManager.setGlobalMetadata(this.globalMetadata);
|
|
2317
2329
|
}
|
|
2318
2330
|
} catch (e) {
|
|
2319
|
-
logger$
|
|
2331
|
+
logger$24.debug("Failed to arm debug runtime from context: " + require_utils.errorMessage(e));
|
|
2320
2332
|
}
|
|
2321
2333
|
}
|
|
2322
2334
|
/**
|
|
@@ -2331,7 +2343,7 @@ var Laminar = class Laminar {
|
|
|
2331
2343
|
*/
|
|
2332
2344
|
static patch(modules) {
|
|
2333
2345
|
if (!this.isInitialized) {
|
|
2334
|
-
logger$
|
|
2346
|
+
logger$24.warn("Laminar must be initialized before patching modules. Skipping patch.");
|
|
2335
2347
|
return;
|
|
2336
2348
|
}
|
|
2337
2349
|
if (!modules || Object.keys(modules).length === 0) throw new Error("Pass at least one module to patch");
|
|
@@ -2591,7 +2603,7 @@ var Laminar = class Laminar {
|
|
|
2591
2603
|
const spanContext = require_utils.tryToOtelSpanContext(laminarContext);
|
|
2592
2604
|
entityContext = _opentelemetry_api.trace.setSpan(entityContext, _opentelemetry_api.trace.wrapSpanContext(spanContext));
|
|
2593
2605
|
} catch (e) {
|
|
2594
|
-
logger$
|
|
2606
|
+
logger$24.warn("Failed to parse parent span context: " + require_utils.errorMessage(e));
|
|
2595
2607
|
}
|
|
2596
2608
|
const tagProperties = tags ? { [`${require_utils.ASSOCIATION_PROPERTIES}.tags`]: Array.from(new Set(tags)) } : {};
|
|
2597
2609
|
const ctxAssociationProperties = LaminarContextManager.getAssociationProperties();
|
|
@@ -2695,13 +2707,13 @@ var Laminar = class Laminar {
|
|
|
2695
2707
|
}
|
|
2696
2708
|
static async flush() {
|
|
2697
2709
|
if (this.isInitialized) {
|
|
2698
|
-
logger$
|
|
2710
|
+
logger$24.debug("Flushing spans");
|
|
2699
2711
|
await forceFlush();
|
|
2700
2712
|
}
|
|
2701
2713
|
}
|
|
2702
2714
|
static async shutdown() {
|
|
2703
2715
|
if (this.isInitialized) {
|
|
2704
|
-
logger$
|
|
2716
|
+
logger$24.debug("Shutting down Laminar");
|
|
2705
2717
|
getRuntime()?.emitPointer();
|
|
2706
2718
|
await forceFlush();
|
|
2707
2719
|
this.isInitialized = false;
|
|
@@ -2836,7 +2848,7 @@ return module.exports;
|
|
|
2836
2848
|
//#endregion
|
|
2837
2849
|
//#region src/browser/utils.ts
|
|
2838
2850
|
const LMNR_SEND_EVENTS_FUNCTION_NAME = "lmnrSendEvents";
|
|
2839
|
-
const logger$
|
|
2851
|
+
const logger$23 = require_utils.initializeLogger();
|
|
2840
2852
|
/**
|
|
2841
2853
|
* If the first argument is a string, return an object with the name of the method
|
|
2842
2854
|
* and the argument as the value.
|
|
@@ -2859,7 +2871,7 @@ const takeFullSnapshot = async (page) => await page.evaluate(() => {
|
|
|
2859
2871
|
window.lmnrRrweb.record.takeFullSnapshot();
|
|
2860
2872
|
return true;
|
|
2861
2873
|
} catch (error) {
|
|
2862
|
-
logger$
|
|
2874
|
+
logger$23.error("Error taking full snapshot: " + require_utils.errorMessage(error));
|
|
2863
2875
|
return false;
|
|
2864
2876
|
}
|
|
2865
2877
|
return false;
|
|
@@ -2870,7 +2882,7 @@ const injectSessionRecorder = async (page, sessionRecordingOptions) => {
|
|
|
2870
2882
|
try {
|
|
2871
2883
|
return await script();
|
|
2872
2884
|
} catch (error) {
|
|
2873
|
-
logger$
|
|
2885
|
+
logger$23.error("Operation " + script.name + " failed: " + require_utils.errorMessage(error));
|
|
2874
2886
|
}
|
|
2875
2887
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
2876
2888
|
}
|
|
@@ -2880,20 +2892,20 @@ const injectSessionRecorder = async (page, sessionRecordingOptions) => {
|
|
|
2880
2892
|
try {
|
|
2881
2893
|
isRrwebPresent = await castedPage.evaluate(() => typeof window.lmnrRrweb !== "undefined");
|
|
2882
2894
|
} catch (error) {
|
|
2883
|
-
logger$
|
|
2895
|
+
logger$23.debug("Failed to check if session recorder is loaded: " + require_utils.errorMessage(error));
|
|
2884
2896
|
}
|
|
2885
2897
|
if (!isRrwebPresent) {
|
|
2886
2898
|
if (!await tryRunScript(async function injectSessionRecorder() {
|
|
2887
2899
|
await castedPage.evaluate(RECORDER);
|
|
2888
2900
|
return true;
|
|
2889
2901
|
})) {
|
|
2890
|
-
logger$
|
|
2902
|
+
logger$23.error("Failed to load session recorder");
|
|
2891
2903
|
return;
|
|
2892
2904
|
}
|
|
2893
2905
|
try {
|
|
2894
2906
|
await castedPage.evaluate(injectScript, sessionRecordingOptions);
|
|
2895
2907
|
} catch (error) {
|
|
2896
|
-
logger$
|
|
2908
|
+
logger$23.debug("Failed to inject session recorder: " + require_utils.errorMessage(error));
|
|
2897
2909
|
}
|
|
2898
2910
|
}
|
|
2899
2911
|
};
|
|
@@ -3159,7 +3171,7 @@ async function sendEvents(chunk, client, chunkBuffers, sessionId, traceId) {
|
|
|
3159
3171
|
try {
|
|
3160
3172
|
const { batchId, chunkIndex, totalChunks, data } = chunk;
|
|
3161
3173
|
if (!sessionId || !traceId) {
|
|
3162
|
-
logger$
|
|
3174
|
+
logger$23.debug("Missing sessionId or traceId in chunk");
|
|
3163
3175
|
return;
|
|
3164
3176
|
}
|
|
3165
3177
|
if (!chunkBuffers.has(batchId)) chunkBuffers.set(batchId, {
|
|
@@ -3178,7 +3190,7 @@ async function sendEvents(chunk, client, chunkBuffers, sessionId, traceId) {
|
|
|
3178
3190
|
traceId,
|
|
3179
3191
|
events
|
|
3180
3192
|
}).catch((error) => {
|
|
3181
|
-
logger$
|
|
3193
|
+
logger$23.debug("Failed to send events: " + require_utils.errorMessage(error));
|
|
3182
3194
|
});
|
|
3183
3195
|
chunkBuffers.delete(batchId);
|
|
3184
3196
|
}
|
|
@@ -3186,16 +3198,16 @@ async function sendEvents(chunk, client, chunkBuffers, sessionId, traceId) {
|
|
|
3186
3198
|
const toDelete = [];
|
|
3187
3199
|
for (const [bid, buffer] of chunkBuffers.entries()) if (currentTime - buffer.timestamp > OLD_BUFFER_TIMEOUT) toDelete.push(bid);
|
|
3188
3200
|
for (const bid of toDelete) {
|
|
3189
|
-
logger$
|
|
3201
|
+
logger$23.debug(`Cleaning up incomplete chunk buffer: ${bid}`);
|
|
3190
3202
|
chunkBuffers.delete(bid);
|
|
3191
3203
|
}
|
|
3192
3204
|
} catch (error) {
|
|
3193
|
-
logger$
|
|
3205
|
+
logger$23.debug("Could not send events: " + require_utils.errorMessage(error));
|
|
3194
3206
|
}
|
|
3195
3207
|
}
|
|
3196
3208
|
//#endregion
|
|
3197
3209
|
//#region src/browser/playwright.ts
|
|
3198
|
-
const logger$
|
|
3210
|
+
const logger$22 = require_utils.initializeLogger();
|
|
3199
3211
|
var PlaywrightInstrumentation = class extends _opentelemetry_instrumentation.InstrumentationBase {
|
|
3200
3212
|
constructor(client, sessionRecordingOptions) {
|
|
3201
3213
|
super("@lmnr/playwright-instrumentation", require_utils.version, { enabled: true });
|
|
@@ -3303,7 +3315,7 @@ var PlaywrightInstrumentation = class extends _opentelemetry_instrumentation.Ins
|
|
|
3303
3315
|
try {
|
|
3304
3316
|
await injectSessionRecorder(page, this._sessionRecordingOptions);
|
|
3305
3317
|
} catch (error) {
|
|
3306
|
-
logger$
|
|
3318
|
+
logger$22.error("Error in onLoad handler: " + require_utils.errorMessage(error));
|
|
3307
3319
|
}
|
|
3308
3320
|
});
|
|
3309
3321
|
const chunkBuffers = /* @__PURE__ */ new Map();
|
|
@@ -3313,7 +3325,7 @@ var PlaywrightInstrumentation = class extends _opentelemetry_instrumentation.Ins
|
|
|
3313
3325
|
await sendEvents(chunk, this._client, chunkBuffers, sessionId, traceId);
|
|
3314
3326
|
});
|
|
3315
3327
|
} catch (error) {
|
|
3316
|
-
logger$
|
|
3328
|
+
logger$22.debug("Could not expose function lmnrSendEvents: " + require_utils.errorMessage(error));
|
|
3317
3329
|
}
|
|
3318
3330
|
}
|
|
3319
3331
|
};
|
|
@@ -3738,7 +3750,7 @@ var StagehandV2Instrumentation = class extends _opentelemetry_instrumentation.In
|
|
|
3738
3750
|
};
|
|
3739
3751
|
//#endregion
|
|
3740
3752
|
//#region src/browser/puppeteer.ts
|
|
3741
|
-
const logger$
|
|
3753
|
+
const logger$21 = require_utils.initializeLogger();
|
|
3742
3754
|
var PuppeteerInstrumentation = class extends _opentelemetry_instrumentation.InstrumentationBase {
|
|
3743
3755
|
constructor(client, sessionRecordingOptions) {
|
|
3744
3756
|
super("@lmnr/puppeteer-instrumentation", require_utils.version, { enabled: true });
|
|
@@ -3783,10 +3795,10 @@ var PuppeteerInstrumentation = class extends _opentelemetry_instrumentation.Inst
|
|
|
3783
3795
|
context.on("targetcreated", (target) => {
|
|
3784
3796
|
target.page().then((page) => {
|
|
3785
3797
|
if (page) plugin.patchPage(page, sessionId).catch((error) => {
|
|
3786
|
-
logger$
|
|
3798
|
+
logger$21.error("Failed to patch page: " + require_utils.errorMessage(error));
|
|
3787
3799
|
});
|
|
3788
3800
|
}).catch((error) => {
|
|
3789
|
-
logger$
|
|
3801
|
+
logger$21.error("Failed to patch page: " + require_utils.errorMessage(error));
|
|
3790
3802
|
});
|
|
3791
3803
|
});
|
|
3792
3804
|
await Promise.all((await context.pages()).map((page) => plugin.patchPage(page, sessionId)));
|
|
@@ -3804,19 +3816,19 @@ var PuppeteerInstrumentation = class extends _opentelemetry_instrumentation.Inst
|
|
|
3804
3816
|
context.on("targetcreated", (target) => {
|
|
3805
3817
|
target.page().then((page) => {
|
|
3806
3818
|
if (page) plugin.patchPage(page, sessionId).catch((error) => {
|
|
3807
|
-
logger$
|
|
3819
|
+
logger$21.error("Failed to patch page: " + require_utils.errorMessage(error));
|
|
3808
3820
|
});
|
|
3809
3821
|
}).catch((error) => {
|
|
3810
|
-
logger$
|
|
3822
|
+
logger$21.error("Failed to patch page: " + require_utils.errorMessage(error));
|
|
3811
3823
|
});
|
|
3812
3824
|
});
|
|
3813
3825
|
context.on("targetchanged", (target) => {
|
|
3814
3826
|
target.page().then((page) => {
|
|
3815
3827
|
if (page) plugin.patchPage(page, sessionId).catch((error) => {
|
|
3816
|
-
logger$
|
|
3828
|
+
logger$21.error("Failed to patch page: " + require_utils.errorMessage(error));
|
|
3817
3829
|
});
|
|
3818
3830
|
}).catch((error) => {
|
|
3819
|
-
logger$
|
|
3831
|
+
logger$21.error("Failed to patch page: " + require_utils.errorMessage(error));
|
|
3820
3832
|
});
|
|
3821
3833
|
});
|
|
3822
3834
|
for (const page of await context.pages()) await plugin.patchPage(page, sessionId);
|
|
@@ -3840,7 +3852,7 @@ var PuppeteerInstrumentation = class extends _opentelemetry_instrumentation.Inst
|
|
|
3840
3852
|
const traceId = otelTraceId ? require_utils.otelTraceIdToUUID(otelTraceId) : require_utils.NIL_UUID;
|
|
3841
3853
|
page.on("domcontentloaded", () => {
|
|
3842
3854
|
injectSessionRecorder(page, this._sessionRecordingOptions).catch((error) => {
|
|
3843
|
-
logger$
|
|
3855
|
+
logger$21.error("Error in onLoad handler: " + require_utils.errorMessage(error));
|
|
3844
3856
|
});
|
|
3845
3857
|
});
|
|
3846
3858
|
await injectSessionRecorder(page, this._sessionRecordingOptions);
|
|
@@ -3850,13 +3862,13 @@ var PuppeteerInstrumentation = class extends _opentelemetry_instrumentation.Inst
|
|
|
3850
3862
|
await sendEvents(chunk, this._client, chunkBuffers, sessionId, traceId);
|
|
3851
3863
|
});
|
|
3852
3864
|
} catch (error) {
|
|
3853
|
-
logger$
|
|
3865
|
+
logger$21.debug("Could not expose function lmnrSendEvents: " + require_utils.errorMessage(error));
|
|
3854
3866
|
}
|
|
3855
3867
|
}
|
|
3856
3868
|
};
|
|
3857
3869
|
//#endregion
|
|
3858
3870
|
//#region src/browser/stagehand/v3/constants.ts
|
|
3859
|
-
const logger$
|
|
3871
|
+
const logger$20 = require_utils.initializeLogger();
|
|
3860
3872
|
const CDP_OPERATION_TIMEOUT_MS = 1e4;
|
|
3861
3873
|
const SKIP_URL_PATTERNS = [
|
|
3862
3874
|
"about:blank",
|
|
@@ -3907,7 +3919,7 @@ async function getOrCreateIsolatedWorld(page) {
|
|
|
3907
3919
|
frameId
|
|
3908
3920
|
};
|
|
3909
3921
|
} catch (error) {
|
|
3910
|
-
logger$
|
|
3922
|
+
logger$20.debug("Failed to create isolated world: " + require_utils.errorMessage(error));
|
|
3911
3923
|
return null;
|
|
3912
3924
|
}
|
|
3913
3925
|
}
|
|
@@ -3924,7 +3936,7 @@ async function isRecorderPresent(page, contextId) {
|
|
|
3924
3936
|
if (!result) return true;
|
|
3925
3937
|
return result.result?.value === true;
|
|
3926
3938
|
} catch (error) {
|
|
3927
|
-
logger$
|
|
3939
|
+
logger$20.debug("Failed to check if recorder is present: " + require_utils.errorMessage(error));
|
|
3928
3940
|
return true;
|
|
3929
3941
|
}
|
|
3930
3942
|
}
|
|
@@ -3937,21 +3949,21 @@ async function injectRecorderViaCDP(page, state, conn, sessionRecordingOptions,
|
|
|
3937
3949
|
try {
|
|
3938
3950
|
url = page.url();
|
|
3939
3951
|
} catch (error) {
|
|
3940
|
-
logger$
|
|
3952
|
+
logger$20.debug("Failed to get page URL, page might be closed: " + require_utils.errorMessage(error));
|
|
3941
3953
|
return null;
|
|
3942
3954
|
}
|
|
3943
3955
|
if (shouldSkipUrl(url)) {
|
|
3944
|
-
logger$
|
|
3956
|
+
logger$20.debug(`Skipping recorder injection for URL: ${url}`);
|
|
3945
3957
|
return null;
|
|
3946
3958
|
}
|
|
3947
3959
|
const worldResult = await getOrCreateIsolatedWorld(page);
|
|
3948
3960
|
if (!worldResult) {
|
|
3949
|
-
logger$
|
|
3961
|
+
logger$20.debug("Failed to get isolated world for page");
|
|
3950
3962
|
return null;
|
|
3951
3963
|
}
|
|
3952
3964
|
const { contextId, frameId } = worldResult;
|
|
3953
3965
|
if (await isRecorderPresent(page, contextId)) {
|
|
3954
|
-
logger$
|
|
3966
|
+
logger$20.debug("Recorder already present, skipping injection");
|
|
3955
3967
|
state.contextIdToSession.set(contextId, {
|
|
3956
3968
|
sessionId: state.sessionId,
|
|
3957
3969
|
traceId: state.traceId
|
|
@@ -3964,7 +3976,7 @@ async function injectRecorderViaCDP(page, state, conn, sessionRecordingOptions,
|
|
|
3964
3976
|
contextId
|
|
3965
3977
|
});
|
|
3966
3978
|
} catch (error) {
|
|
3967
|
-
logger$
|
|
3979
|
+
logger$20.debug("Failed to inject rrweb: " + require_utils.errorMessage(error));
|
|
3968
3980
|
return null;
|
|
3969
3981
|
}
|
|
3970
3982
|
const optionsJson = JSON.stringify(sessionRecordingOptions ?? {});
|
|
@@ -3975,14 +3987,14 @@ async function injectRecorderViaCDP(page, state, conn, sessionRecordingOptions,
|
|
|
3975
3987
|
contextId
|
|
3976
3988
|
});
|
|
3977
3989
|
} catch (error) {
|
|
3978
|
-
logger$
|
|
3990
|
+
logger$20.debug("Failed to inject recording setup: " + require_utils.errorMessage(error));
|
|
3979
3991
|
return null;
|
|
3980
3992
|
}
|
|
3981
3993
|
try {
|
|
3982
3994
|
await sendCDP(page, "Runtime.addBinding", { name: "lmnrSendEvents" });
|
|
3983
|
-
logger$
|
|
3995
|
+
logger$20.debug(`Added binding 'lmnrSendEvents' for page ${frameId}, context ${contextId}`);
|
|
3984
3996
|
} catch (error) {
|
|
3985
|
-
logger$
|
|
3997
|
+
logger$20.debug("Binding may already exist: " + require_utils.errorMessage(error));
|
|
3986
3998
|
}
|
|
3987
3999
|
if (bindingHandler) try {
|
|
3988
4000
|
const pageTargetId = page.targetId();
|
|
@@ -3992,26 +4004,26 @@ async function injectRecorderViaCDP(page, state, conn, sessionRecordingOptions,
|
|
|
3992
4004
|
const sessionTyped = session;
|
|
3993
4005
|
if (sessionTyped.on) {
|
|
3994
4006
|
sessionTyped.on("Runtime.bindingCalled", bindingHandler);
|
|
3995
|
-
logger$
|
|
4007
|
+
logger$20.debug(`Set up binding handler on session ${sessionId}`);
|
|
3996
4008
|
}
|
|
3997
4009
|
} catch (error) {
|
|
3998
|
-
logger$
|
|
4010
|
+
logger$20.debug(`Failed to set up binding handler on session ${sessionId}: ` + require_utils.errorMessage(error));
|
|
3999
4011
|
}
|
|
4000
4012
|
state.pageSessionHandlers.set(pageTargetId, bindingHandler);
|
|
4001
4013
|
}
|
|
4002
4014
|
} catch (error) {
|
|
4003
|
-
logger$
|
|
4015
|
+
logger$20.debug("Failed to set up binding handler: " + require_utils.errorMessage(error));
|
|
4004
4016
|
}
|
|
4005
4017
|
state.contextIdToSession.set(contextId, {
|
|
4006
4018
|
sessionId: state.sessionId,
|
|
4007
4019
|
traceId: state.traceId
|
|
4008
4020
|
});
|
|
4009
|
-
logger$
|
|
4021
|
+
logger$20.debug(`Registered context mapping: contextId=${contextId}, sessionId=${state.sessionId}`);
|
|
4010
4022
|
state.instrumentedPageIds.add(frameId);
|
|
4011
|
-
logger$
|
|
4023
|
+
logger$20.debug(`Successfully injected recorder into page ${frameId}`);
|
|
4012
4024
|
return contextId;
|
|
4013
4025
|
} catch (error) {
|
|
4014
|
-
logger$
|
|
4026
|
+
logger$20.debug("Error injecting recorder: " + require_utils.errorMessage(error));
|
|
4015
4027
|
return null;
|
|
4016
4028
|
}
|
|
4017
4029
|
}
|
|
@@ -4025,13 +4037,13 @@ function createBindingHandler(state) {
|
|
|
4025
4037
|
if (event.name !== "lmnrSendEvents") return;
|
|
4026
4038
|
const sessionInfo = state.contextIdToSession.get(event.executionContextId);
|
|
4027
4039
|
if (!sessionInfo) {
|
|
4028
|
-
logger$
|
|
4040
|
+
logger$20.debug("No session info found for context ID: " + event.executionContextId);
|
|
4029
4041
|
if (state.contextIdToSession.size > 0) {
|
|
4030
4042
|
const [, fallbackInfo] = state.contextIdToSession.entries().next().value;
|
|
4031
4043
|
if (fallbackInfo) try {
|
|
4032
4044
|
sendEvents(JSON.parse(event.payload), state.client, state.chunkBuffers, fallbackInfo.sessionId, fallbackInfo.traceId);
|
|
4033
4045
|
} catch (error) {
|
|
4034
|
-
logger$
|
|
4046
|
+
logger$20.debug("Failed to parse binding payload: " + require_utils.errorMessage(error));
|
|
4035
4047
|
}
|
|
4036
4048
|
}
|
|
4037
4049
|
return;
|
|
@@ -4039,7 +4051,7 @@ function createBindingHandler(state) {
|
|
|
4039
4051
|
try {
|
|
4040
4052
|
sendEvents(JSON.parse(event.payload), state.client, state.chunkBuffers, sessionInfo.sessionId, sessionInfo.traceId);
|
|
4041
4053
|
} catch (error) {
|
|
4042
|
-
logger$
|
|
4054
|
+
logger$20.debug("Failed to handle binding event: " + require_utils.errorMessage(error));
|
|
4043
4055
|
}
|
|
4044
4056
|
};
|
|
4045
4057
|
}
|
|
@@ -4055,10 +4067,10 @@ function createTargetCreatedHandler(context, state, sessionRecordingOptions) {
|
|
|
4055
4067
|
const pageId = page.mainFrameId();
|
|
4056
4068
|
if (!state.instrumentedPageIds.has(pageId)) await injectRecorderViaCDP(page, state, context.conn, sessionRecordingOptions, state.bindingHandler ?? void 0);
|
|
4057
4069
|
} catch (error) {
|
|
4058
|
-
logger$
|
|
4070
|
+
logger$20.debug("Error injecting recorder into new page: " + require_utils.errorMessage(error));
|
|
4059
4071
|
}
|
|
4060
4072
|
} catch (error) {
|
|
4061
|
-
logger$
|
|
4073
|
+
logger$20.debug("Error handling target created event: " + require_utils.errorMessage(error));
|
|
4062
4074
|
}
|
|
4063
4075
|
};
|
|
4064
4076
|
}
|
|
@@ -4070,7 +4082,7 @@ function createTargetInfoChangedHandler(context, state, sessionRecordingOptions)
|
|
|
4070
4082
|
if (event.targetInfo.type !== "page") return;
|
|
4071
4083
|
const url = event.targetInfo.url;
|
|
4072
4084
|
if (shouldSkipUrl(url)) return;
|
|
4073
|
-
logger$
|
|
4085
|
+
logger$20.debug(`Target URL changed to ${url}, checking if recorder injection needed`);
|
|
4074
4086
|
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
4075
4087
|
try {
|
|
4076
4088
|
for (const page of context.pages()) if (page.targetId() === event.targetInfo.targetId) {
|
|
@@ -4078,7 +4090,7 @@ function createTargetInfoChangedHandler(context, state, sessionRecordingOptions)
|
|
|
4078
4090
|
break;
|
|
4079
4091
|
}
|
|
4080
4092
|
} catch (error) {
|
|
4081
|
-
logger$
|
|
4093
|
+
logger$20.debug("Error handling target info change: " + require_utils.errorMessage(error));
|
|
4082
4094
|
}
|
|
4083
4095
|
};
|
|
4084
4096
|
}
|
|
@@ -4239,12 +4251,12 @@ var StagehandInstrumentation = class extends _opentelemetry_instrumentation.Inst
|
|
|
4239
4251
|
*/
|
|
4240
4252
|
async setupSessionRecording(stagehandInstance, sessionId, traceId) {
|
|
4241
4253
|
if (!this._client) {
|
|
4242
|
-
logger$
|
|
4254
|
+
logger$20.debug("No Laminar client available, skipping session recording setup");
|
|
4243
4255
|
return;
|
|
4244
4256
|
}
|
|
4245
4257
|
const context = stagehandInstance.context;
|
|
4246
4258
|
if (!context || !context.conn) {
|
|
4247
|
-
logger$
|
|
4259
|
+
logger$20.debug("No context or connection available, skipping session recording setup");
|
|
4248
4260
|
return;
|
|
4249
4261
|
}
|
|
4250
4262
|
const recorderState = {
|
|
@@ -4274,11 +4286,11 @@ var StagehandInstrumentation = class extends _opentelemetry_instrumentation.Inst
|
|
|
4274
4286
|
for (const page of pages) try {
|
|
4275
4287
|
if (await injectRecorderViaCDP(page, recorderState, context.conn, this._sessionRecordingOptions, bindingHandler) !== null) injectedCount++;
|
|
4276
4288
|
} catch (error) {
|
|
4277
|
-
logger$
|
|
4289
|
+
logger$20.debug("Error injecting recorder into page: " + require_utils.errorMessage(error));
|
|
4278
4290
|
}
|
|
4279
|
-
logger$
|
|
4291
|
+
logger$20.debug(`Injected session recorder into ${injectedCount} of ${pages.length} existing page(s)`);
|
|
4280
4292
|
} catch (error) {
|
|
4281
|
-
logger$
|
|
4293
|
+
logger$20.debug("Error accessing pages for recorder injection: " + require_utils.errorMessage(error));
|
|
4282
4294
|
}
|
|
4283
4295
|
}
|
|
4284
4296
|
patchStagehandClose() {
|
|
@@ -4308,12 +4320,12 @@ var StagehandInstrumentation = class extends _opentelemetry_instrumentation.Inst
|
|
|
4308
4320
|
if (recorderState.targetCreatedHandler) try {
|
|
4309
4321
|
context.conn.off("Target.targetCreated", recorderState.targetCreatedHandler);
|
|
4310
4322
|
} catch (error) {
|
|
4311
|
-
logger$
|
|
4323
|
+
logger$20.debug("Error removing target created handler: " + require_utils.errorMessage(error));
|
|
4312
4324
|
}
|
|
4313
4325
|
if (recorderState.targetInfoChangedHandler) try {
|
|
4314
4326
|
context.conn.off("Target.targetInfoChanged", recorderState.targetInfoChangedHandler);
|
|
4315
4327
|
} catch (error) {
|
|
4316
|
-
logger$
|
|
4328
|
+
logger$20.debug("Error removing target info changed handler: " + require_utils.errorMessage(error));
|
|
4317
4329
|
}
|
|
4318
4330
|
}
|
|
4319
4331
|
recorderState.contextIdToSession.clear();
|
|
@@ -4324,7 +4336,7 @@ var StagehandInstrumentation = class extends _opentelemetry_instrumentation.Inst
|
|
|
4324
4336
|
recorderState.targetCreatedHandler = null;
|
|
4325
4337
|
recorderState.targetInfoChangedHandler = null;
|
|
4326
4338
|
this.stagehandInstanceToRecorderState.delete(stagehandInstance);
|
|
4327
|
-
logger$
|
|
4339
|
+
logger$20.debug("Cleaned up session recording for Stagehand instance");
|
|
4328
4340
|
}
|
|
4329
4341
|
patchStagehandGlobalMethod(methodName, sessionId, parentSpan) {
|
|
4330
4342
|
return (original) => async function method(...args) {
|
|
@@ -4672,9 +4684,11 @@ function extractToolDefinitions(config) {
|
|
|
4672
4684
|
}
|
|
4673
4685
|
/**
|
|
4674
4686
|
* Set request attributes on a span from generateContent params.
|
|
4687
|
+
* Also used by the google-aiplatform (Vertex AI) instrumentation, which
|
|
4688
|
+
* reshapes its requests into the same `{model, contents, config}` form.
|
|
4675
4689
|
*/
|
|
4676
|
-
function setRequestAttributes$
|
|
4677
|
-
safeSetAttribute$2(span, LLM_SYSTEM,
|
|
4690
|
+
function setRequestAttributes$5(span, params, traceContent, system = "gemini") {
|
|
4691
|
+
safeSetAttribute$2(span, LLM_SYSTEM, system);
|
|
4678
4692
|
safeSetAttribute$2(span, LLM_REQUEST_MODEL, params?.model);
|
|
4679
4693
|
const config = params?.config;
|
|
4680
4694
|
if (config) {
|
|
@@ -4700,7 +4714,7 @@ function setRequestAttributes$2(span, params, traceContent) {
|
|
|
4700
4714
|
/**
|
|
4701
4715
|
* Set response attributes on a span from a GenerateContentResponse.
|
|
4702
4716
|
*/
|
|
4703
|
-
function setResponseAttributes$
|
|
4717
|
+
function setResponseAttributes$5(span, response, traceContent) {
|
|
4704
4718
|
if (!response) return;
|
|
4705
4719
|
safeSetAttribute$2(span, LLM_RESPONSE_MODEL, response.modelVersion);
|
|
4706
4720
|
const usage = response.usageMetadata;
|
|
@@ -4802,7 +4816,7 @@ function wrapStreamingResponse(span, asyncGen, traceContent) {
|
|
|
4802
4816
|
try {
|
|
4803
4817
|
if (modelVersion !== void 0 || role !== void 0 || accumulatedParts.length > 0 || sawUsageMetadata) {
|
|
4804
4818
|
const mergedParts = mergeTextParts(accumulatedParts);
|
|
4805
|
-
setResponseAttributes$
|
|
4819
|
+
setResponseAttributes$5(span, {
|
|
4806
4820
|
modelVersion,
|
|
4807
4821
|
candidates: [{ content: {
|
|
4808
4822
|
role: role ?? "model",
|
|
@@ -4831,177 +4845,1548 @@ function wrapStreamingResponse(span, asyncGen, traceContent) {
|
|
|
4831
4845
|
return wrapper();
|
|
4832
4846
|
}
|
|
4833
4847
|
//#endregion
|
|
4834
|
-
//#region src/opentelemetry-lib/instrumentation/
|
|
4835
|
-
const
|
|
4836
|
-
const
|
|
4837
|
-
|
|
4848
|
+
//#region src/opentelemetry-lib/instrumentation/anthropic/utils.ts
|
|
4849
|
+
const ANTHROPIC_SYSTEM = "anthropic";
|
|
4850
|
+
const INPUT_MESSAGES$2 = "gen_ai.input.messages";
|
|
4851
|
+
const OUTPUT_MESSAGES$2 = "gen_ai.output.messages";
|
|
4852
|
+
const createStreamState$1 = () => ({
|
|
4853
|
+
usage: {},
|
|
4854
|
+
blocks: [],
|
|
4855
|
+
completion: ""
|
|
4856
|
+
});
|
|
4857
|
+
const toJson$1 = (value) => {
|
|
4858
|
+
if (value === void 0 || value === null) return void 0;
|
|
4859
|
+
return JSON.stringify(value);
|
|
4860
|
+
};
|
|
4861
|
+
const extractStructuredOutputSchema$1 = (params) => {
|
|
4862
|
+
const outputFormat = params?.output_format;
|
|
4863
|
+
if (outputFormat && typeof outputFormat === "object") return toJson$1(outputFormat.schema ?? outputFormat);
|
|
4864
|
+
const schema = params?.output_config?.format?.schema;
|
|
4865
|
+
if (schema && typeof schema === "object") return toJson$1(schema);
|
|
4866
|
+
};
|
|
4867
|
+
const buildInputMessages$1 = (params) => {
|
|
4868
|
+
if (params?.prompt !== void 0 && params?.prompt !== null) return [{
|
|
4869
|
+
role: "user",
|
|
4870
|
+
content: params.prompt
|
|
4871
|
+
}];
|
|
4872
|
+
if (!Array.isArray(params?.messages)) return void 0;
|
|
4873
|
+
const messages = [];
|
|
4874
|
+
if (params.system) messages.push({
|
|
4875
|
+
role: "system",
|
|
4876
|
+
content: params.system
|
|
4877
|
+
});
|
|
4878
|
+
messages.push(...params.messages);
|
|
4879
|
+
return messages;
|
|
4880
|
+
};
|
|
4881
|
+
const setRequestAttributes$4 = (span, params, traceContent) => {
|
|
4882
|
+
safeSetAttribute$2(span, "gen_ai.system", ANTHROPIC_SYSTEM);
|
|
4883
|
+
safeSetAttribute$2(span, "gen_ai.request.model", params?.model);
|
|
4884
|
+
safeSetAttribute$2(span, "gen_ai.request.max_tokens", params?.max_tokens ?? params?.max_tokens_to_sample);
|
|
4885
|
+
safeSetAttribute$2(span, "gen_ai.request.temperature", params?.temperature);
|
|
4886
|
+
safeSetAttribute$2(span, "gen_ai.request.top_p", params?.top_p);
|
|
4887
|
+
safeSetAttribute$2(span, "gen_ai.request.frequency_penalty", params?.frequency_penalty);
|
|
4888
|
+
safeSetAttribute$2(span, "gen_ai.request.presence_penalty", params?.presence_penalty);
|
|
4889
|
+
safeSetAttribute$2(span, "llm.is_streaming", params?.stream);
|
|
4890
|
+
safeSetAttribute$2(span, "anthropic.request.service_tier", params?.service_tier);
|
|
4891
|
+
if (traceContent) {
|
|
4892
|
+
safeSetAttribute$2(span, INPUT_MESSAGES$2, toJson$1(buildInputMessages$1(params)));
|
|
4893
|
+
safeSetAttribute$2(span, "gen_ai.tool.definitions", toJson$1(params?.tools));
|
|
4894
|
+
}
|
|
4895
|
+
safeSetAttribute$2(span, "gen_ai.request.structured_output_schema", extractStructuredOutputSchema$1(params));
|
|
4896
|
+
};
|
|
4897
|
+
const setUsageAttributes$1 = (span, usage, includeTotal) => {
|
|
4898
|
+
if (!usage || typeof usage !== "object" || !Object.keys(usage).length) return;
|
|
4899
|
+
const promptTokens = usage.input_tokens ?? 0;
|
|
4900
|
+
const cacheReadTokens = usage.cache_read_input_tokens ?? 0;
|
|
4901
|
+
const cacheCreationTokens = usage.cache_creation_input_tokens ?? 0;
|
|
4902
|
+
const outputTokens = usage.output_tokens ?? 0;
|
|
4903
|
+
const inputTokens = promptTokens + cacheReadTokens + cacheCreationTokens;
|
|
4904
|
+
span.setAttribute("gen_ai.usage.input_tokens", inputTokens);
|
|
4905
|
+
span.setAttribute("gen_ai.usage.output_tokens", outputTokens);
|
|
4906
|
+
if (includeTotal) span.setAttribute("llm.usage.total_tokens", inputTokens + outputTokens);
|
|
4907
|
+
span.setAttribute("gen_ai.usage.cache_read_input_tokens", cacheReadTokens);
|
|
4908
|
+
span.setAttribute("gen_ai.usage.cache_creation_input_tokens", cacheCreationTokens);
|
|
4909
|
+
};
|
|
4910
|
+
/**
|
|
4911
|
+
* Response attributes for a non-streaming `Message` (or legacy `Completion`).
|
|
4912
|
+
*/
|
|
4913
|
+
const setResponseAttributes$4 = (span, response, traceContent) => {
|
|
4914
|
+
if (!response || typeof response !== "object") return;
|
|
4915
|
+
safeSetAttribute$2(span, "gen_ai.response.model", response.model);
|
|
4916
|
+
safeSetAttribute$2(span, "gen_ai.response.id", response.id);
|
|
4917
|
+
safeSetAttribute$2(span, "anthropic.response.service_tier", response.usage?.service_tier);
|
|
4918
|
+
setUsageAttributes$1(span, response.usage, true);
|
|
4919
|
+
if (traceContent) {
|
|
4920
|
+
const output = {
|
|
4921
|
+
role: response.role ?? "assistant",
|
|
4922
|
+
content: []
|
|
4923
|
+
};
|
|
4924
|
+
if (response.stop_reason) output.stop_reason = response.stop_reason;
|
|
4925
|
+
if (response.completion) output.content.push({
|
|
4926
|
+
type: "text",
|
|
4927
|
+
text: response.completion
|
|
4928
|
+
});
|
|
4929
|
+
else if (Array.isArray(response.content)) output.content.push(...response.content);
|
|
4930
|
+
safeSetAttribute$2(span, OUTPUT_MESSAGES$2, toJson$1([output]));
|
|
4931
|
+
}
|
|
4932
|
+
span.setStatus({ code: _opentelemetry_api.SpanStatusCode.OK });
|
|
4933
|
+
};
|
|
4934
|
+
const mergeUsage = (state, usage) => {
|
|
4935
|
+
if (!usage || typeof usage !== "object") return;
|
|
4936
|
+
for (const [key, value] of Object.entries(usage)) if (value !== null && value !== void 0) state.usage[key] = value;
|
|
4937
|
+
};
|
|
4938
|
+
/**
|
|
4939
|
+
* Fold one raw stream event (`RawMessageStreamEvent`, or a legacy completion
|
|
4940
|
+
* chunk) into the accumulated state.
|
|
4941
|
+
*/
|
|
4942
|
+
const processStreamEvent$1 = (state, event) => {
|
|
4943
|
+
switch (event?.type) {
|
|
4944
|
+
case "message_start": {
|
|
4945
|
+
const message = event.message ?? {};
|
|
4946
|
+
state.id = message.id;
|
|
4947
|
+
state.model = message.model;
|
|
4948
|
+
mergeUsage(state, message.usage);
|
|
4949
|
+
state.serviceTier = message.usage?.service_tier ?? void 0;
|
|
4950
|
+
break;
|
|
4951
|
+
}
|
|
4952
|
+
case "content_block_start": {
|
|
4953
|
+
const block = { ...event.content_block };
|
|
4954
|
+
if ("input" in block) block.input = "";
|
|
4955
|
+
state.blocks[event.index] = block;
|
|
4956
|
+
break;
|
|
4957
|
+
}
|
|
4958
|
+
case "content_block_delta": {
|
|
4959
|
+
const block = state.blocks[event.index];
|
|
4960
|
+
const delta = event.delta ?? {};
|
|
4961
|
+
if (!block) break;
|
|
4962
|
+
if (delta.type === "text_delta") block.text = (block.text ?? "") + (delta.text ?? "");
|
|
4963
|
+
else if (delta.type === "thinking_delta") block.thinking = (block.thinking ?? "") + (delta.thinking ?? "");
|
|
4964
|
+
else if (delta.type === "input_json_delta") block.input = (block.input ?? "") + (delta.partial_json ?? "");
|
|
4965
|
+
break;
|
|
4966
|
+
}
|
|
4967
|
+
case "message_delta":
|
|
4968
|
+
if (event.delta?.stop_reason) state.stopReason = event.delta.stop_reason;
|
|
4969
|
+
mergeUsage(state, event.usage);
|
|
4970
|
+
break;
|
|
4971
|
+
case "completion":
|
|
4972
|
+
state.id = event.id ?? state.id;
|
|
4973
|
+
state.model = event.model ?? state.model;
|
|
4974
|
+
state.completion += event.completion ?? "";
|
|
4975
|
+
if (event.stop_reason) state.stopReason = event.stop_reason;
|
|
4976
|
+
}
|
|
4977
|
+
};
|
|
4978
|
+
const parseToolInput = (input) => {
|
|
4979
|
+
if (typeof input !== "string") return input;
|
|
4980
|
+
if (input === "") return {};
|
|
4981
|
+
try {
|
|
4982
|
+
return JSON.parse(input);
|
|
4983
|
+
} catch {
|
|
4984
|
+
return input;
|
|
4985
|
+
}
|
|
4986
|
+
};
|
|
4987
|
+
const buildStreamedContent = (state) => {
|
|
4988
|
+
if (state.completion) return [{
|
|
4989
|
+
type: "text",
|
|
4990
|
+
text: state.completion
|
|
4991
|
+
}];
|
|
4992
|
+
const content = [];
|
|
4993
|
+
for (const block of state.blocks) {
|
|
4994
|
+
if (!block) continue;
|
|
4995
|
+
if (block.type === "text") {
|
|
4996
|
+
if (block.text) content.push({
|
|
4997
|
+
type: "text",
|
|
4998
|
+
text: block.text
|
|
4999
|
+
});
|
|
5000
|
+
} else if (block.type === "thinking") content.push({
|
|
5001
|
+
type: "thinking",
|
|
5002
|
+
thinking: block.thinking ?? ""
|
|
5003
|
+
});
|
|
5004
|
+
else if ("input" in block) content.push({
|
|
5005
|
+
...block,
|
|
5006
|
+
input: parseToolInput(block.input)
|
|
5007
|
+
});
|
|
5008
|
+
else content.push(block);
|
|
5009
|
+
}
|
|
5010
|
+
return content;
|
|
5011
|
+
};
|
|
5012
|
+
const setStreamingResponseAttributes = (span, state, traceContent) => {
|
|
5013
|
+
safeSetAttribute$2(span, "gen_ai.response.id", state.id);
|
|
5014
|
+
safeSetAttribute$2(span, "gen_ai.response.model", state.model);
|
|
5015
|
+
safeSetAttribute$2(span, "anthropic.response.service_tier", state.usage.service_tier ?? state.serviceTier);
|
|
5016
|
+
setUsageAttributes$1(span, state.usage, false);
|
|
5017
|
+
if (traceContent) {
|
|
5018
|
+
const output = {
|
|
5019
|
+
role: "assistant",
|
|
5020
|
+
content: buildStreamedContent(state)
|
|
5021
|
+
};
|
|
5022
|
+
if (state.stopReason) output.stop_reason = state.stopReason;
|
|
5023
|
+
safeSetAttribute$2(span, OUTPUT_MESSAGES$2, toJson$1([output]));
|
|
5024
|
+
}
|
|
5025
|
+
};
|
|
5026
|
+
//#endregion
|
|
5027
|
+
//#region src/opentelemetry-lib/instrumentation/anthropic/index.ts
|
|
5028
|
+
const logger$19 = require_utils.initializeLogger();
|
|
5029
|
+
const PATCH_DATA_SYMBOL$3 = Symbol("lmnr.anthropic.patchData");
|
|
5030
|
+
const hasOwnPatchData$2 = (proto) => Object.getOwnPropertyDescriptor(proto, PATCH_DATA_SYMBOL$3) !== void 0;
|
|
5031
|
+
const markSpanError$3 = (span, error) => {
|
|
5032
|
+
span.recordException(error);
|
|
5033
|
+
span.setAttribute("error.type", error?.constructor?.name ?? "Error");
|
|
5034
|
+
span.setStatus({ code: _opentelemetry_api.SpanStatusCode.ERROR });
|
|
5035
|
+
};
|
|
5036
|
+
/**
|
|
5037
|
+
* Attribute extraction must never break the user's call.
|
|
5038
|
+
*/
|
|
5039
|
+
const safely$3 = (fn) => {
|
|
5040
|
+
try {
|
|
5041
|
+
fn();
|
|
5042
|
+
} catch (error) {
|
|
5043
|
+
logger$19.debug(`Failed to set anthropic span attributes: ${String(error)}`);
|
|
5044
|
+
}
|
|
5045
|
+
};
|
|
5046
|
+
const isAsyncIterable$3 = (value) => value !== null && typeof value === "object" && typeof value[Symbol.asyncIterator] === "function";
|
|
5047
|
+
/**
|
|
5048
|
+
* Resolve the `Anthropic` client class from the module namespace, its default
|
|
5049
|
+
* export, or the class itself.
|
|
5050
|
+
*/
|
|
5051
|
+
const resolveClientClass$1 = (mod) => {
|
|
5052
|
+
for (const candidate of [
|
|
5053
|
+
mod?.Anthropic,
|
|
5054
|
+
mod?.default,
|
|
5055
|
+
mod
|
|
5056
|
+
]) if (typeof candidate?.Messages === "function") return candidate;
|
|
5057
|
+
};
|
|
5058
|
+
/**
|
|
5059
|
+
* Instruments `@anthropic-ai/sdk`: `messages.create`, `beta.messages.create`
|
|
5060
|
+
* and the legacy `completions.create`. `messages.parse()` and
|
|
5061
|
+
* `messages.stream()` (and their beta counterparts) delegate to `create`, so
|
|
5062
|
+
* they are traced through it without nesting a second span.
|
|
5063
|
+
*/
|
|
5064
|
+
var AnthropicInstrumentation = class extends _opentelemetry_instrumentation.InstrumentationBase {
|
|
4838
5065
|
constructor(config) {
|
|
4839
|
-
super("@lmnr/
|
|
5066
|
+
super("@lmnr/anthropic-instrumentation", require_utils.version, { enabled: true });
|
|
4840
5067
|
this.traceContent = config?.traceContent ?? true;
|
|
4841
5068
|
}
|
|
4842
5069
|
init() {
|
|
4843
|
-
return new _opentelemetry_instrumentation.InstrumentationNodeModuleDefinition("@
|
|
5070
|
+
return new _opentelemetry_instrumentation.InstrumentationNodeModuleDefinition("@anthropic-ai/sdk", [">=0.9.1"], this.patch.bind(this), this.unpatch.bind(this));
|
|
4844
5071
|
}
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
5072
|
+
/**
|
|
5073
|
+
* Accepts the `@anthropic-ai/sdk` module namespace or the `Anthropic`
|
|
5074
|
+
* class (the module's default export).
|
|
5075
|
+
*/
|
|
5076
|
+
manuallyInstrument(module) {
|
|
5077
|
+
if (!resolveClientClass$1(module)) {
|
|
5078
|
+
_opentelemetry_api.diag.warn("Could not find the Anthropic client in anthropic manual instrumentation input. Pass the @anthropic-ai/sdk module.");
|
|
5079
|
+
return;
|
|
5080
|
+
}
|
|
5081
|
+
_opentelemetry_api.diag.debug("Manually instrumenting @anthropic-ai/sdk");
|
|
5082
|
+
this.patch(module);
|
|
4850
5083
|
}
|
|
4851
5084
|
patch(moduleExports) {
|
|
4852
|
-
_opentelemetry_api.diag.debug("Patching @
|
|
4853
|
-
const
|
|
4854
|
-
|
|
5085
|
+
_opentelemetry_api.diag.debug("Patching @anthropic-ai/sdk");
|
|
5086
|
+
const [completions, ...messages] = this.resourcePrototypes(moduleExports);
|
|
5087
|
+
this.patchCreate(completions, "anthropic.completion");
|
|
5088
|
+
for (const proto of messages) this.patchCreate(proto, "anthropic.chat");
|
|
4855
5089
|
return moduleExports;
|
|
4856
5090
|
}
|
|
4857
5091
|
unpatch(moduleExports) {
|
|
4858
|
-
_opentelemetry_api.diag.debug("Unpatching @
|
|
4859
|
-
const
|
|
4860
|
-
|
|
5092
|
+
_opentelemetry_api.diag.debug("Unpatching @anthropic-ai/sdk");
|
|
5093
|
+
for (const proto of this.resourcePrototypes(moduleExports)) {
|
|
5094
|
+
if (!proto || !hasOwnPatchData$2(proto)) continue;
|
|
5095
|
+
proto.create = proto[PATCH_DATA_SYMBOL$3].original;
|
|
5096
|
+
delete proto[PATCH_DATA_SYMBOL$3];
|
|
5097
|
+
}
|
|
4861
5098
|
}
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
5099
|
+
/**
|
|
5100
|
+
* `[Completions, Messages, Beta.Messages]` prototypes; entries are
|
|
5101
|
+
* undefined where the installed SDK version lacks the resource.
|
|
5102
|
+
*/
|
|
5103
|
+
resourcePrototypes(moduleExports) {
|
|
5104
|
+
const client = resolveClientClass$1(moduleExports);
|
|
5105
|
+
return [
|
|
5106
|
+
client?.Completions?.prototype,
|
|
5107
|
+
client?.Messages?.prototype,
|
|
5108
|
+
client?.Beta?.Messages?.prototype
|
|
5109
|
+
];
|
|
4865
5110
|
}
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
_opentelemetry_api.diag.warn("Could not patch GoogleGenAI.prototype.models: descriptor is non-configurable.");
|
|
5111
|
+
/**
|
|
5112
|
+
* Replace `proto.create` with a traced wrapper. Re-patching an already
|
|
5113
|
+
* patched prototype only refreshes `traceContent`, so repeated
|
|
5114
|
+
* initialization never double-wraps.
|
|
5115
|
+
*/
|
|
5116
|
+
patchCreate(proto, spanName) {
|
|
5117
|
+
if (!proto || typeof proto.create !== "function") return;
|
|
5118
|
+
if (hasOwnPatchData$2(proto)) {
|
|
5119
|
+
proto[PATCH_DATA_SYMBOL$3].traceContent = this.traceContent;
|
|
4876
5120
|
return;
|
|
4877
5121
|
}
|
|
4878
5122
|
const patchData = {
|
|
4879
|
-
instrumentation: this,
|
|
4880
5123
|
traceContent: this.traceContent,
|
|
4881
|
-
|
|
5124
|
+
original: proto.create
|
|
4882
5125
|
};
|
|
4883
|
-
Object.defineProperty(
|
|
5126
|
+
Object.defineProperty(proto, PATCH_DATA_SYMBOL$3, {
|
|
4884
5127
|
value: patchData,
|
|
4885
5128
|
writable: true,
|
|
4886
5129
|
configurable: true,
|
|
4887
5130
|
enumerable: false
|
|
4888
5131
|
});
|
|
4889
|
-
|
|
4890
|
-
configurable: true,
|
|
4891
|
-
enumerable: originalModelsDescriptor?.enumerable ?? true,
|
|
4892
|
-
get: function() {
|
|
4893
|
-
if (originalModelsDescriptor?.get) return originalModelsDescriptor.get.call(this);
|
|
4894
|
-
},
|
|
4895
|
-
set: function(value) {
|
|
4896
|
-
patchData.instrumentation.patchModelsInstance(value, patchData);
|
|
4897
|
-
if (originalModelsDescriptor?.set) {
|
|
4898
|
-
originalModelsDescriptor.set.call(this, value);
|
|
4899
|
-
return;
|
|
4900
|
-
}
|
|
4901
|
-
Object.defineProperty(this, "models", {
|
|
4902
|
-
value,
|
|
4903
|
-
writable: true,
|
|
4904
|
-
configurable: true,
|
|
4905
|
-
enumerable: true
|
|
4906
|
-
});
|
|
4907
|
-
}
|
|
4908
|
-
});
|
|
5132
|
+
proto.create = wrapCreate(patchData, spanName);
|
|
4909
5133
|
}
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
5134
|
+
};
|
|
5135
|
+
const wrapCreate = (patchData, spanName) => function(params, ...rest) {
|
|
5136
|
+
const { original } = patchData;
|
|
5137
|
+
if ((0, _opentelemetry_core.isTracingSuppressed)(_opentelemetry_api.context.active())) return original.call(this, params, ...rest);
|
|
5138
|
+
const traceContent = patchData.traceContent;
|
|
5139
|
+
const span = Laminar.startSpan({
|
|
5140
|
+
name: spanName,
|
|
5141
|
+
spanType: "LLM"
|
|
5142
|
+
});
|
|
5143
|
+
safely$3(() => setRequestAttributes$4(span, params, traceContent));
|
|
5144
|
+
let result;
|
|
5145
|
+
try {
|
|
5146
|
+
result = Laminar.withSpan(span, () => ({ value: original.call(this, params, ...rest) })).value;
|
|
5147
|
+
} catch (error) {
|
|
5148
|
+
markSpanError$3(span, error);
|
|
5149
|
+
span.end();
|
|
5150
|
+
throw error;
|
|
4916
5151
|
}
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
if (
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
}
|
|
4941
|
-
}, true);
|
|
4942
|
-
};
|
|
4943
|
-
models.generateContentStream = async function(params) {
|
|
4944
|
-
if ((0, _opentelemetry_core.isTracingSuppressed)(_opentelemetry_api.context.active())) return originalGenerateContentStream(params);
|
|
4945
|
-
const span = Laminar.startSpan({
|
|
4946
|
-
name: "gemini.generate_content_stream",
|
|
4947
|
-
spanType: "LLM"
|
|
4948
|
-
});
|
|
4949
|
-
setRequestAttributes$2(span, params, classPatchData.traceContent);
|
|
4950
|
-
try {
|
|
4951
|
-
return wrapStreamingResponse(span, await originalGenerateContentStream(params), classPatchData.traceContent);
|
|
4952
|
-
} catch (error) {
|
|
4953
|
-
span.setAttribute("error.type", error.constructor?.name ?? "Error");
|
|
4954
|
-
span.recordException(error);
|
|
4955
|
-
span.setStatus({ code: _opentelemetry_api.SpanStatusCode.ERROR });
|
|
4956
|
-
span.end();
|
|
4957
|
-
throw error;
|
|
4958
|
-
}
|
|
4959
|
-
};
|
|
4960
|
-
models[WRAPPED_SYMBOL$1] = true;
|
|
5152
|
+
let ended = false;
|
|
5153
|
+
const fail = (error) => {
|
|
5154
|
+
if (ended) return;
|
|
5155
|
+
ended = true;
|
|
5156
|
+
markSpanError$3(span, error);
|
|
5157
|
+
span.end();
|
|
5158
|
+
};
|
|
5159
|
+
const succeed = (data) => {
|
|
5160
|
+
if (ended) return data;
|
|
5161
|
+
if (params?.stream && isAsyncIterable$3(data)) {
|
|
5162
|
+
ended = true;
|
|
5163
|
+
instrumentStream$2(data, span, traceContent);
|
|
5164
|
+
return data;
|
|
5165
|
+
}
|
|
5166
|
+
ended = true;
|
|
5167
|
+
safely$3(() => setResponseAttributes$4(span, data, traceContent));
|
|
5168
|
+
span.end();
|
|
5169
|
+
return data;
|
|
5170
|
+
};
|
|
5171
|
+
if (typeof result?._thenUnwrap === "function") {
|
|
5172
|
+
const responsePromise = typeof result._responseProps === "function" ? result._responseProps() : result.responsePromise;
|
|
5173
|
+
if (typeof responsePromise?.then === "function") responsePromise.then(void 0, fail);
|
|
5174
|
+
return result._thenUnwrap(succeed);
|
|
4961
5175
|
}
|
|
5176
|
+
if (typeof result?.then === "function") return result.then(succeed, (error) => {
|
|
5177
|
+
fail(error);
|
|
5178
|
+
throw error;
|
|
5179
|
+
});
|
|
5180
|
+
return succeed(result);
|
|
5181
|
+
};
|
|
5182
|
+
/**
|
|
5183
|
+
* Aggregate the events of an SDK `Stream` as the caller iterates it, and end
|
|
5184
|
+
* the span when iteration finishes. The stream object itself is kept, so
|
|
5185
|
+
* `tee()`, `toReadableStream()` and `controller` keep working.
|
|
5186
|
+
*/
|
|
5187
|
+
const instrumentStream$2 = (stream, span, traceContent) => {
|
|
5188
|
+
const originalIterator = stream[Symbol.asyncIterator].bind(stream);
|
|
5189
|
+
const state = createStreamState$1();
|
|
5190
|
+
let ended = false;
|
|
5191
|
+
const end = (error) => {
|
|
5192
|
+
if (ended) return;
|
|
5193
|
+
ended = true;
|
|
5194
|
+
safely$3(() => setStreamingResponseAttributes(span, state, traceContent));
|
|
5195
|
+
if (error !== void 0) markSpanError$3(span, error);
|
|
5196
|
+
else span.setStatus({ code: _opentelemetry_api.SpanStatusCode.OK });
|
|
5197
|
+
span.end();
|
|
5198
|
+
};
|
|
5199
|
+
stream[Symbol.asyncIterator] = async function* () {
|
|
5200
|
+
const iterable = { [Symbol.asyncIterator]: originalIterator };
|
|
5201
|
+
try {
|
|
5202
|
+
for await (const event of iterable) {
|
|
5203
|
+
safely$3(() => processStreamEvent$1(state, event));
|
|
5204
|
+
yield event;
|
|
5205
|
+
}
|
|
5206
|
+
} catch (error) {
|
|
5207
|
+
end(error);
|
|
5208
|
+
throw error;
|
|
5209
|
+
} finally {
|
|
5210
|
+
end();
|
|
5211
|
+
}
|
|
5212
|
+
};
|
|
4962
5213
|
};
|
|
4963
5214
|
//#endregion
|
|
4964
|
-
//#region src/opentelemetry-lib/instrumentation/
|
|
4965
|
-
const
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
"
|
|
4970
|
-
"
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
"
|
|
4976
|
-
"
|
|
4977
|
-
"
|
|
4978
|
-
"
|
|
4979
|
-
|
|
4980
|
-
];
|
|
4981
|
-
const
|
|
4982
|
-
|
|
4983
|
-
"
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
];
|
|
4989
|
-
var KernelInstrumentation = class extends _opentelemetry_instrumentation.InstrumentationBase {
|
|
4990
|
-
constructor() {
|
|
4991
|
-
super("@lmnr/kernel-instrumentation", require_utils.version, { enabled: true });
|
|
4992
|
-
}
|
|
4993
|
-
init() {
|
|
4994
|
-
return new _opentelemetry_instrumentation.InstrumentationNodeModuleDefinition("@onkernel/sdk", [">=0.7.0"], this.patch.bind(this), this.unpatch.bind(this));
|
|
4995
|
-
}
|
|
4996
|
-
manuallyInstrument(kernelModule) {
|
|
4997
|
-
for (const wrappedMethod of WRAPPED_BROWSER_METHODS) this._wrap(kernelModule.Kernel.Browsers.prototype, wrappedMethod, this.patchMethod("Browser", wrappedMethod, "DEFAULT"));
|
|
4998
|
-
for (const wrappedMethod of WRAPPED_COMPUTER_METHODS) if (wrappedMethod === "captureScreenshot") this._wrap(kernelModule.Kernel.Browsers.Computer.prototype, wrappedMethod, this.patchTakeScreenshot());
|
|
4999
|
-
else this._wrap(kernelModule.Kernel.Browsers.Computer.prototype, wrappedMethod, this.patchMethod("Computer", wrappedMethod, "TOOL"));
|
|
5000
|
-
for (const wrappedMethod of WRAPPED_PROCESS_METHODS) this._wrap(kernelModule.Kernel.Browsers.Process.prototype, wrappedMethod, this.patchProcessMethod(wrappedMethod));
|
|
5001
|
-
this._wrap(kernelModule.Kernel.prototype, "app", this.patchAppFactory());
|
|
5215
|
+
//#region src/opentelemetry-lib/instrumentation/cohere/utils.ts
|
|
5216
|
+
const COHERE_SYSTEM = "cohere";
|
|
5217
|
+
const INPUT_MESSAGES$1 = "gen_ai.input.messages";
|
|
5218
|
+
const OUTPUT_MESSAGES$1 = "gen_ai.output.messages";
|
|
5219
|
+
const OPERATION_NAMES = {
|
|
5220
|
+
chat: "chat",
|
|
5221
|
+
completion: "text_completion",
|
|
5222
|
+
rerank: "rerank",
|
|
5223
|
+
embedding: "embeddings"
|
|
5224
|
+
};
|
|
5225
|
+
const V1_ROLES = {
|
|
5226
|
+
USER: "user",
|
|
5227
|
+
CHATBOT: "assistant",
|
|
5228
|
+
SYSTEM: "system",
|
|
5229
|
+
TOOL: "tool"
|
|
5230
|
+
};
|
|
5231
|
+
const asList = (value) => Array.isArray(value) ? value : [];
|
|
5232
|
+
const asInt = (value) => typeof value === "number" && Number.isFinite(value) ? Math.trunc(value) : void 0;
|
|
5233
|
+
const maybeJson = (value) => {
|
|
5234
|
+
if (typeof value !== "string") return value;
|
|
5235
|
+
try {
|
|
5236
|
+
return JSON.parse(value);
|
|
5237
|
+
} catch {
|
|
5238
|
+
return value;
|
|
5002
5239
|
}
|
|
5003
|
-
|
|
5004
|
-
|
|
5240
|
+
};
|
|
5241
|
+
const textPart = (text) => ({
|
|
5242
|
+
type: "text",
|
|
5243
|
+
content: typeof text === "string" ? text : JSON.stringify(text)
|
|
5244
|
+
});
|
|
5245
|
+
const imagePart = (url) => {
|
|
5246
|
+
const comma = url.indexOf(",");
|
|
5247
|
+
if (url.startsWith("data:") && comma !== -1) return {
|
|
5248
|
+
type: "blob",
|
|
5249
|
+
modality: "image",
|
|
5250
|
+
mime_type: url.slice(5, comma).split(";")[0],
|
|
5251
|
+
content: url.slice(comma + 1)
|
|
5252
|
+
};
|
|
5253
|
+
return {
|
|
5254
|
+
type: "uri",
|
|
5255
|
+
modality: "image",
|
|
5256
|
+
uri: url
|
|
5257
|
+
};
|
|
5258
|
+
};
|
|
5259
|
+
/**
|
|
5260
|
+
* Convert v2 message content (a string or a list of content items) into parts.
|
|
5261
|
+
*/
|
|
5262
|
+
const contentParts = (content) => {
|
|
5263
|
+
if (typeof content === "string") return content ? [textPart(content)] : [];
|
|
5264
|
+
const parts = [];
|
|
5265
|
+
for (const item of asList(content)) {
|
|
5266
|
+
if (typeof item === "string") {
|
|
5267
|
+
if (item) parts.push(textPart(item));
|
|
5268
|
+
continue;
|
|
5269
|
+
}
|
|
5270
|
+
if (!item || typeof item !== "object") continue;
|
|
5271
|
+
const url = item.imageUrl?.url;
|
|
5272
|
+
if (item.type === "text") parts.push(textPart(item.text ?? ""));
|
|
5273
|
+
else if (item.type === "thinking") parts.push({
|
|
5274
|
+
type: "thinking",
|
|
5275
|
+
content: item.thinking ?? ""
|
|
5276
|
+
});
|
|
5277
|
+
else if (item.type === "image_url" && typeof url === "string") parts.push(imagePart(url));
|
|
5278
|
+
else parts.push(item);
|
|
5279
|
+
}
|
|
5280
|
+
return parts;
|
|
5281
|
+
};
|
|
5282
|
+
/**
|
|
5283
|
+
* v2 tool message content: a string, or a list of text / document items.
|
|
5284
|
+
*/
|
|
5285
|
+
const toolResult = (content) => {
|
|
5286
|
+
if (typeof content === "string") return content;
|
|
5287
|
+
const results = asList(content).map((item) => {
|
|
5288
|
+
if (item?.type === "text") return item.text;
|
|
5289
|
+
if (item?.type === "document") return item.document?.data;
|
|
5290
|
+
return item;
|
|
5291
|
+
});
|
|
5292
|
+
return results.length === 1 ? results[0] : results;
|
|
5293
|
+
};
|
|
5294
|
+
const v2ToolCallParts = (toolCalls) => asList(toolCalls).map((toolCall) => {
|
|
5295
|
+
const part = {
|
|
5296
|
+
type: "tool_call",
|
|
5297
|
+
id: toolCall?.id ?? "",
|
|
5298
|
+
name: toolCall?.function?.name ?? ""
|
|
5299
|
+
};
|
|
5300
|
+
const args = toolCall?.function?.arguments;
|
|
5301
|
+
if (args !== void 0 && args !== null) part.arguments = maybeJson(args);
|
|
5302
|
+
return part;
|
|
5303
|
+
});
|
|
5304
|
+
const v1ToolCallParts = (toolCalls) => asList(toolCalls).map((toolCall) => ({
|
|
5305
|
+
type: "tool_call",
|
|
5306
|
+
name: toolCall?.name ?? "",
|
|
5307
|
+
arguments: toolCall?.parameters
|
|
5308
|
+
}));
|
|
5309
|
+
const v1ToolResultParts = (toolResults) => asList(toolResults).map((toolResult) => ({
|
|
5310
|
+
type: "tool_call_response",
|
|
5311
|
+
name: toolResult?.call?.name ?? "",
|
|
5312
|
+
result: toolResult?.outputs
|
|
5313
|
+
}));
|
|
5314
|
+
const v1Message = (message) => {
|
|
5315
|
+
const role = String(message?.role ?? "USER");
|
|
5316
|
+
const parts = [];
|
|
5317
|
+
if (message?.message) parts.push(textPart(message.message));
|
|
5318
|
+
parts.push(...v1ToolCallParts(message?.toolCalls));
|
|
5319
|
+
parts.push(...v1ToolResultParts(message?.toolResults));
|
|
5320
|
+
return {
|
|
5321
|
+
role: V1_ROLES[role.toUpperCase()] ?? role.toLowerCase(),
|
|
5322
|
+
parts
|
|
5323
|
+
};
|
|
5324
|
+
};
|
|
5325
|
+
const v2Message = (message) => {
|
|
5326
|
+
const role = message?.role ?? "user";
|
|
5327
|
+
if (role === "tool") return {
|
|
5328
|
+
role: "tool",
|
|
5329
|
+
parts: [{
|
|
5330
|
+
type: "tool_call_response",
|
|
5331
|
+
id: message?.toolCallId ?? "",
|
|
5332
|
+
result: toolResult(message?.content)
|
|
5333
|
+
}]
|
|
5334
|
+
};
|
|
5335
|
+
const parts = contentParts(message?.content);
|
|
5336
|
+
if (message?.toolPlan) parts.push(textPart(message.toolPlan));
|
|
5337
|
+
parts.push(...v2ToolCallParts(message?.toolCalls));
|
|
5338
|
+
return {
|
|
5339
|
+
role,
|
|
5340
|
+
parts
|
|
5341
|
+
};
|
|
5342
|
+
};
|
|
5343
|
+
const v1ChatInput = (params) => {
|
|
5344
|
+
const messages = [];
|
|
5345
|
+
if (params?.preamble) messages.push({
|
|
5346
|
+
role: "system",
|
|
5347
|
+
parts: [textPart(params.preamble)]
|
|
5348
|
+
});
|
|
5349
|
+
messages.push(...asList(params?.chatHistory).map(v1Message));
|
|
5350
|
+
const toolResults = v1ToolResultParts(params?.toolResults);
|
|
5351
|
+
if (toolResults.length > 0) messages.push({
|
|
5352
|
+
role: "tool",
|
|
5353
|
+
parts: toolResults
|
|
5354
|
+
});
|
|
5355
|
+
if (params?.message) messages.push({
|
|
5356
|
+
role: "user",
|
|
5357
|
+
parts: [textPart(params.message)]
|
|
5358
|
+
});
|
|
5359
|
+
return messages;
|
|
5360
|
+
};
|
|
5361
|
+
const rerankInput = (params) => {
|
|
5362
|
+
const messages = asList(params?.documents).map((document) => {
|
|
5363
|
+
const text = typeof document === "string" ? document : document?.text;
|
|
5364
|
+
return {
|
|
5365
|
+
role: "system",
|
|
5366
|
+
parts: [textPart(text ?? document)]
|
|
5367
|
+
};
|
|
5368
|
+
});
|
|
5369
|
+
if (params?.query) messages.push({
|
|
5370
|
+
role: "user",
|
|
5371
|
+
parts: [textPart(params.query)]
|
|
5372
|
+
});
|
|
5373
|
+
return messages;
|
|
5374
|
+
};
|
|
5375
|
+
const embedInput = (params) => {
|
|
5376
|
+
const parts = [...asList(params?.texts).filter((text) => typeof text === "string").map(textPart), ...asList(params?.images).filter((image) => typeof image === "string").map(imagePart)];
|
|
5377
|
+
for (const input of asList(params?.inputs)) parts.push(...contentParts(input?.content));
|
|
5378
|
+
return parts.length > 0 ? [{
|
|
5379
|
+
role: "user",
|
|
5380
|
+
parts
|
|
5381
|
+
}] : [];
|
|
5382
|
+
};
|
|
5383
|
+
const toolDefinitions = (tools) => asList(tools).map((tool) => {
|
|
5384
|
+
const fn = tool?.function ?? tool;
|
|
5385
|
+
const definition = { name: fn?.name };
|
|
5386
|
+
if (fn?.description) definition.description = fn.description;
|
|
5387
|
+
const parameters = fn?.parameters ?? fn?.parameterDefinitions;
|
|
5388
|
+
if (parameters) definition.parameters = parameters;
|
|
5389
|
+
return definition;
|
|
5390
|
+
});
|
|
5391
|
+
const setRequestAttributes$3 = (span, params, requestType, isV2, isStreaming, traceContent) => {
|
|
5392
|
+
safeSetAttribute$2(span, "gen_ai.system", COHERE_SYSTEM);
|
|
5393
|
+
safeSetAttribute$2(span, "gen_ai.operation.name", OPERATION_NAMES[requestType]);
|
|
5394
|
+
safeSetAttribute$2(span, "gen_ai.request.model", params?.model);
|
|
5395
|
+
safeSetAttribute$2(span, "gen_ai.request.max_tokens", asInt(params?.maxTokens));
|
|
5396
|
+
safeSetAttribute$2(span, "gen_ai.request.temperature", params?.temperature);
|
|
5397
|
+
safeSetAttribute$2(span, "gen_ai.request.top_p", params?.p);
|
|
5398
|
+
safeSetAttribute$2(span, "gen_ai.request.top_k", params?.k);
|
|
5399
|
+
safeSetAttribute$2(span, "gen_ai.request.frequency_penalty", params?.frequencyPenalty);
|
|
5400
|
+
safeSetAttribute$2(span, "gen_ai.request.presence_penalty", params?.presencePenalty);
|
|
5401
|
+
safeSetAttribute$2(span, "gen_ai.request.seed", asInt(params?.seed));
|
|
5402
|
+
safeSetAttribute$2(span, "gen_ai.request.stop_sequences", asList(params?.stopSequences).filter((s) => typeof s === "string"));
|
|
5403
|
+
span.setAttribute("llm.is_streaming", isStreaming);
|
|
5404
|
+
if (!traceContent) return;
|
|
5405
|
+
let messages;
|
|
5406
|
+
if (requestType === "chat") {
|
|
5407
|
+
messages = isV2 ? asList(params?.messages).map(v2Message) : v1ChatInput(params);
|
|
5408
|
+
const tools = toolDefinitions(params?.tools);
|
|
5409
|
+
if (tools.length > 0) span.setAttribute("gen_ai.tool.definitions", JSON.stringify(tools));
|
|
5410
|
+
} else if (requestType === "completion") messages = params?.prompt ? [{
|
|
5411
|
+
role: "user",
|
|
5412
|
+
parts: [textPart(params.prompt)]
|
|
5413
|
+
}] : [];
|
|
5414
|
+
else if (requestType === "rerank") messages = rerankInput(params);
|
|
5415
|
+
else messages = embedInput(params);
|
|
5416
|
+
if (messages.length > 0) span.setAttribute(INPUT_MESSAGES$1, JSON.stringify(messages));
|
|
5417
|
+
};
|
|
5418
|
+
/**
|
|
5419
|
+
* `usage` (v2) / `meta` (v1): actual `tokens`, falling back to `billedUnits`.
|
|
5420
|
+
*/
|
|
5421
|
+
const setUsage = (span, usage) => {
|
|
5422
|
+
const inputTokens = asInt(usage?.tokens?.inputTokens) ?? asInt(usage?.billedUnits?.inputTokens);
|
|
5423
|
+
const outputTokens = asInt(usage?.tokens?.outputTokens) ?? asInt(usage?.billedUnits?.outputTokens);
|
|
5424
|
+
safeSetAttribute$2(span, "gen_ai.usage.input_tokens", inputTokens);
|
|
5425
|
+
safeSetAttribute$2(span, "gen_ai.usage.output_tokens", outputTokens);
|
|
5426
|
+
if (inputTokens !== void 0 || outputTokens !== void 0) span.setAttribute("llm.usage.total_tokens", (inputTokens ?? 0) + (outputTokens ?? 0));
|
|
5427
|
+
safeSetAttribute$2(span, "gen_ai.usage.cache_read_input_tokens", asInt(usage?.cachedTokens));
|
|
5428
|
+
};
|
|
5429
|
+
const v1ChatOutput = (response) => {
|
|
5430
|
+
const parts = [];
|
|
5431
|
+
if (response?.text) parts.push(textPart(response.text));
|
|
5432
|
+
parts.push(...v1ToolCallParts(response?.toolCalls));
|
|
5433
|
+
return {
|
|
5434
|
+
role: "assistant",
|
|
5435
|
+
parts
|
|
5436
|
+
};
|
|
5437
|
+
};
|
|
5438
|
+
const v2ChatOutput = (response) => ({
|
|
5439
|
+
...v2Message(response?.message ?? {}),
|
|
5440
|
+
role: "assistant"
|
|
5441
|
+
});
|
|
5442
|
+
const completionOutput = (response) => asList(response?.generations).map((generation) => ({
|
|
5443
|
+
role: "assistant",
|
|
5444
|
+
parts: [textPart(generation?.text ?? "")]
|
|
5445
|
+
}));
|
|
5446
|
+
const rerankOutput = (response) => {
|
|
5447
|
+
const parts = asList(response?.results).map((result) => {
|
|
5448
|
+
let text = `Doc ${result?.index}, Score: ${result?.relevanceScore}`;
|
|
5449
|
+
if (result?.document?.text) text += `\n${result.document.text}`;
|
|
5450
|
+
return textPart(text);
|
|
5451
|
+
});
|
|
5452
|
+
return parts.length > 0 ? [{
|
|
5453
|
+
role: "assistant",
|
|
5454
|
+
parts
|
|
5455
|
+
}] : [];
|
|
5456
|
+
};
|
|
5457
|
+
const setResponseAttributes$3 = (span, response, requestType, isV2, traceContent) => {
|
|
5458
|
+
safeSetAttribute$2(span, "gen_ai.response.id", response?.responseId ?? response?.id);
|
|
5459
|
+
setUsage(span, response?.usage ?? response?.meta);
|
|
5460
|
+
let output = [];
|
|
5461
|
+
if (requestType === "chat") {
|
|
5462
|
+
const message = isV2 ? v2ChatOutput(response) : v1ChatOutput(response);
|
|
5463
|
+
if (response?.finishReason) {
|
|
5464
|
+
const finishReason = String(response.finishReason);
|
|
5465
|
+
message.finish_reason = finishReason;
|
|
5466
|
+
span.setAttribute("gen_ai.response.finish_reasons", [finishReason]);
|
|
5467
|
+
}
|
|
5468
|
+
output = [message];
|
|
5469
|
+
} else if (requestType === "completion") output = completionOutput(response);
|
|
5470
|
+
else if (requestType === "rerank") output = rerankOutput(response);
|
|
5471
|
+
if (output.length > 0 && traceContent) span.setAttribute(OUTPUT_MESSAGES$1, JSON.stringify(output));
|
|
5472
|
+
};
|
|
5473
|
+
/**
|
|
5474
|
+
* v1 `chatStream`: the `stream-end` event carries the full response.
|
|
5475
|
+
*/
|
|
5476
|
+
const createV1ChatStreamAccumulator = () => {
|
|
5477
|
+
let final;
|
|
5478
|
+
let text = "";
|
|
5479
|
+
const toolCalls = [];
|
|
5480
|
+
return {
|
|
5481
|
+
add: (event) => {
|
|
5482
|
+
if (event?.eventType === "text-generation") text += event.text ?? "";
|
|
5483
|
+
else if (event?.eventType === "tool-calls-generation") toolCalls.push(...asList(event.toolCalls));
|
|
5484
|
+
else if (event?.eventType === "stream-end") final = event.response;
|
|
5485
|
+
},
|
|
5486
|
+
response: () => final ?? {
|
|
5487
|
+
text,
|
|
5488
|
+
toolCalls
|
|
5489
|
+
}
|
|
5490
|
+
};
|
|
5491
|
+
};
|
|
5492
|
+
/**
|
|
5493
|
+
* v2 `chatStream`: rebuilds a `V2ChatResponse`-shaped object from the events.
|
|
5494
|
+
*/
|
|
5495
|
+
const createV2ChatStreamAccumulator = () => {
|
|
5496
|
+
let id;
|
|
5497
|
+
let toolPlan = "";
|
|
5498
|
+
let finishReason;
|
|
5499
|
+
let usage;
|
|
5500
|
+
const content = /* @__PURE__ */ new Map();
|
|
5501
|
+
const toolCalls = /* @__PURE__ */ new Map();
|
|
5502
|
+
const indexOf = (event, items, isNew) => {
|
|
5503
|
+
if (typeof event?.index === "number") return event.index;
|
|
5504
|
+
if (isNew || items.size === 0) return items.size;
|
|
5505
|
+
return Math.max(...items.keys());
|
|
5506
|
+
};
|
|
5507
|
+
const sorted = (items) => [...items.entries()].sort(([a], [b]) => a - b).map(([, item]) => item);
|
|
5508
|
+
return {
|
|
5509
|
+
add: (event) => {
|
|
5510
|
+
const message = event?.delta?.message;
|
|
5511
|
+
switch (event?.type) {
|
|
5512
|
+
case "message-start":
|
|
5513
|
+
id = event.id ?? id;
|
|
5514
|
+
break;
|
|
5515
|
+
case "content-start":
|
|
5516
|
+
content.set(indexOf(event, content, true), {
|
|
5517
|
+
type: message?.content?.type ?? "text",
|
|
5518
|
+
text: message?.content?.text ?? "",
|
|
5519
|
+
thinking: message?.content?.thinking ?? ""
|
|
5520
|
+
});
|
|
5521
|
+
break;
|
|
5522
|
+
case "content-delta": {
|
|
5523
|
+
const index = indexOf(event, content, false);
|
|
5524
|
+
const item = content.get(index) ?? {
|
|
5525
|
+
type: "text",
|
|
5526
|
+
text: "",
|
|
5527
|
+
thinking: ""
|
|
5528
|
+
};
|
|
5529
|
+
item.text += message?.content?.text ?? "";
|
|
5530
|
+
item.thinking += message?.content?.thinking ?? "";
|
|
5531
|
+
content.set(index, item);
|
|
5532
|
+
break;
|
|
5533
|
+
}
|
|
5534
|
+
case "tool-plan-delta":
|
|
5535
|
+
toolPlan += message?.toolPlan ?? "";
|
|
5536
|
+
break;
|
|
5537
|
+
case "tool-call-start":
|
|
5538
|
+
toolCalls.set(indexOf(event, toolCalls, true), {
|
|
5539
|
+
id: message?.toolCalls?.id ?? "",
|
|
5540
|
+
type: "function",
|
|
5541
|
+
function: {
|
|
5542
|
+
name: message?.toolCalls?.function?.name ?? "",
|
|
5543
|
+
arguments: message?.toolCalls?.function?.arguments ?? ""
|
|
5544
|
+
}
|
|
5545
|
+
});
|
|
5546
|
+
break;
|
|
5547
|
+
case "tool-call-delta": {
|
|
5548
|
+
const index = indexOf(event, toolCalls, false);
|
|
5549
|
+
const toolCall = toolCalls.get(index) ?? {
|
|
5550
|
+
id: "",
|
|
5551
|
+
type: "function",
|
|
5552
|
+
function: {
|
|
5553
|
+
name: "",
|
|
5554
|
+
arguments: ""
|
|
5555
|
+
}
|
|
5556
|
+
};
|
|
5557
|
+
toolCall.function.arguments += message?.toolCalls?.function?.arguments ?? "";
|
|
5558
|
+
toolCalls.set(index, toolCall);
|
|
5559
|
+
break;
|
|
5560
|
+
}
|
|
5561
|
+
case "message-end":
|
|
5562
|
+
id = event.id ?? id;
|
|
5563
|
+
finishReason = event.delta?.finishReason;
|
|
5564
|
+
usage = event.delta?.usage;
|
|
5565
|
+
}
|
|
5566
|
+
},
|
|
5567
|
+
response: () => ({
|
|
5568
|
+
id,
|
|
5569
|
+
finishReason,
|
|
5570
|
+
usage,
|
|
5571
|
+
message: {
|
|
5572
|
+
role: "assistant",
|
|
5573
|
+
content: sorted(content).map((item) => item.type === "thinking" ? {
|
|
5574
|
+
type: "thinking",
|
|
5575
|
+
thinking: item.thinking
|
|
5576
|
+
} : {
|
|
5577
|
+
type: "text",
|
|
5578
|
+
text: item.text
|
|
5579
|
+
}),
|
|
5580
|
+
toolPlan: toolPlan || void 0,
|
|
5581
|
+
toolCalls: sorted(toolCalls)
|
|
5582
|
+
}
|
|
5583
|
+
})
|
|
5584
|
+
};
|
|
5585
|
+
};
|
|
5586
|
+
//#endregion
|
|
5587
|
+
//#region src/opentelemetry-lib/instrumentation/cohere/index.ts
|
|
5588
|
+
const logger$18 = require_utils.initializeLogger();
|
|
5589
|
+
const PATCH_DATA_SYMBOL$2 = Symbol("lmnr.cohere.patchData");
|
|
5590
|
+
const SPAN_NAMES = {
|
|
5591
|
+
chat: "cohere.chat",
|
|
5592
|
+
completion: "cohere.completion",
|
|
5593
|
+
rerank: "cohere.rerank",
|
|
5594
|
+
embedding: "cohere.embed"
|
|
5595
|
+
};
|
|
5596
|
+
const V1_METHODS = [
|
|
5597
|
+
{
|
|
5598
|
+
method: "chat",
|
|
5599
|
+
requestType: "chat",
|
|
5600
|
+
streaming: false
|
|
5601
|
+
},
|
|
5602
|
+
{
|
|
5603
|
+
method: "chatStream",
|
|
5604
|
+
requestType: "chat",
|
|
5605
|
+
streaming: true
|
|
5606
|
+
},
|
|
5607
|
+
{
|
|
5608
|
+
method: "generate",
|
|
5609
|
+
requestType: "completion",
|
|
5610
|
+
streaming: false
|
|
5611
|
+
},
|
|
5612
|
+
{
|
|
5613
|
+
method: "rerank",
|
|
5614
|
+
requestType: "rerank",
|
|
5615
|
+
streaming: false
|
|
5616
|
+
},
|
|
5617
|
+
{
|
|
5618
|
+
method: "embed",
|
|
5619
|
+
requestType: "embedding",
|
|
5620
|
+
streaming: false
|
|
5621
|
+
}
|
|
5622
|
+
];
|
|
5623
|
+
const V2_METHODS = [
|
|
5624
|
+
{
|
|
5625
|
+
method: "chat",
|
|
5626
|
+
requestType: "chat",
|
|
5627
|
+
streaming: false
|
|
5628
|
+
},
|
|
5629
|
+
{
|
|
5630
|
+
method: "chatStream",
|
|
5631
|
+
requestType: "chat",
|
|
5632
|
+
streaming: true
|
|
5633
|
+
},
|
|
5634
|
+
{
|
|
5635
|
+
method: "rerank",
|
|
5636
|
+
requestType: "rerank",
|
|
5637
|
+
streaming: false
|
|
5638
|
+
},
|
|
5639
|
+
{
|
|
5640
|
+
method: "embed",
|
|
5641
|
+
requestType: "embedding",
|
|
5642
|
+
streaming: false
|
|
5643
|
+
}
|
|
5644
|
+
];
|
|
5645
|
+
const ownPatchData = (proto) => Object.getOwnPropertyDescriptor(proto, PATCH_DATA_SYMBOL$2)?.value;
|
|
5646
|
+
const markSpanError$2 = (span, error) => {
|
|
5647
|
+
span.recordException(error);
|
|
5648
|
+
span.setAttribute("error.type", error?.constructor?.name ?? "Error");
|
|
5649
|
+
span.setStatus({
|
|
5650
|
+
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
5651
|
+
message: error?.message
|
|
5652
|
+
});
|
|
5653
|
+
};
|
|
5654
|
+
/**
|
|
5655
|
+
* Attribute extraction must never break the user's call.
|
|
5656
|
+
*/
|
|
5657
|
+
const safely$2 = (fn) => {
|
|
5658
|
+
try {
|
|
5659
|
+
fn();
|
|
5660
|
+
} catch (error) {
|
|
5661
|
+
logger$18.debug(`Failed to set cohere span attributes: ${String(error)}`);
|
|
5662
|
+
}
|
|
5663
|
+
};
|
|
5664
|
+
const isAsyncIterable$2 = (value) => value !== null && typeof value === "object" && typeof value[Symbol.asyncIterator] === "function";
|
|
5665
|
+
/**
|
|
5666
|
+
* The prototype in `proto`'s chain that defines `method`, so subclasses
|
|
5667
|
+
* (`CohereClient` is a thin `CustomClient` subclass) share one wrapper.
|
|
5668
|
+
*/
|
|
5669
|
+
const definingPrototype = (proto, method) => {
|
|
5670
|
+
for (let p = proto; p && p !== Object.prototype; p = Object.getPrototypeOf(p)) {
|
|
5671
|
+
const descriptor = Object.getOwnPropertyDescriptor(p, method);
|
|
5672
|
+
if (descriptor) return typeof descriptor.value === "function" ? p : void 0;
|
|
5673
|
+
}
|
|
5674
|
+
};
|
|
5675
|
+
/**
|
|
5676
|
+
* Resolve the `cohere-ai` module namespace from the namespace itself or its
|
|
5677
|
+
* default export (ESM default import of the CJS package).
|
|
5678
|
+
*/
|
|
5679
|
+
const resolveModule = (mod) => {
|
|
5680
|
+
for (const candidate of [mod, mod?.default]) if (typeof candidate?.CohereClient === "function") return candidate;
|
|
5681
|
+
};
|
|
5682
|
+
/**
|
|
5683
|
+
* `V2Client` is not exported from the package root, so read its prototype
|
|
5684
|
+
* off a throwaway client: `CohereClient#v2` (lazy getter) or
|
|
5685
|
+
* `CohereClientV2#clientV2`. Constructing a client does no I/O.
|
|
5686
|
+
*/
|
|
5687
|
+
const v2ClientPrototype = (mod) => {
|
|
5688
|
+
const probes = [() => new mod.CohereClient({ token: "lmnr" }).v2, () => new mod.CohereClientV2({ token: "lmnr" }).clientV2];
|
|
5689
|
+
for (const probe of probes) try {
|
|
5690
|
+
const client = probe();
|
|
5691
|
+
if (client && typeof client === "object") return Object.getPrototypeOf(client);
|
|
5692
|
+
} catch {}
|
|
5693
|
+
};
|
|
5694
|
+
/**
|
|
5695
|
+
* Instruments `cohere-ai`: v1 `chat`, `chatStream`, `generate`, `rerank` and
|
|
5696
|
+
* `embed` on `CohereClient`, and v2 `chat`, `chatStream`, `rerank` and
|
|
5697
|
+
* `embed` on `V2Client` (behind `CohereClientV2` and `CohereClient#v2`).
|
|
5698
|
+
*
|
|
5699
|
+
* `CohereClientV2` binds its methods as instance fields in its constructor,
|
|
5700
|
+
* so clients created before instrumentation are not traced.
|
|
5701
|
+
*/
|
|
5702
|
+
var CohereInstrumentation = class extends _opentelemetry_instrumentation.InstrumentationBase {
|
|
5703
|
+
constructor(config) {
|
|
5704
|
+
super("@lmnr/cohere-instrumentation", require_utils.version, { enabled: true });
|
|
5705
|
+
this.traceContent = config?.traceContent ?? true;
|
|
5706
|
+
}
|
|
5707
|
+
init() {
|
|
5708
|
+
return new _opentelemetry_instrumentation.InstrumentationNodeModuleDefinition("cohere-ai", [">=7.0.0"], this.patch.bind(this), this.unpatch.bind(this));
|
|
5709
|
+
}
|
|
5710
|
+
/**
|
|
5711
|
+
* Accepts the `cohere-ai` module namespace (`import * as cohere from
|
|
5712
|
+
* "cohere-ai"`) or its default export.
|
|
5713
|
+
*/
|
|
5714
|
+
manuallyInstrument(module) {
|
|
5715
|
+
if (!resolveModule(module)) {
|
|
5716
|
+
_opentelemetry_api.diag.warn("Could not find CohereClient in cohere manual instrumentation input. Pass the cohere-ai module.");
|
|
5717
|
+
return;
|
|
5718
|
+
}
|
|
5719
|
+
_opentelemetry_api.diag.debug("Manually instrumenting cohere-ai");
|
|
5720
|
+
this.patch(module);
|
|
5721
|
+
}
|
|
5722
|
+
patch(moduleExports) {
|
|
5723
|
+
_opentelemetry_api.diag.debug("Patching cohere-ai");
|
|
5724
|
+
for (const { proto, specs, isV2 } of this.targets(moduleExports)) for (const spec of specs) this.patchMethod(definingPrototype(proto, spec.method), spec, isV2);
|
|
5725
|
+
return moduleExports;
|
|
5726
|
+
}
|
|
5727
|
+
unpatch(moduleExports) {
|
|
5728
|
+
_opentelemetry_api.diag.debug("Unpatching cohere-ai");
|
|
5729
|
+
for (const { proto, specs } of this.targets(moduleExports)) for (const { method } of specs) {
|
|
5730
|
+
const owner = definingPrototype(proto, method);
|
|
5731
|
+
const patched = owner && ownPatchData(owner);
|
|
5732
|
+
if (!patched?.[method]) continue;
|
|
5733
|
+
owner[method] = patched[method].original;
|
|
5734
|
+
delete patched[method];
|
|
5735
|
+
}
|
|
5736
|
+
}
|
|
5737
|
+
targets(moduleExports) {
|
|
5738
|
+
const mod = resolveModule(moduleExports);
|
|
5739
|
+
if (!mod) return [];
|
|
5740
|
+
const v1 = mod.CohereClient.prototype;
|
|
5741
|
+
const v2 = v2ClientPrototype(mod);
|
|
5742
|
+
const targets = [{
|
|
5743
|
+
proto: v1,
|
|
5744
|
+
specs: V1_METHODS,
|
|
5745
|
+
isV2: false
|
|
5746
|
+
}];
|
|
5747
|
+
if (v2 && definingPrototype(v2, "chat") !== definingPrototype(v1, "chat")) targets.push({
|
|
5748
|
+
proto: v2,
|
|
5749
|
+
specs: V2_METHODS,
|
|
5750
|
+
isV2: true
|
|
5751
|
+
});
|
|
5752
|
+
return targets;
|
|
5753
|
+
}
|
|
5754
|
+
/**
|
|
5755
|
+
* Replace `proto[method]` with a traced wrapper. Re-patching an already
|
|
5756
|
+
* patched method only refreshes `traceContent`, so repeated initialization
|
|
5757
|
+
* never double-wraps.
|
|
5758
|
+
*/
|
|
5759
|
+
patchMethod(proto, spec, isV2) {
|
|
5760
|
+
if (!proto) return;
|
|
5761
|
+
let patched = ownPatchData(proto);
|
|
5762
|
+
if (patched?.[spec.method]) {
|
|
5763
|
+
patched[spec.method].traceContent = this.traceContent;
|
|
5764
|
+
return;
|
|
5765
|
+
}
|
|
5766
|
+
if (!patched) {
|
|
5767
|
+
patched = {};
|
|
5768
|
+
Object.defineProperty(proto, PATCH_DATA_SYMBOL$2, {
|
|
5769
|
+
value: patched,
|
|
5770
|
+
writable: true,
|
|
5771
|
+
configurable: true,
|
|
5772
|
+
enumerable: false
|
|
5773
|
+
});
|
|
5774
|
+
}
|
|
5775
|
+
const patchData = {
|
|
5776
|
+
...spec,
|
|
5777
|
+
isV2,
|
|
5778
|
+
traceContent: this.traceContent,
|
|
5779
|
+
original: proto[spec.method]
|
|
5780
|
+
};
|
|
5781
|
+
patched[spec.method] = patchData;
|
|
5782
|
+
proto[spec.method] = wrapMethod$1(patchData);
|
|
5783
|
+
}
|
|
5784
|
+
};
|
|
5785
|
+
const wrapMethod$1 = (patchData) => function(request, ...rest) {
|
|
5786
|
+
const { original, requestType, streaming, isV2 } = patchData;
|
|
5787
|
+
if ((0, _opentelemetry_core.isTracingSuppressed)(_opentelemetry_api.context.active())) return original.call(this, request, ...rest);
|
|
5788
|
+
const traceContent = patchData.traceContent;
|
|
5789
|
+
const span = Laminar.startSpan({
|
|
5790
|
+
name: SPAN_NAMES[requestType],
|
|
5791
|
+
spanType: "LLM"
|
|
5792
|
+
});
|
|
5793
|
+
safely$2(() => setRequestAttributes$3(span, request, requestType, isV2, streaming, traceContent));
|
|
5794
|
+
let result;
|
|
5795
|
+
try {
|
|
5796
|
+
result = Laminar.withSpan(span, () => ({ value: original.call(this, request, ...rest) })).value;
|
|
5797
|
+
} catch (error) {
|
|
5798
|
+
markSpanError$2(span, error);
|
|
5799
|
+
span.end();
|
|
5800
|
+
throw error;
|
|
5801
|
+
}
|
|
5802
|
+
let ended = false;
|
|
5803
|
+
const fail = (error) => {
|
|
5804
|
+
if (ended) return;
|
|
5805
|
+
ended = true;
|
|
5806
|
+
markSpanError$2(span, error);
|
|
5807
|
+
span.end();
|
|
5808
|
+
};
|
|
5809
|
+
const succeed = (data) => {
|
|
5810
|
+
if (ended) return data;
|
|
5811
|
+
ended = true;
|
|
5812
|
+
if (streaming && isAsyncIterable$2(data)) {
|
|
5813
|
+
const accumulator = isV2 ? createV2ChatStreamAccumulator() : createV1ChatStreamAccumulator();
|
|
5814
|
+
instrumentStream$1(data, span, accumulator, isV2, traceContent);
|
|
5815
|
+
return data;
|
|
5816
|
+
}
|
|
5817
|
+
safely$2(() => setResponseAttributes$3(span, data, requestType, isV2, traceContent));
|
|
5818
|
+
span.end();
|
|
5819
|
+
return data;
|
|
5820
|
+
};
|
|
5821
|
+
const rethrow = (error) => {
|
|
5822
|
+
fail(error);
|
|
5823
|
+
throw error;
|
|
5824
|
+
};
|
|
5825
|
+
if (typeof result?.withRawResponse === "function" && typeof result.constructor?.fromPromise === "function") return result.constructor.fromPromise(result.withRawResponse().then((raw) => ({
|
|
5826
|
+
...raw,
|
|
5827
|
+
data: succeed(raw?.data)
|
|
5828
|
+
}), rethrow));
|
|
5829
|
+
if (typeof result?.then === "function") return result.then(succeed, rethrow);
|
|
5830
|
+
return succeed(result);
|
|
5831
|
+
};
|
|
5832
|
+
/**
|
|
5833
|
+
* Aggregate the events of an SDK `Stream` as the caller iterates it, and end
|
|
5834
|
+
* the span when iteration finishes, fails, or is abandoned early.
|
|
5835
|
+
*/
|
|
5836
|
+
const instrumentStream$1 = (stream, span, accumulator, isV2, traceContent) => {
|
|
5837
|
+
const originalIterator = stream[Symbol.asyncIterator].bind(stream);
|
|
5838
|
+
let ended = false;
|
|
5839
|
+
const end = (error) => {
|
|
5840
|
+
if (ended) return;
|
|
5841
|
+
ended = true;
|
|
5842
|
+
safely$2(() => setResponseAttributes$3(span, accumulator.response(), "chat", isV2, traceContent));
|
|
5843
|
+
if (error !== void 0) markSpanError$2(span, error);
|
|
5844
|
+
else span.setStatus({ code: _opentelemetry_api.SpanStatusCode.OK });
|
|
5845
|
+
span.end();
|
|
5846
|
+
};
|
|
5847
|
+
stream[Symbol.asyncIterator] = async function* () {
|
|
5848
|
+
const iterable = { [Symbol.asyncIterator]: originalIterator };
|
|
5849
|
+
try {
|
|
5850
|
+
for await (const event of iterable) {
|
|
5851
|
+
safely$2(() => accumulator.add(event));
|
|
5852
|
+
yield event;
|
|
5853
|
+
}
|
|
5854
|
+
} catch (error) {
|
|
5855
|
+
end(error);
|
|
5856
|
+
throw error;
|
|
5857
|
+
} finally {
|
|
5858
|
+
end();
|
|
5859
|
+
}
|
|
5860
|
+
};
|
|
5861
|
+
};
|
|
5862
|
+
//#endregion
|
|
5863
|
+
//#region src/opentelemetry-lib/instrumentation/google-aiplatform/utils.ts
|
|
5864
|
+
const VERTEX_AI_SYSTEM = "vertex_ai";
|
|
5865
|
+
const VALUE_KINDS = [
|
|
5866
|
+
"stringValue",
|
|
5867
|
+
"numberValue",
|
|
5868
|
+
"boolValue",
|
|
5869
|
+
"structValue",
|
|
5870
|
+
"listValue",
|
|
5871
|
+
"nullValue"
|
|
5872
|
+
];
|
|
5873
|
+
/**
|
|
5874
|
+
* Decode a `google.protobuf.Value` (either a protobufjs message instance or
|
|
5875
|
+
* a plain object, e.g. produced by `aiplatform.helpers.toValue`) into a plain
|
|
5876
|
+
* JS value. Unset oneof members on protobufjs messages default to `null`, so
|
|
5877
|
+
* we prefer the `kind` virtual field when it is available.
|
|
5878
|
+
*/
|
|
5879
|
+
const fromValue = (value) => {
|
|
5880
|
+
if (value === null || value === void 0 || typeof value !== "object") return value;
|
|
5881
|
+
const kind = typeof value.kind === "string" ? value.kind : VALUE_KINDS.find((k) => value[k] !== void 0 && value[k] !== null);
|
|
5882
|
+
switch (kind) {
|
|
5883
|
+
case "stringValue":
|
|
5884
|
+
case "numberValue":
|
|
5885
|
+
case "boolValue": return value[kind];
|
|
5886
|
+
case "structValue": return Object.fromEntries(Object.entries(value.structValue?.fields ?? {}).map(([k, v]) => [k, fromValue(v)]));
|
|
5887
|
+
case "listValue": return (value.listValue?.values ?? []).map(fromValue);
|
|
5888
|
+
case "nullValue": return null;
|
|
5889
|
+
default: return;
|
|
5890
|
+
}
|
|
5891
|
+
};
|
|
5892
|
+
const asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
5893
|
+
const asNumber = (value) => typeof value === "number" && !Number.isNaN(value) ? value : void 0;
|
|
5894
|
+
/**
|
|
5895
|
+
* Strip resource prefixes, e.g.
|
|
5896
|
+
* `projects/p/locations/l/publishers/google/models/text-bison` -> `text-bison`.
|
|
5897
|
+
*/
|
|
5898
|
+
const lastPathSegment = (value) => {
|
|
5899
|
+
if (typeof value !== "string" || value.length === 0) return void 0;
|
|
5900
|
+
return value.split("/").pop() || void 0;
|
|
5901
|
+
};
|
|
5902
|
+
const toInputMessages = (instance) => {
|
|
5903
|
+
const messages = [];
|
|
5904
|
+
if (typeof instance.context === "string" && instance.context.length > 0) messages.push({
|
|
5905
|
+
role: "system",
|
|
5906
|
+
content: instance.context
|
|
5907
|
+
});
|
|
5908
|
+
if (typeof instance.prompt === "string") messages.push({
|
|
5909
|
+
role: "user",
|
|
5910
|
+
content: instance.prompt
|
|
5911
|
+
});
|
|
5912
|
+
if (Array.isArray(instance.messages)) for (const message of instance.messages) {
|
|
5913
|
+
const record = asRecord(message);
|
|
5914
|
+
if (!record) continue;
|
|
5915
|
+
const author = typeof record.author === "string" ? record.author : "user";
|
|
5916
|
+
messages.push({
|
|
5917
|
+
role: author === "bot" ? "assistant" : author,
|
|
5918
|
+
content: record.content
|
|
5919
|
+
});
|
|
5920
|
+
}
|
|
5921
|
+
return messages;
|
|
5922
|
+
};
|
|
5923
|
+
/**
|
|
5924
|
+
* Set request attributes on a span from a `PredictionServiceClient.predict`
|
|
5925
|
+
* request (PaLM-style text / chat models served through Vertex AI).
|
|
5926
|
+
*/
|
|
5927
|
+
const setPredictRequestAttributes = (span, request, traceContent) => {
|
|
5928
|
+
safeSetAttribute$2(span, "gen_ai.system", VERTEX_AI_SYSTEM);
|
|
5929
|
+
safeSetAttribute$2(span, "gen_ai.request.model", lastPathSegment(request?.endpoint));
|
|
5930
|
+
const parameters = asRecord(fromValue(request?.parameters));
|
|
5931
|
+
if (parameters) {
|
|
5932
|
+
safeSetAttribute$2(span, "gen_ai.request.max_tokens", asNumber(parameters.maxOutputTokens));
|
|
5933
|
+
safeSetAttribute$2(span, "gen_ai.request.temperature", asNumber(parameters.temperature));
|
|
5934
|
+
safeSetAttribute$2(span, "gen_ai.request.top_p", asNumber(parameters.topP));
|
|
5935
|
+
safeSetAttribute$2(span, "gen_ai.request.top_k", asNumber(parameters.topK));
|
|
5936
|
+
}
|
|
5937
|
+
if (!traceContent || !Array.isArray(request?.instances)) return;
|
|
5938
|
+
const messages = request.instances.flatMap((instance) => {
|
|
5939
|
+
const record = asRecord(fromValue(instance));
|
|
5940
|
+
return record ? toInputMessages(record) : [];
|
|
5941
|
+
});
|
|
5942
|
+
if (messages.length > 0) safeSetAttribute$2(span, "gen_ai.input.messages", JSON.stringify(messages));
|
|
5943
|
+
};
|
|
5944
|
+
const predictionContent = (prediction) => {
|
|
5945
|
+
const record = asRecord(fromValue(prediction));
|
|
5946
|
+
if (!record) return void 0;
|
|
5947
|
+
if (typeof record.content === "string") return record.content;
|
|
5948
|
+
const firstCandidate = Array.isArray(record.candidates) ? asRecord(record.candidates[0]) : void 0;
|
|
5949
|
+
if (typeof firstCandidate?.content === "string") return firstCandidate.content;
|
|
5950
|
+
};
|
|
5951
|
+
/**
|
|
5952
|
+
* Set response attributes on a span from a `PredictResponse`.
|
|
5953
|
+
*/
|
|
5954
|
+
const setPredictResponseAttributes = (span, response, traceContent) => {
|
|
5955
|
+
if (!response) return;
|
|
5956
|
+
safeSetAttribute$2(span, "gen_ai.response.model", lastPathSegment(response.model));
|
|
5957
|
+
const metadata = asRecord(fromValue(response.metadata));
|
|
5958
|
+
const tokenMetadata = asRecord(metadata?.tokenMetadata);
|
|
5959
|
+
const inputTokens = asNumber(asRecord(tokenMetadata?.inputTokenCount)?.totalTokens);
|
|
5960
|
+
const outputTokens = asNumber(asRecord(tokenMetadata?.outputTokenCount)?.totalTokens);
|
|
5961
|
+
safeSetAttribute$2(span, "gen_ai.usage.input_tokens", inputTokens);
|
|
5962
|
+
safeSetAttribute$2(span, "gen_ai.usage.output_tokens", outputTokens);
|
|
5963
|
+
if (inputTokens !== void 0 && outputTokens !== void 0) safeSetAttribute$2(span, "llm.usage.total_tokens", inputTokens + outputTokens);
|
|
5964
|
+
if (!traceContent || !Array.isArray(response.predictions)) return;
|
|
5965
|
+
const outputMessages = response.predictions.map(predictionContent).filter((content) => content !== void 0).map((content) => ({
|
|
5966
|
+
role: "assistant",
|
|
5967
|
+
content
|
|
5968
|
+
}));
|
|
5969
|
+
if (outputMessages.length > 0) safeSetAttribute$2(span, "gen_ai.output.messages", JSON.stringify(outputMessages));
|
|
5970
|
+
};
|
|
5971
|
+
/**
|
|
5972
|
+
* Reshape a Vertex AI `GenerativeModel` request into the
|
|
5973
|
+
* `{model, contents, config}` form the google-genai helpers understand.
|
|
5974
|
+
* Request-level `generationConfig`, `systemInstruction`, and `tools` fully
|
|
5975
|
+
* override the model-level ones, mirroring the Vertex AI SDK.
|
|
5976
|
+
*/
|
|
5977
|
+
const toGenAiParams = (model, request) => {
|
|
5978
|
+
const isStringRequest = typeof request === "string";
|
|
5979
|
+
const req = isStringRequest ? {} : request ?? {};
|
|
5980
|
+
return {
|
|
5981
|
+
model: lastPathSegment(model?.model),
|
|
5982
|
+
contents: isStringRequest ? request : req.contents,
|
|
5983
|
+
config: {
|
|
5984
|
+
...req.generationConfig ?? model?.generationConfig ?? {},
|
|
5985
|
+
systemInstruction: req.systemInstruction ?? model?.systemInstruction,
|
|
5986
|
+
tools: req.tools ?? model?.tools
|
|
5987
|
+
}
|
|
5988
|
+
};
|
|
5989
|
+
};
|
|
5990
|
+
//#endregion
|
|
5991
|
+
//#region src/opentelemetry-lib/instrumentation/google-aiplatform/index.ts
|
|
5992
|
+
const logger$17 = require_utils.initializeLogger();
|
|
5993
|
+
const PATCH_DATA_SYMBOL$1 = Symbol("lmnr.google-aiplatform.patchData");
|
|
5994
|
+
const hasOwnPatchData$1 = (proto) => Object.getOwnPropertyDescriptor(proto, PATCH_DATA_SYMBOL$1) !== void 0;
|
|
5995
|
+
const GENERATIVE_MODEL_CLASSES = ["GenerativeModel", "GenerativeModelPreview"];
|
|
5996
|
+
const markSpanError$1 = (span, error) => {
|
|
5997
|
+
span.setAttribute("error.type", error?.constructor?.name ?? "Error");
|
|
5998
|
+
span.setStatus({ code: _opentelemetry_api.SpanStatusCode.ERROR });
|
|
5999
|
+
};
|
|
6000
|
+
/**
|
|
6001
|
+
* Attribute extraction must never break the user's call.
|
|
6002
|
+
*/
|
|
6003
|
+
const safely$1 = (fn) => {
|
|
6004
|
+
try {
|
|
6005
|
+
fn();
|
|
6006
|
+
} catch (error) {
|
|
6007
|
+
logger$17.debug(`Failed to set google-aiplatform span attributes: ${String(error)}`);
|
|
6008
|
+
}
|
|
6009
|
+
};
|
|
6010
|
+
/**
|
|
6011
|
+
* Instruments Google Cloud Vertex AI:
|
|
6012
|
+
* - `@google-cloud/vertexai`: `GenerativeModel` (and `GenerativeModelPreview`)
|
|
6013
|
+
* `generateContent` / `generateContentStream`.
|
|
6014
|
+
* - `@google-cloud/aiplatform`: `PredictionServiceClient.predict`.
|
|
6015
|
+
*/
|
|
6016
|
+
var GoogleAIPlatformInstrumentation = class extends _opentelemetry_instrumentation.InstrumentationBase {
|
|
6017
|
+
constructor(config) {
|
|
6018
|
+
super("@lmnr/google-aiplatform-instrumentation", require_utils.version, { enabled: true });
|
|
6019
|
+
this.traceContent = config?.traceContent ?? true;
|
|
6020
|
+
}
|
|
6021
|
+
init() {
|
|
6022
|
+
return [new _opentelemetry_instrumentation.InstrumentationNodeModuleDefinition("@google-cloud/vertexai", [">=1.1.0"], this.patchVertexAI.bind(this), this.unpatchVertexAI.bind(this)), new _opentelemetry_instrumentation.InstrumentationNodeModuleDefinition("@google-cloud/aiplatform", [">=3.10.0"], this.patchAIPlatform.bind(this), this.unpatchAIPlatform.bind(this))];
|
|
6023
|
+
}
|
|
6024
|
+
/**
|
|
6025
|
+
* Accepts the `@google-cloud/vertexai` module (or its `GenerativeModel`
|
|
6026
|
+
* class) and/or the `@google-cloud/aiplatform` module (or its
|
|
6027
|
+
* `PredictionServiceClient` class).
|
|
6028
|
+
*/
|
|
6029
|
+
manuallyInstrument(module) {
|
|
6030
|
+
const modules = Array.isArray(module) ? module : [module];
|
|
6031
|
+
for (const mod of modules) if (this.isVertexAIModule(mod)) {
|
|
6032
|
+
_opentelemetry_api.diag.debug("Manually instrumenting @google-cloud/vertexai");
|
|
6033
|
+
this.patchVertexAI(mod);
|
|
6034
|
+
} else if (this.isAIPlatformModule(mod)) {
|
|
6035
|
+
_opentelemetry_api.diag.debug("Manually instrumenting @google-cloud/aiplatform");
|
|
6036
|
+
this.patchAIPlatform(mod);
|
|
6037
|
+
} else if (typeof mod?.prototype?.generateContent === "function") {
|
|
6038
|
+
_opentelemetry_api.diag.debug("Manually instrumenting Vertex AI GenerativeModel class");
|
|
6039
|
+
this.patchGenerativeModelClass(mod);
|
|
6040
|
+
} else if (typeof mod?.prototype?.predict === "function") {
|
|
6041
|
+
_opentelemetry_api.diag.debug("Manually instrumenting PredictionServiceClient class");
|
|
6042
|
+
this.patchPredictionServiceClass(mod);
|
|
6043
|
+
} else _opentelemetry_api.diag.warn("Could not find GenerativeModel or PredictionServiceClient in google_aiplatform manual instrumentation input. Pass the @google-cloud/vertexai or @google-cloud/aiplatform module.");
|
|
6044
|
+
}
|
|
6045
|
+
isVertexAIModule(mod) {
|
|
6046
|
+
return typeof mod?.GenerativeModel === "function";
|
|
6047
|
+
}
|
|
6048
|
+
isAIPlatformModule(mod) {
|
|
6049
|
+
return typeof mod?.PredictionServiceClient === "function";
|
|
6050
|
+
}
|
|
6051
|
+
patchVertexAI(moduleExports) {
|
|
6052
|
+
_opentelemetry_api.diag.debug("Patching @google-cloud/vertexai");
|
|
6053
|
+
for (const className of GENERATIVE_MODEL_CLASSES) {
|
|
6054
|
+
const cls = moduleExports?.[className];
|
|
6055
|
+
if (typeof cls === "function") this.patchGenerativeModelClass(cls);
|
|
6056
|
+
}
|
|
6057
|
+
return moduleExports;
|
|
6058
|
+
}
|
|
6059
|
+
unpatchVertexAI(moduleExports) {
|
|
6060
|
+
_opentelemetry_api.diag.debug("Unpatching @google-cloud/vertexai");
|
|
6061
|
+
for (const className of GENERATIVE_MODEL_CLASSES) {
|
|
6062
|
+
const cls = moduleExports?.[className];
|
|
6063
|
+
if (typeof cls === "function") this.unpatchMethods(cls.prototype);
|
|
6064
|
+
}
|
|
6065
|
+
}
|
|
6066
|
+
patchAIPlatform(moduleExports) {
|
|
6067
|
+
_opentelemetry_api.diag.debug("Patching @google-cloud/aiplatform");
|
|
6068
|
+
for (const cls of this.predictionServiceClasses(moduleExports)) this.patchPredictionServiceClass(cls);
|
|
6069
|
+
return moduleExports;
|
|
6070
|
+
}
|
|
6071
|
+
unpatchAIPlatform(moduleExports) {
|
|
6072
|
+
_opentelemetry_api.diag.debug("Unpatching @google-cloud/aiplatform");
|
|
6073
|
+
for (const cls of this.predictionServiceClasses(moduleExports)) this.unpatchMethods(cls.prototype);
|
|
6074
|
+
}
|
|
6075
|
+
predictionServiceClasses(moduleExports) {
|
|
6076
|
+
const classes = [
|
|
6077
|
+
moduleExports?.PredictionServiceClient,
|
|
6078
|
+
moduleExports?.v1?.PredictionServiceClient,
|
|
6079
|
+
moduleExports?.v1beta1?.PredictionServiceClient
|
|
6080
|
+
].filter((cls) => typeof cls === "function");
|
|
6081
|
+
return [...new Set(classes)];
|
|
6082
|
+
}
|
|
6083
|
+
patchGenerativeModelClass(cls) {
|
|
6084
|
+
this.patchMethod(cls.prototype, "generateContent", (original, data) => this.wrapGenerateContent(original, data));
|
|
6085
|
+
this.patchMethod(cls.prototype, "generateContentStream", (original, data) => this.wrapGenerateContentStream(original, data));
|
|
6086
|
+
}
|
|
6087
|
+
patchPredictionServiceClass(cls) {
|
|
6088
|
+
this.patchMethod(cls.prototype, "predict", (original, data) => this.wrapPredict(original, data));
|
|
6089
|
+
}
|
|
6090
|
+
/**
|
|
6091
|
+
* Replace `proto[method]` with a traced wrapper. Re-patching an already
|
|
6092
|
+
* patched prototype only refreshes `traceContent`, so repeated
|
|
6093
|
+
* initialization never double-wraps.
|
|
6094
|
+
*/
|
|
6095
|
+
patchMethod(proto, method, makeWrapper) {
|
|
6096
|
+
if (!proto || typeof proto[method] !== "function") return;
|
|
6097
|
+
if (!hasOwnPatchData$1(proto)) Object.defineProperty(proto, PATCH_DATA_SYMBOL$1, {
|
|
6098
|
+
value: {},
|
|
6099
|
+
writable: true,
|
|
6100
|
+
configurable: true,
|
|
6101
|
+
enumerable: false
|
|
6102
|
+
});
|
|
6103
|
+
const patches = proto[PATCH_DATA_SYMBOL$1];
|
|
6104
|
+
const existing = patches[method];
|
|
6105
|
+
if (existing) {
|
|
6106
|
+
existing.traceContent = this.traceContent;
|
|
6107
|
+
return;
|
|
6108
|
+
}
|
|
6109
|
+
const patchData = {
|
|
6110
|
+
traceContent: this.traceContent,
|
|
6111
|
+
original: proto[method]
|
|
6112
|
+
};
|
|
6113
|
+
patches[method] = patchData;
|
|
6114
|
+
proto[method] = makeWrapper(patchData.original, patchData);
|
|
6115
|
+
}
|
|
6116
|
+
unpatchMethods(proto) {
|
|
6117
|
+
if (!proto || !hasOwnPatchData$1(proto)) return;
|
|
6118
|
+
const patches = proto[PATCH_DATA_SYMBOL$1];
|
|
6119
|
+
for (const [method, patchData] of Object.entries(patches)) proto[method] = patchData.original;
|
|
6120
|
+
delete proto[PATCH_DATA_SYMBOL$1];
|
|
6121
|
+
}
|
|
6122
|
+
wrapGenerateContent(original, patchData) {
|
|
6123
|
+
return function(request, ...rest) {
|
|
6124
|
+
if ((0, _opentelemetry_core.isTracingSuppressed)(_opentelemetry_api.context.active())) return original.call(this, request, ...rest);
|
|
6125
|
+
const span = Laminar.startSpan({
|
|
6126
|
+
name: "vertexai.generate_content",
|
|
6127
|
+
spanType: "LLM"
|
|
6128
|
+
});
|
|
6129
|
+
const params = toGenAiParams(this, request);
|
|
6130
|
+
safely$1(() => setRequestAttributes$5(span, params, patchData.traceContent, VERTEX_AI_SYSTEM));
|
|
6131
|
+
return Laminar.withSpan(span, async () => {
|
|
6132
|
+
try {
|
|
6133
|
+
const result = await original.call(this, request, ...rest);
|
|
6134
|
+
safely$1(() => setGenerateContentResponseAttributes(span, result?.response, params.model, patchData.traceContent));
|
|
6135
|
+
return result;
|
|
6136
|
+
} catch (error) {
|
|
6137
|
+
markSpanError$1(span, error);
|
|
6138
|
+
throw error;
|
|
6139
|
+
}
|
|
6140
|
+
}, true);
|
|
6141
|
+
};
|
|
6142
|
+
}
|
|
6143
|
+
wrapGenerateContentStream(original, patchData) {
|
|
6144
|
+
return async function(request, ...rest) {
|
|
6145
|
+
if ((0, _opentelemetry_core.isTracingSuppressed)(_opentelemetry_api.context.active())) return original.call(this, request, ...rest);
|
|
6146
|
+
const span = Laminar.startSpan({
|
|
6147
|
+
name: "vertexai.generate_content_stream",
|
|
6148
|
+
spanType: "LLM"
|
|
6149
|
+
});
|
|
6150
|
+
const params = toGenAiParams(this, request);
|
|
6151
|
+
safely$1(() => setRequestAttributes$5(span, params, patchData.traceContent, VERTEX_AI_SYSTEM));
|
|
6152
|
+
let result;
|
|
6153
|
+
try {
|
|
6154
|
+
result = await Laminar.withSpan(span, () => original.call(this, request, ...rest));
|
|
6155
|
+
} catch (error) {
|
|
6156
|
+
markSpanError$1(span, error);
|
|
6157
|
+
span.end();
|
|
6158
|
+
throw error;
|
|
6159
|
+
}
|
|
6160
|
+
const aggregated = result?.response;
|
|
6161
|
+
if (aggregated && typeof aggregated.then === "function") aggregated.then((response) => {
|
|
6162
|
+
safely$1(() => setGenerateContentResponseAttributes(span, response, params.model, patchData.traceContent));
|
|
6163
|
+
span.end();
|
|
6164
|
+
}, (error) => {
|
|
6165
|
+
span.recordException(error);
|
|
6166
|
+
markSpanError$1(span, error);
|
|
6167
|
+
span.end();
|
|
6168
|
+
});
|
|
6169
|
+
else span.end();
|
|
6170
|
+
return result;
|
|
6171
|
+
};
|
|
6172
|
+
}
|
|
6173
|
+
wrapPredict(original, patchData) {
|
|
6174
|
+
return function(...args) {
|
|
6175
|
+
if ((0, _opentelemetry_core.isTracingSuppressed)(_opentelemetry_api.context.active())) return original.apply(this, args);
|
|
6176
|
+
const span = Laminar.startSpan({
|
|
6177
|
+
name: "aiplatform.predict",
|
|
6178
|
+
spanType: "LLM"
|
|
6179
|
+
});
|
|
6180
|
+
safely$1(() => setPredictRequestAttributes(span, args[0], patchData.traceContent));
|
|
6181
|
+
const callbackIndex = args.length - 1;
|
|
6182
|
+
const callback = args[callbackIndex];
|
|
6183
|
+
if (typeof callback === "function") {
|
|
6184
|
+
const wrappedArgs = [...args];
|
|
6185
|
+
wrappedArgs[callbackIndex] = function(error, response, ...rest) {
|
|
6186
|
+
if (error) {
|
|
6187
|
+
span.recordException(error);
|
|
6188
|
+
markSpanError$1(span, error);
|
|
6189
|
+
} else safely$1(() => setPredictResponseAttributes(span, response, patchData.traceContent));
|
|
6190
|
+
span.end();
|
|
6191
|
+
return callback.call(this, error, response, ...rest);
|
|
6192
|
+
};
|
|
6193
|
+
try {
|
|
6194
|
+
return Laminar.withSpan(span, () => original.apply(this, wrappedArgs));
|
|
6195
|
+
} catch (error) {
|
|
6196
|
+
markSpanError$1(span, error);
|
|
6197
|
+
span.end();
|
|
6198
|
+
throw error;
|
|
6199
|
+
}
|
|
6200
|
+
}
|
|
6201
|
+
return Laminar.withSpan(span, async () => {
|
|
6202
|
+
try {
|
|
6203
|
+
const result = await original.apply(this, args);
|
|
6204
|
+
safely$1(() => setPredictResponseAttributes(span, Array.isArray(result) ? result[0] : result, patchData.traceContent));
|
|
6205
|
+
return result;
|
|
6206
|
+
} catch (error) {
|
|
6207
|
+
markSpanError$1(span, error);
|
|
6208
|
+
throw error;
|
|
6209
|
+
}
|
|
6210
|
+
}, true);
|
|
6211
|
+
};
|
|
6212
|
+
}
|
|
6213
|
+
};
|
|
6214
|
+
const setGenerateContentResponseAttributes = (span, response, requestModel, traceContent) => {
|
|
6215
|
+
setResponseAttributes$5(span, response, traceContent);
|
|
6216
|
+
if (!response?.modelVersion) safeSetAttribute$2(span, "gen_ai.response.model", requestModel);
|
|
6217
|
+
};
|
|
6218
|
+
//#endregion
|
|
6219
|
+
//#region src/opentelemetry-lib/instrumentation/google-genai/index.ts
|
|
6220
|
+
const WRAPPED_SYMBOL$1 = Symbol("lmnr.google-genai.wrapped");
|
|
6221
|
+
const CLASS_PATCH_DATA_SYMBOL = Symbol("lmnr.google-genai.classPatchData");
|
|
6222
|
+
var GoogleGenAiInstrumentation = class extends _opentelemetry_instrumentation.InstrumentationBase {
|
|
6223
|
+
constructor(config) {
|
|
6224
|
+
super("@lmnr/google-genai-instrumentation", require_utils.version, { enabled: true });
|
|
6225
|
+
this.traceContent = config?.traceContent ?? true;
|
|
6226
|
+
}
|
|
6227
|
+
init() {
|
|
6228
|
+
return new _opentelemetry_instrumentation.InstrumentationNodeModuleDefinition("@google/genai", [">=1.0.0"], this.patch.bind(this), this.unpatch.bind(this));
|
|
6229
|
+
}
|
|
6230
|
+
manuallyInstrument(genaiModule) {
|
|
6231
|
+
_opentelemetry_api.diag.debug("Manually instrumenting @google/genai");
|
|
6232
|
+
const GoogleGenAIClass = this.resolveGoogleGenAIClass(genaiModule);
|
|
6233
|
+
if (GoogleGenAIClass) this.patchGoogleGenAIClass(GoogleGenAIClass);
|
|
6234
|
+
else _opentelemetry_api.diag.warn("Could not find GoogleGenAI class in google_genai manual instrumentation input. Pass either GoogleGenAI class or module object with GoogleGenAI export.");
|
|
6235
|
+
}
|
|
6236
|
+
patch(moduleExports) {
|
|
6237
|
+
_opentelemetry_api.diag.debug("Patching @google/genai");
|
|
6238
|
+
const GoogleGenAIClass = this.resolveGoogleGenAIClass(moduleExports);
|
|
6239
|
+
if (GoogleGenAIClass) this.patchGoogleGenAIClass(GoogleGenAIClass);
|
|
6240
|
+
return moduleExports;
|
|
6241
|
+
}
|
|
6242
|
+
unpatch(moduleExports) {
|
|
6243
|
+
_opentelemetry_api.diag.debug("Unpatching @google/genai");
|
|
6244
|
+
const GoogleGenAIClass = this.resolveGoogleGenAIClass(moduleExports);
|
|
6245
|
+
if (GoogleGenAIClass) this.unpatchGoogleGenAIClass(GoogleGenAIClass);
|
|
6246
|
+
}
|
|
6247
|
+
resolveGoogleGenAIClass(moduleOrClass) {
|
|
6248
|
+
if (typeof moduleOrClass === "function") return moduleOrClass;
|
|
6249
|
+
if (moduleOrClass?.GoogleGenAI && typeof moduleOrClass.GoogleGenAI === "function") return moduleOrClass.GoogleGenAI;
|
|
6250
|
+
}
|
|
6251
|
+
patchGoogleGenAIClass(GoogleGenAIClass) {
|
|
6252
|
+
const classPatchData = GoogleGenAIClass[CLASS_PATCH_DATA_SYMBOL];
|
|
6253
|
+
if (classPatchData) {
|
|
6254
|
+
classPatchData.instrumentation = this;
|
|
6255
|
+
classPatchData.traceContent = this.traceContent;
|
|
6256
|
+
return;
|
|
6257
|
+
}
|
|
6258
|
+
const originalModelsDescriptor = Object.getOwnPropertyDescriptor(GoogleGenAIClass.prototype, "models");
|
|
6259
|
+
if (originalModelsDescriptor && !originalModelsDescriptor.configurable) {
|
|
6260
|
+
_opentelemetry_api.diag.warn("Could not patch GoogleGenAI.prototype.models: descriptor is non-configurable.");
|
|
6261
|
+
return;
|
|
6262
|
+
}
|
|
6263
|
+
const patchData = {
|
|
6264
|
+
instrumentation: this,
|
|
6265
|
+
traceContent: this.traceContent,
|
|
6266
|
+
originalModelsDescriptor
|
|
6267
|
+
};
|
|
6268
|
+
Object.defineProperty(GoogleGenAIClass, CLASS_PATCH_DATA_SYMBOL, {
|
|
6269
|
+
value: patchData,
|
|
6270
|
+
writable: true,
|
|
6271
|
+
configurable: true,
|
|
6272
|
+
enumerable: false
|
|
6273
|
+
});
|
|
6274
|
+
Object.defineProperty(GoogleGenAIClass.prototype, "models", {
|
|
6275
|
+
configurable: true,
|
|
6276
|
+
enumerable: originalModelsDescriptor?.enumerable ?? true,
|
|
6277
|
+
get: function() {
|
|
6278
|
+
if (originalModelsDescriptor?.get) return originalModelsDescriptor.get.call(this);
|
|
6279
|
+
},
|
|
6280
|
+
set: function(value) {
|
|
6281
|
+
patchData.instrumentation.patchModelsInstance(value, patchData);
|
|
6282
|
+
if (originalModelsDescriptor?.set) {
|
|
6283
|
+
originalModelsDescriptor.set.call(this, value);
|
|
6284
|
+
return;
|
|
6285
|
+
}
|
|
6286
|
+
Object.defineProperty(this, "models", {
|
|
6287
|
+
value,
|
|
6288
|
+
writable: true,
|
|
6289
|
+
configurable: true,
|
|
6290
|
+
enumerable: true
|
|
6291
|
+
});
|
|
6292
|
+
}
|
|
6293
|
+
});
|
|
6294
|
+
}
|
|
6295
|
+
unpatchGoogleGenAIClass(GoogleGenAIClass) {
|
|
6296
|
+
const classPatchData = GoogleGenAIClass[CLASS_PATCH_DATA_SYMBOL];
|
|
6297
|
+
if (!classPatchData) return;
|
|
6298
|
+
if (classPatchData.originalModelsDescriptor) Object.defineProperty(GoogleGenAIClass.prototype, "models", classPatchData.originalModelsDescriptor);
|
|
6299
|
+
else delete GoogleGenAIClass.prototype.models;
|
|
6300
|
+
delete GoogleGenAIClass[CLASS_PATCH_DATA_SYMBOL];
|
|
6301
|
+
}
|
|
6302
|
+
patchModelsInstance(models, classPatchData) {
|
|
6303
|
+
if (!models || typeof models !== "object") return;
|
|
6304
|
+
if (typeof models.generateContent !== "function") return;
|
|
6305
|
+
if (typeof models.generateContentStream !== "function") return;
|
|
6306
|
+
if (models[WRAPPED_SYMBOL$1]) return;
|
|
6307
|
+
const originalGenerateContent = models.generateContent.bind(models);
|
|
6308
|
+
const originalGenerateContentStream = models.generateContentStream.bind(models);
|
|
6309
|
+
models.generateContent = async function(params) {
|
|
6310
|
+
if ((0, _opentelemetry_core.isTracingSuppressed)(_opentelemetry_api.context.active())) return originalGenerateContent(params);
|
|
6311
|
+
const span = Laminar.startSpan({
|
|
6312
|
+
name: "gemini.generate_content",
|
|
6313
|
+
spanType: "LLM"
|
|
6314
|
+
});
|
|
6315
|
+
setRequestAttributes$5(span, params, classPatchData.traceContent);
|
|
6316
|
+
return Laminar.withSpan(span, async () => {
|
|
6317
|
+
try {
|
|
6318
|
+
const response = await originalGenerateContent(params);
|
|
6319
|
+
setResponseAttributes$5(span, response, classPatchData.traceContent);
|
|
6320
|
+
return response;
|
|
6321
|
+
} catch (error) {
|
|
6322
|
+
span.setAttribute("error.type", error.constructor?.name ?? "Error");
|
|
6323
|
+
span.setStatus({ code: _opentelemetry_api.SpanStatusCode.ERROR });
|
|
6324
|
+
throw error;
|
|
6325
|
+
}
|
|
6326
|
+
}, true);
|
|
6327
|
+
};
|
|
6328
|
+
models.generateContentStream = async function(params) {
|
|
6329
|
+
if ((0, _opentelemetry_core.isTracingSuppressed)(_opentelemetry_api.context.active())) return originalGenerateContentStream(params);
|
|
6330
|
+
const span = Laminar.startSpan({
|
|
6331
|
+
name: "gemini.generate_content_stream",
|
|
6332
|
+
spanType: "LLM"
|
|
6333
|
+
});
|
|
6334
|
+
setRequestAttributes$5(span, params, classPatchData.traceContent);
|
|
6335
|
+
try {
|
|
6336
|
+
return wrapStreamingResponse(span, await originalGenerateContentStream(params), classPatchData.traceContent);
|
|
6337
|
+
} catch (error) {
|
|
6338
|
+
span.setAttribute("error.type", error.constructor?.name ?? "Error");
|
|
6339
|
+
span.recordException(error);
|
|
6340
|
+
span.setStatus({ code: _opentelemetry_api.SpanStatusCode.ERROR });
|
|
6341
|
+
span.end();
|
|
6342
|
+
throw error;
|
|
6343
|
+
}
|
|
6344
|
+
};
|
|
6345
|
+
models[WRAPPED_SYMBOL$1] = true;
|
|
6346
|
+
}
|
|
6347
|
+
};
|
|
6348
|
+
//#endregion
|
|
6349
|
+
//#region src/opentelemetry-lib/instrumentation/kernel.ts
|
|
6350
|
+
const WRAPPED_BROWSER_METHODS = [
|
|
6351
|
+
"create",
|
|
6352
|
+
"retrieve",
|
|
6353
|
+
"list",
|
|
6354
|
+
"deleteByID",
|
|
6355
|
+
"loadExtensions"
|
|
6356
|
+
];
|
|
6357
|
+
const WRAPPED_COMPUTER_METHODS = [
|
|
6358
|
+
"captureScreenshot",
|
|
6359
|
+
"clickMouse",
|
|
6360
|
+
"dragMouse",
|
|
6361
|
+
"moveMouse",
|
|
6362
|
+
"pressKey",
|
|
6363
|
+
"scroll",
|
|
6364
|
+
"typeText"
|
|
6365
|
+
];
|
|
6366
|
+
const WRAPPED_PROCESS_METHODS = [
|
|
6367
|
+
"exec",
|
|
6368
|
+
"kill",
|
|
6369
|
+
"spawn",
|
|
6370
|
+
"status",
|
|
6371
|
+
"stdin",
|
|
6372
|
+
"stdoutStream"
|
|
6373
|
+
];
|
|
6374
|
+
var KernelInstrumentation = class extends _opentelemetry_instrumentation.InstrumentationBase {
|
|
6375
|
+
constructor() {
|
|
6376
|
+
super("@lmnr/kernel-instrumentation", require_utils.version, { enabled: true });
|
|
6377
|
+
}
|
|
6378
|
+
init() {
|
|
6379
|
+
return new _opentelemetry_instrumentation.InstrumentationNodeModuleDefinition("@onkernel/sdk", [">=0.7.0"], this.patch.bind(this), this.unpatch.bind(this));
|
|
6380
|
+
}
|
|
6381
|
+
manuallyInstrument(kernelModule) {
|
|
6382
|
+
for (const wrappedMethod of WRAPPED_BROWSER_METHODS) this._wrap(kernelModule.Kernel.Browsers.prototype, wrappedMethod, this.patchMethod("Browser", wrappedMethod, "DEFAULT"));
|
|
6383
|
+
for (const wrappedMethod of WRAPPED_COMPUTER_METHODS) if (wrappedMethod === "captureScreenshot") this._wrap(kernelModule.Kernel.Browsers.Computer.prototype, wrappedMethod, this.patchTakeScreenshot());
|
|
6384
|
+
else this._wrap(kernelModule.Kernel.Browsers.Computer.prototype, wrappedMethod, this.patchMethod("Computer", wrappedMethod, "TOOL"));
|
|
6385
|
+
for (const wrappedMethod of WRAPPED_PROCESS_METHODS) this._wrap(kernelModule.Kernel.Browsers.Process.prototype, wrappedMethod, this.patchProcessMethod(wrappedMethod));
|
|
6386
|
+
this._wrap(kernelModule.Kernel.prototype, "app", this.patchAppFactory());
|
|
6387
|
+
}
|
|
6388
|
+
patchMethod(className, method, spanType) {
|
|
6389
|
+
const plugin = this;
|
|
5005
6390
|
return (original) => async function(...args) {
|
|
5006
6391
|
return await observe({
|
|
5007
6392
|
name: `${className}.${method}`,
|
|
@@ -5096,100 +6481,950 @@ var KernelInstrumentation = class extends _opentelemetry_instrumentation.Instrum
|
|
|
5096
6481
|
this.manuallyInstrument(moduleExports);
|
|
5097
6482
|
return moduleExports;
|
|
5098
6483
|
}
|
|
5099
|
-
unpatch(moduleExports) {
|
|
5100
|
-
_opentelemetry_api.diag.debug("Unpatching @onkernel/sdk");
|
|
5101
|
-
for (const wrappedMethod of WRAPPED_BROWSER_METHODS) this._unwrap(moduleExports.Kernel.Browsers.prototype, wrappedMethod);
|
|
5102
|
-
for (const wrappedMethod of WRAPPED_COMPUTER_METHODS) this._unwrap(moduleExports.Kernel.Browsers.Computer.prototype, wrappedMethod);
|
|
5103
|
-
for (const wrappedMethod of WRAPPED_PROCESS_METHODS) this._unwrap(moduleExports.Kernel.Browsers.Process.prototype, wrappedMethod);
|
|
5104
|
-
this._unwrap(moduleExports.Kernel.prototype, "app");
|
|
6484
|
+
unpatch(moduleExports) {
|
|
6485
|
+
_opentelemetry_api.diag.debug("Unpatching @onkernel/sdk");
|
|
6486
|
+
for (const wrappedMethod of WRAPPED_BROWSER_METHODS) this._unwrap(moduleExports.Kernel.Browsers.prototype, wrappedMethod);
|
|
6487
|
+
for (const wrappedMethod of WRAPPED_COMPUTER_METHODS) this._unwrap(moduleExports.Kernel.Browsers.Computer.prototype, wrappedMethod);
|
|
6488
|
+
for (const wrappedMethod of WRAPPED_PROCESS_METHODS) this._unwrap(moduleExports.Kernel.Browsers.Process.prototype, wrappedMethod);
|
|
6489
|
+
this._unwrap(moduleExports.Kernel.prototype, "app");
|
|
6490
|
+
}
|
|
6491
|
+
};
|
|
6492
|
+
const DISABLE_OPENAI_RESPONSES_INSTRUMENTATION_CONTEXT_KEY = (0, _opentelemetry_api.createContextKey)("LMNR_DISABLE_OPENAI_RESPONSES_INSTRUMENTATION");
|
|
6493
|
+
const systemInstructionsStorage = new async_hooks.AsyncLocalStorage();
|
|
6494
|
+
const getCurrentSystemInstructions = () => systemInstructionsStorage.getStore();
|
|
6495
|
+
const runWithSystemInstructions = (value, fn) => systemInstructionsStorage.run(value, fn);
|
|
6496
|
+
const wrapStreamWithSystemInstructions = (value, source) => ({ [Symbol.asyncIterator]() {
|
|
6497
|
+
let iterator;
|
|
6498
|
+
const getIterator = () => {
|
|
6499
|
+
if (iterator === void 0) iterator = systemInstructionsStorage.run(value, () => source[Symbol.asyncIterator]());
|
|
6500
|
+
return iterator;
|
|
6501
|
+
};
|
|
6502
|
+
return {
|
|
6503
|
+
next: (..._args) => systemInstructionsStorage.run(value, () => getIterator().next()),
|
|
6504
|
+
return: (returnValue) => {
|
|
6505
|
+
const it = getIterator();
|
|
6506
|
+
return it.return ? systemInstructionsStorage.run(value, () => it.return(returnValue)) : Promise.resolve({
|
|
6507
|
+
done: true,
|
|
6508
|
+
value: returnValue
|
|
6509
|
+
});
|
|
6510
|
+
},
|
|
6511
|
+
throw: (err) => {
|
|
6512
|
+
const it = getIterator();
|
|
6513
|
+
return it.throw ? systemInstructionsStorage.run(value, () => it.throw(err)) : Promise.reject(err);
|
|
6514
|
+
}
|
|
6515
|
+
};
|
|
6516
|
+
} });
|
|
6517
|
+
const spanKind = (spanData) => {
|
|
6518
|
+
if (spanData == null) return "";
|
|
6519
|
+
return typeof spanData.type === "string" ? spanData.type : "";
|
|
6520
|
+
};
|
|
6521
|
+
const spanName = (span, spanData) => {
|
|
6522
|
+
const name = span?.name;
|
|
6523
|
+
if (typeof name === "string" && name) return name;
|
|
6524
|
+
const kind = spanKind(spanData);
|
|
6525
|
+
if (kind) {
|
|
6526
|
+
if (kind === "agent" || kind === "custom" || kind === "function" || kind === "tool") return nameFromSpanData(spanData) || `agents.${kind}`;
|
|
6527
|
+
return `agents.${kind}`;
|
|
6528
|
+
}
|
|
6529
|
+
return "agents.span";
|
|
6530
|
+
};
|
|
6531
|
+
const mapSpanType = (spanData) => {
|
|
6532
|
+
const kind = spanKind(spanData);
|
|
6533
|
+
if (kind === "generation" || kind === "response" || kind === "transcription" || kind === "speech" || kind === "speech_group") return "LLM";
|
|
6534
|
+
if (kind === "function" || kind === "tool" || kind === "mcp_list_tools" || kind === "mcp_tools" || kind === "handoff") return "TOOL";
|
|
6535
|
+
return "DEFAULT";
|
|
6536
|
+
};
|
|
6537
|
+
const nameFromSpanData = (agent) => {
|
|
6538
|
+
if (agent == null) return "";
|
|
6539
|
+
if (typeof agent === "string") return agent;
|
|
6540
|
+
if (typeof agent === "object") {
|
|
6541
|
+
if (typeof agent.name === "string") return agent.name;
|
|
6542
|
+
}
|
|
6543
|
+
return "";
|
|
6544
|
+
};
|
|
6545
|
+
const getFirstNotNull = (d, ...keys) => {
|
|
6546
|
+
if (d == null) return;
|
|
6547
|
+
for (const key of keys) {
|
|
6548
|
+
const v = d[key];
|
|
6549
|
+
if (v !== void 0 && v !== null) return v;
|
|
6550
|
+
}
|
|
6551
|
+
};
|
|
6552
|
+
const modelAsDict = (obj) => {
|
|
6553
|
+
if (obj == null) return;
|
|
6554
|
+
if (typeof obj !== "object") return;
|
|
6555
|
+
if (Array.isArray(obj)) return;
|
|
6556
|
+
return obj;
|
|
6557
|
+
};
|
|
6558
|
+
const normalizeMessages = (data, role = "user") => {
|
|
6559
|
+
if (data == null) return [];
|
|
6560
|
+
if (typeof data === "string") return [{
|
|
6561
|
+
role,
|
|
6562
|
+
content: data
|
|
6563
|
+
}];
|
|
6564
|
+
if (Array.isArray(data)) {
|
|
6565
|
+
const messages = [];
|
|
6566
|
+
for (const item of data) if (item != null && typeof item === "object" && !Array.isArray(item)) messages.push(item);
|
|
6567
|
+
else messages.push({
|
|
6568
|
+
role,
|
|
6569
|
+
content: String(item)
|
|
6570
|
+
});
|
|
6571
|
+
return messages;
|
|
6572
|
+
}
|
|
6573
|
+
if (typeof data === "object") return [data];
|
|
6574
|
+
return [{
|
|
6575
|
+
role,
|
|
6576
|
+
content: String(data)
|
|
6577
|
+
}];
|
|
6578
|
+
};
|
|
6579
|
+
//#endregion
|
|
6580
|
+
//#region src/opentelemetry-lib/instrumentation/openai/utils.ts
|
|
6581
|
+
const INPUT_MESSAGES = "gen_ai.input.messages";
|
|
6582
|
+
const OUTPUT_MESSAGES = "gen_ai.output.messages";
|
|
6583
|
+
const TOOL_DEFINITIONS = "gen_ai.tool.definitions";
|
|
6584
|
+
const STRUCTURED_OUTPUT_SCHEMA = "gen_ai.request.structured_output_schema";
|
|
6585
|
+
const toJson = (value) => {
|
|
6586
|
+
if (value === void 0 || value === null) return void 0;
|
|
6587
|
+
return JSON.stringify(value);
|
|
6588
|
+
};
|
|
6589
|
+
/**
|
|
6590
|
+
* Base URL of the client behind a resource instance. openai >= 4 resources
|
|
6591
|
+
* hold the client in `_client`; the v3 `OpenAIApi` keeps `basePath` itself.
|
|
6592
|
+
*/
|
|
6593
|
+
const getBaseUrl = (instance) => {
|
|
6594
|
+
const baseUrl = instance?._client?.baseURL ?? instance?.basePath ?? instance?.configuration?.basePath;
|
|
6595
|
+
return typeof baseUrl === "string" ? baseUrl : void 0;
|
|
6596
|
+
};
|
|
6597
|
+
const getVendorFromUrl = (baseUrl) => {
|
|
6598
|
+
if (!baseUrl) return "openai";
|
|
6599
|
+
if (baseUrl.includes("openai.azure.com")) return "Azure";
|
|
6600
|
+
if (baseUrl.includes("amazonaws.com") || baseUrl.includes("bedrock")) return "AWS";
|
|
6601
|
+
if (baseUrl.includes("googleapis.com") || baseUrl.includes("vertex")) return "Google";
|
|
6602
|
+
if (baseUrl.includes("openrouter.ai")) return "OpenRouter";
|
|
6603
|
+
return "openai";
|
|
6604
|
+
};
|
|
6605
|
+
/**
|
|
6606
|
+
* Bedrock model ids carry a vendor (`anthropic.claude-...`) and optionally a
|
|
6607
|
+
* cross-region prefix (`us.anthropic.claude-...`); keep only the model.
|
|
6608
|
+
*/
|
|
6609
|
+
const crossRegionCheck = (model) => {
|
|
6610
|
+
if (!model.includes(".")) return model;
|
|
6611
|
+
const parts = model.split(".");
|
|
6612
|
+
if ([
|
|
6613
|
+
"us",
|
|
6614
|
+
"us-gov",
|
|
6615
|
+
"eu",
|
|
6616
|
+
"apac"
|
|
6617
|
+
].includes(parts[0])) return parts.length > 2 ? parts.slice(2).join(".") : model;
|
|
6618
|
+
return parts.slice(1).join(".");
|
|
6619
|
+
};
|
|
6620
|
+
/**
|
|
6621
|
+
* Client attributes shared by every span: the vendor (from the base URL) as
|
|
6622
|
+
* `gen_ai.system`, the base URL itself and, for Azure, the API version.
|
|
6623
|
+
*/
|
|
6624
|
+
const setClientAttributes = (span, instance) => {
|
|
6625
|
+
const baseUrl = getBaseUrl(instance);
|
|
6626
|
+
const vendor = getVendorFromUrl(baseUrl);
|
|
6627
|
+
safeSetAttribute$2(span, "gen_ai.system", vendor);
|
|
6628
|
+
safeSetAttribute$2(span, "gen_ai.request.base_url", baseUrl);
|
|
6629
|
+
const client = instance?._client;
|
|
6630
|
+
safeSetAttribute$2(span, "gen_ai.openai.api_version", client?.apiVersion ?? client?._options?.defaultQuery?.["api-version"]);
|
|
6631
|
+
return vendor;
|
|
6632
|
+
};
|
|
6633
|
+
const extractStructuredOutputSchema = (params) => {
|
|
6634
|
+
const responseFormat = params?.response_format;
|
|
6635
|
+
if (responseFormat?.type === "json_schema") return toJson(responseFormat.json_schema?.schema);
|
|
6636
|
+
};
|
|
6637
|
+
/**
|
|
6638
|
+
* Request attributes common to chat and legacy completions.
|
|
6639
|
+
*/
|
|
6640
|
+
const setRequestAttributes$2 = (span, params, instance) => {
|
|
6641
|
+
const vendor = setClientAttributes(span, instance);
|
|
6642
|
+
let model = params?.model;
|
|
6643
|
+
if (vendor === "AWS" && typeof model === "string") model = crossRegionCheck(model);
|
|
6644
|
+
safeSetAttribute$2(span, "gen_ai.request.model", model);
|
|
6645
|
+
safeSetAttribute$2(span, "gen_ai.request.max_tokens", params?.max_tokens);
|
|
6646
|
+
safeSetAttribute$2(span, "gen_ai.request.temperature", params?.temperature);
|
|
6647
|
+
safeSetAttribute$2(span, "gen_ai.request.top_p", params?.top_p);
|
|
6648
|
+
safeSetAttribute$2(span, "gen_ai.request.frequency_penalty", params?.frequency_penalty);
|
|
6649
|
+
safeSetAttribute$2(span, "gen_ai.request.presence_penalty", params?.presence_penalty);
|
|
6650
|
+
safeSetAttribute$2(span, "llm.user", params?.user);
|
|
6651
|
+
span.setAttribute("llm.is_streaming", Boolean(params?.stream));
|
|
6652
|
+
safeSetAttribute$2(span, "gen_ai.request.reasoning_effort", params?.reasoning_effort);
|
|
6653
|
+
safeSetAttribute$2(span, "openai.request.service_tier", params?.service_tier);
|
|
6654
|
+
safeSetAttribute$2(span, STRUCTURED_OUTPUT_SCHEMA, extractStructuredOutputSchema(params));
|
|
6655
|
+
};
|
|
6656
|
+
const setChatPromptAttributes = (span, params) => {
|
|
6657
|
+
safeSetAttribute$2(span, INPUT_MESSAGES, toJson(params?.messages));
|
|
6658
|
+
safeSetAttribute$2(span, TOOL_DEFINITIONS, toJson(params?.functions ?? params?.tools));
|
|
6659
|
+
};
|
|
6660
|
+
const setCompletionPromptAttributes = (span, params) => {
|
|
6661
|
+
const prompt = params?.prompt;
|
|
6662
|
+
if (prompt !== void 0 && prompt !== null) safeSetAttribute$2(span, INPUT_MESSAGES, toJson((Array.isArray(prompt) ? prompt : [prompt]).map((content) => ({
|
|
6663
|
+
role: "user",
|
|
6664
|
+
content
|
|
6665
|
+
}))));
|
|
6666
|
+
safeSetAttribute$2(span, TOOL_DEFINITIONS, toJson(params?.functions));
|
|
6667
|
+
};
|
|
6668
|
+
const setEmbeddingsPromptAttributes = (span, params) => {
|
|
6669
|
+
const input = params?.input;
|
|
6670
|
+
if (input === void 0 || input === null) return;
|
|
6671
|
+
safeSetAttribute$2(span, INPUT_MESSAGES, toJson((Array.isArray(input) ? input : [input]).map((content) => ({ content }))));
|
|
6672
|
+
};
|
|
6673
|
+
/**
|
|
6674
|
+
* Response attributes for chat completions, legacy completions and
|
|
6675
|
+
* embeddings (and accumulated stream responses, which share the shape).
|
|
6676
|
+
*/
|
|
6677
|
+
const setResponseAttributes$2 = (span, response) => {
|
|
6678
|
+
if (!response || typeof response !== "object") return;
|
|
6679
|
+
if (response.error) {
|
|
6680
|
+
safeSetAttribute$2(span, "gen_ai.prompt.prompt_error", toJson(response.error));
|
|
6681
|
+
return;
|
|
6682
|
+
}
|
|
6683
|
+
safeSetAttribute$2(span, "gen_ai.response.model", response.model);
|
|
6684
|
+
safeSetAttribute$2(span, "gen_ai.response.id", response.id);
|
|
6685
|
+
safeSetAttribute$2(span, "openai.response.system_fingerprint", response.system_fingerprint);
|
|
6686
|
+
safeSetAttribute$2(span, "gen_ai.prompt.prompt_filter_results", toJson(response.prompt_filter_results));
|
|
6687
|
+
safeSetAttribute$2(span, "openai.response.service_tier", response.service_tier);
|
|
6688
|
+
const usage = response.usage;
|
|
6689
|
+
if (!usage || typeof usage !== "object") return;
|
|
6690
|
+
span.setAttribute("llm.usage.total_tokens", usage.total_tokens ?? 0);
|
|
6691
|
+
span.setAttribute("gen_ai.usage.output_tokens", usage.completion_tokens ?? 0);
|
|
6692
|
+
span.setAttribute("gen_ai.usage.input_tokens", usage.prompt_tokens ?? 0);
|
|
6693
|
+
span.setAttribute("gen_ai.usage.cache_read_input_tokens", usage.prompt_tokens_details?.cached_tokens ?? 0);
|
|
6694
|
+
if (usage.completion_tokens_details) span.setAttribute("gen_ai.usage.reasoning_tokens", usage.completion_tokens_details.reasoning_tokens ?? 0);
|
|
6695
|
+
};
|
|
6696
|
+
const setChoicesAttributes = (span, response) => {
|
|
6697
|
+
if (Array.isArray(response?.choices)) safeSetAttribute$2(span, OUTPUT_MESSAGES, toJson(response.choices));
|
|
6698
|
+
};
|
|
6699
|
+
/**
|
|
6700
|
+
* Fold a chat completion chunk into the accumulated `ChatCompletion`.
|
|
6701
|
+
*/
|
|
6702
|
+
const accumulateChatChunk = (complete, chunk) => {
|
|
6703
|
+
if (!chunk || typeof chunk !== "object") return;
|
|
6704
|
+
complete.id ??= chunk.id;
|
|
6705
|
+
complete.model ??= chunk.model;
|
|
6706
|
+
complete.created ??= chunk.created;
|
|
6707
|
+
complete.service_tier = chunk.service_tier ?? complete.service_tier;
|
|
6708
|
+
complete.system_fingerprint = chunk.system_fingerprint ?? complete.system_fingerprint;
|
|
6709
|
+
if (chunk.prompt_filter_results) complete.prompt_filter_results = chunk.prompt_filter_results;
|
|
6710
|
+
const usage = chunk.usage ?? chunk.choices?.[0]?.usage;
|
|
6711
|
+
if (usage) complete.usage = usage;
|
|
6712
|
+
for (const choice of chunk.choices ?? []) {
|
|
6713
|
+
const index = choice.index ?? 0;
|
|
6714
|
+
complete.choices[index] ??= {
|
|
6715
|
+
index,
|
|
6716
|
+
message: {
|
|
6717
|
+
role: "",
|
|
6718
|
+
content: ""
|
|
6719
|
+
}
|
|
6720
|
+
};
|
|
6721
|
+
const target = complete.choices[index];
|
|
6722
|
+
if (choice.finish_reason) target.finish_reason = choice.finish_reason;
|
|
6723
|
+
if (choice.content_filter_results) target.content_filter_results = choice.content_filter_results;
|
|
6724
|
+
const delta = choice.delta ?? {};
|
|
6725
|
+
if (delta.role) target.message.role = delta.role;
|
|
6726
|
+
if (delta.content) target.message.content += delta.content;
|
|
6727
|
+
if (delta.refusal) target.message.refusal = (target.message.refusal ?? "") + delta.refusal;
|
|
6728
|
+
if (delta.function_call) {
|
|
6729
|
+
target.message.function_call ??= {
|
|
6730
|
+
name: "",
|
|
6731
|
+
arguments: ""
|
|
6732
|
+
};
|
|
6733
|
+
target.message.function_call.name += delta.function_call.name ?? "";
|
|
6734
|
+
target.message.function_call.arguments += delta.function_call.arguments ?? "";
|
|
6735
|
+
}
|
|
6736
|
+
for (const toolCall of delta.tool_calls ?? []) {
|
|
6737
|
+
target.message.tool_calls ??= [];
|
|
6738
|
+
const toolIndex = toolCall.index ?? 0;
|
|
6739
|
+
target.message.tool_calls[toolIndex] ??= {
|
|
6740
|
+
id: "",
|
|
6741
|
+
type: "function",
|
|
6742
|
+
function: {
|
|
6743
|
+
name: "",
|
|
6744
|
+
arguments: ""
|
|
6745
|
+
}
|
|
6746
|
+
};
|
|
6747
|
+
const call = target.message.tool_calls[toolIndex];
|
|
6748
|
+
if (toolCall.id) call.id = toolCall.id;
|
|
6749
|
+
if (toolCall.function?.name) call.function.name = toolCall.function.name;
|
|
6750
|
+
call.function.arguments += toolCall.function?.arguments ?? "";
|
|
6751
|
+
}
|
|
6752
|
+
}
|
|
6753
|
+
};
|
|
6754
|
+
/**
|
|
6755
|
+
* Fold a legacy completion chunk into the accumulated `Completion`.
|
|
6756
|
+
*/
|
|
6757
|
+
const accumulateCompletionChunk = (complete, chunk) => {
|
|
6758
|
+
if (!chunk || typeof chunk !== "object") return;
|
|
6759
|
+
complete.id ??= chunk.id;
|
|
6760
|
+
complete.model ??= chunk.model;
|
|
6761
|
+
complete.created ??= chunk.created;
|
|
6762
|
+
if (chunk.usage) complete.usage = chunk.usage;
|
|
6763
|
+
for (const choice of chunk.choices ?? []) {
|
|
6764
|
+
const index = choice.index ?? 0;
|
|
6765
|
+
complete.choices[index] ??= {
|
|
6766
|
+
index,
|
|
6767
|
+
text: ""
|
|
6768
|
+
};
|
|
6769
|
+
const target = complete.choices[index];
|
|
6770
|
+
target.text += choice.text ?? "";
|
|
6771
|
+
if (choice.finish_reason) target.finish_reason = choice.finish_reason;
|
|
6772
|
+
}
|
|
6773
|
+
};
|
|
6774
|
+
/**
|
|
6775
|
+
* `decisions.create` request attributes.
|
|
6776
|
+
*/
|
|
6777
|
+
const setDecisionRequestAttributes = (span, params, instance, traceContent) => {
|
|
6778
|
+
setClientAttributes(span, instance);
|
|
6779
|
+
safeSetAttribute$2(span, "gen_ai.request.model", params?.model);
|
|
6780
|
+
safeSetAttribute$2(span, "llm.user", params?.safety_identifier);
|
|
6781
|
+
if (!traceContent) return;
|
|
6782
|
+
const input = params?.input;
|
|
6783
|
+
if (typeof input === "string") safeSetAttribute$2(span, INPUT_MESSAGES, toJson([{
|
|
6784
|
+
role: "user",
|
|
6785
|
+
content: input
|
|
6786
|
+
}]));
|
|
6787
|
+
else if (Array.isArray(input)) safeSetAttribute$2(span, INPUT_MESSAGES, toJson(input));
|
|
6788
|
+
if (Array.isArray(params?.questions)) safeSetAttribute$2(span, STRUCTURED_OUTPUT_SCHEMA, toJson(params.questions));
|
|
6789
|
+
};
|
|
6790
|
+
const setDecisionResponseAttributes = (span, response, traceContent) => {
|
|
6791
|
+
if (!response || typeof response !== "object") return;
|
|
6792
|
+
safeSetAttribute$2(span, "gen_ai.response.model", response.model);
|
|
6793
|
+
const usage = response.usage;
|
|
6794
|
+
if (usage && typeof usage === "object") {
|
|
6795
|
+
safeSetAttribute$2(span, "gen_ai.usage.input_tokens", usage.input_tokens);
|
|
6796
|
+
safeSetAttribute$2(span, "gen_ai.usage.output_tokens", usage.output_tokens);
|
|
6797
|
+
safeSetAttribute$2(span, "llm.usage.total_tokens", usage.total_tokens);
|
|
6798
|
+
safeSetAttribute$2(span, "gen_ai.usage.cache_read_input_tokens", usage.input_tokens_details?.cached_tokens);
|
|
6799
|
+
safeSetAttribute$2(span, "gen_ai.usage.cache_creation_input_tokens", usage.input_tokens_details?.cache_write_tokens);
|
|
6800
|
+
safeSetAttribute$2(span, "gen_ai.usage.reasoning_tokens", usage.output_tokens_details?.reasoning_tokens);
|
|
6801
|
+
}
|
|
6802
|
+
if (traceContent && Array.isArray(response.answers)) safeSetAttribute$2(span, OUTPUT_MESSAGES, toJson([{
|
|
6803
|
+
role: "assistant",
|
|
6804
|
+
content: toJson(response.answers)
|
|
6805
|
+
}]));
|
|
6806
|
+
};
|
|
6807
|
+
//#endregion
|
|
6808
|
+
//#region src/opentelemetry-lib/instrumentation/openai/responses.ts
|
|
6809
|
+
const RESPONSE_SPAN_NAME = "openai.response";
|
|
6810
|
+
const TERMINAL_STATUSES = /* @__PURE__ */ new Set([
|
|
6811
|
+
"completed",
|
|
6812
|
+
"incomplete",
|
|
6813
|
+
"failed",
|
|
6814
|
+
"cancelled"
|
|
6815
|
+
]);
|
|
6816
|
+
const pendingResponses = /* @__PURE__ */ new Map();
|
|
6817
|
+
const recordedResponses = /* @__PURE__ */ new Set();
|
|
6818
|
+
const MAX_RECORDED_RESPONSES = 1e3;
|
|
6819
|
+
const markRecorded = (responseId) => {
|
|
6820
|
+
if (!responseId) return;
|
|
6821
|
+
recordedResponses.delete(responseId);
|
|
6822
|
+
recordedResponses.add(responseId);
|
|
6823
|
+
if (recordedResponses.size > MAX_RECORDED_RESPONSES) {
|
|
6824
|
+
const [oldest] = recordedResponses;
|
|
6825
|
+
recordedResponses.delete(oldest);
|
|
6826
|
+
}
|
|
6827
|
+
};
|
|
6828
|
+
const createStreamState = () => ({
|
|
6829
|
+
outputText: "",
|
|
6830
|
+
outputBlocks: {}
|
|
6831
|
+
});
|
|
6832
|
+
const getFunctionTools = (params) => Array.isArray(params?.tools) ? params.tools.filter((tool) => tool?.type === "function") : [];
|
|
6833
|
+
const outputBlocksOf = (response) => {
|
|
6834
|
+
const blocks = {};
|
|
6835
|
+
if (!Array.isArray(response?.output)) return blocks;
|
|
6836
|
+
response.output.forEach((block, index) => {
|
|
6837
|
+
blocks[block?.id ?? `output_${index}`] = block;
|
|
6838
|
+
});
|
|
6839
|
+
return blocks;
|
|
6840
|
+
};
|
|
6841
|
+
/**
|
|
6842
|
+
* Data for a call that produced no response to read from (a request error,
|
|
6843
|
+
* or a stream that ended before any snapshot), merged with anything stashed
|
|
6844
|
+
* under `params.response_id`.
|
|
6845
|
+
*/
|
|
6846
|
+
const tracedDataFromRequest = (startTime, params) => {
|
|
6847
|
+
const responseId = params?.response_id ?? "";
|
|
6848
|
+
const existing = pendingResponses.get(responseId);
|
|
6849
|
+
const tools = getFunctionTools(params);
|
|
6850
|
+
return {
|
|
6851
|
+
startTime: existing?.startTime ?? startTime,
|
|
6852
|
+
responseId,
|
|
6853
|
+
input: params?.input ?? existing?.input,
|
|
6854
|
+
instructions: params?.instructions ?? existing?.instructions,
|
|
6855
|
+
tools: tools.length ? tools : existing?.tools,
|
|
6856
|
+
outputBlocks: { ...existing?.outputBlocks },
|
|
6857
|
+
outputText: existing?.outputText,
|
|
6858
|
+
usage: existing?.usage,
|
|
6859
|
+
requestModel: params?.model ?? existing?.requestModel,
|
|
6860
|
+
responseModel: existing?.responseModel,
|
|
6861
|
+
requestReasoningSummary: params?.reasoning?.summary ?? existing?.requestReasoningSummary,
|
|
6862
|
+
requestReasoningEffort: params?.reasoning?.effort ?? existing?.requestReasoningEffort,
|
|
6863
|
+
requestServiceTier: params?.service_tier ?? existing?.requestServiceTier
|
|
6864
|
+
};
|
|
6865
|
+
};
|
|
6866
|
+
/**
|
|
6867
|
+
* Merge a response into the data stashed under its id: request fields prefer
|
|
6868
|
+
* the stash (a `retrieve` carries no request body), response fields and
|
|
6869
|
+
* output blocks prefer the new response.
|
|
6870
|
+
*/
|
|
6871
|
+
const tracedDataFromResponse = (startTime, response, params) => {
|
|
6872
|
+
const responseId = response?.id;
|
|
6873
|
+
if (!responseId) return void 0;
|
|
6874
|
+
const existing = pendingResponses.get(responseId);
|
|
6875
|
+
const tools = [...existing?.tools ?? [], ...getFunctionTools(params)];
|
|
6876
|
+
return {
|
|
6877
|
+
startTime: existing?.startTime ?? startTime,
|
|
6878
|
+
responseId,
|
|
6879
|
+
input: existing?.input ?? params?.input,
|
|
6880
|
+
instructions: existing?.instructions ?? params?.instructions,
|
|
6881
|
+
tools: tools.length ? tools : void 0,
|
|
6882
|
+
outputBlocks: {
|
|
6883
|
+
...existing?.outputBlocks,
|
|
6884
|
+
...outputBlocksOf(response)
|
|
6885
|
+
},
|
|
6886
|
+
outputText: response.output_text || existing?.outputText,
|
|
6887
|
+
usage: response.usage ?? existing?.usage,
|
|
6888
|
+
requestModel: existing?.requestModel ?? params?.model,
|
|
6889
|
+
responseModel: response.model ?? existing?.responseModel,
|
|
6890
|
+
requestReasoningSummary: existing?.requestReasoningSummary ?? params?.reasoning?.summary,
|
|
6891
|
+
requestReasoningEffort: existing?.requestReasoningEffort ?? params?.reasoning?.effort,
|
|
6892
|
+
requestServiceTier: existing?.requestServiceTier ?? params?.service_tier,
|
|
6893
|
+
responseServiceTier: response.service_tier ?? existing?.responseServiceTier
|
|
6894
|
+
};
|
|
6895
|
+
};
|
|
6896
|
+
const buildInputMessages = (data) => {
|
|
6897
|
+
const messages = [];
|
|
6898
|
+
if (typeof data.instructions === "string" && data.instructions) messages.push({
|
|
6899
|
+
role: "system",
|
|
6900
|
+
content: data.instructions
|
|
6901
|
+
});
|
|
6902
|
+
if (typeof data.input === "string") messages.push({
|
|
6903
|
+
role: "user",
|
|
6904
|
+
content: data.input
|
|
6905
|
+
});
|
|
6906
|
+
else if (Array.isArray(data.input)) for (const item of data.input) messages.push(item && typeof item === "object" && !("type" in item) ? {
|
|
6907
|
+
...item,
|
|
6908
|
+
type: "message"
|
|
6909
|
+
} : item);
|
|
6910
|
+
return messages.length ? messages : void 0;
|
|
6911
|
+
};
|
|
6912
|
+
const buildOutputMessages = (data) => {
|
|
6913
|
+
const blocks = Object.values(data.outputBlocks).filter(Boolean);
|
|
6914
|
+
if (!blocks.length && data.outputText) return [{
|
|
6915
|
+
type: "message",
|
|
6916
|
+
role: "assistant",
|
|
6917
|
+
content: [{
|
|
6918
|
+
type: "output_text",
|
|
6919
|
+
text: data.outputText
|
|
6920
|
+
}]
|
|
6921
|
+
}];
|
|
6922
|
+
return blocks;
|
|
6923
|
+
};
|
|
6924
|
+
const setDataAttributes = (span, data, instance, traceContent) => {
|
|
6925
|
+
setClientAttributes(span, instance);
|
|
6926
|
+
safeSetAttribute$2(span, "gen_ai.request.model", data.requestModel);
|
|
6927
|
+
safeSetAttribute$2(span, "gen_ai.response.id", data.responseId);
|
|
6928
|
+
safeSetAttribute$2(span, "gen_ai.response.model", data.responseModel);
|
|
6929
|
+
const usage = data.usage;
|
|
6930
|
+
if (usage && typeof usage === "object") {
|
|
6931
|
+
safeSetAttribute$2(span, "gen_ai.usage.input_tokens", usage.input_tokens);
|
|
6932
|
+
safeSetAttribute$2(span, "gen_ai.usage.output_tokens", usage.output_tokens);
|
|
6933
|
+
safeSetAttribute$2(span, "llm.usage.total_tokens", usage.total_tokens);
|
|
6934
|
+
safeSetAttribute$2(span, "gen_ai.usage.cache_read_input_tokens", usage.input_tokens_details?.cached_tokens);
|
|
6935
|
+
span.setAttribute("gen_ai.usage.reasoning_tokens", usage.output_tokens_details?.reasoning_tokens ?? 0);
|
|
6936
|
+
}
|
|
6937
|
+
safeSetAttribute$2(span, "gen_ai.request.reasoning_summary", data.requestReasoningSummary);
|
|
6938
|
+
safeSetAttribute$2(span, "gen_ai.request.reasoning_effort", data.requestReasoningEffort);
|
|
6939
|
+
safeSetAttribute$2(span, "openai.request.service_tier", data.requestServiceTier);
|
|
6940
|
+
safeSetAttribute$2(span, "openai.response.service_tier", data.responseServiceTier);
|
|
6941
|
+
if (traceContent) {
|
|
6942
|
+
safeSetAttribute$2(span, INPUT_MESSAGES, toJson(buildInputMessages(data)));
|
|
6943
|
+
safeSetAttribute$2(span, OUTPUT_MESSAGES, toJson(buildOutputMessages(data)));
|
|
6944
|
+
safeSetAttribute$2(span, TOOL_DEFINITIONS, toJson(data.tools));
|
|
6945
|
+
}
|
|
6946
|
+
};
|
|
6947
|
+
const isDuplicate = (responseId, options) => Boolean(options.isRetrieve) && recordedResponses.has(responseId);
|
|
6948
|
+
const startResponseSpan = (data, options) => {
|
|
6949
|
+
const span = Laminar.startSpan({
|
|
6950
|
+
name: RESPONSE_SPAN_NAME,
|
|
6951
|
+
spanType: "LLM",
|
|
6952
|
+
startTime: data.startTime,
|
|
6953
|
+
context: options.parentContext
|
|
6954
|
+
});
|
|
6955
|
+
setDataAttributes(span, data, options.instance, options.traceContent);
|
|
6956
|
+
return span;
|
|
6957
|
+
};
|
|
6958
|
+
const markFailed = (span, message) => {
|
|
6959
|
+
span.recordException(new Error(message));
|
|
6960
|
+
span.setStatus({
|
|
6961
|
+
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
6962
|
+
message
|
|
6963
|
+
});
|
|
6964
|
+
};
|
|
6965
|
+
const failureMessage = (response) => {
|
|
6966
|
+
switch (response?.status) {
|
|
6967
|
+
case "failed": return response.error?.message ?? "Response failed";
|
|
6968
|
+
case "cancelled": return "Response cancelled";
|
|
6969
|
+
default: return;
|
|
6970
|
+
}
|
|
6971
|
+
};
|
|
6972
|
+
/**
|
|
6973
|
+
* A `create` / `retrieve` that returned a response object. The span is
|
|
6974
|
+
* emitted once the response is final; until then (background mode) its
|
|
6975
|
+
* request data waits in the stash.
|
|
6976
|
+
*/
|
|
6977
|
+
const processResponse = (startTime, response, params, options) => {
|
|
6978
|
+
const data = tracedDataFromResponse(startTime, response, params);
|
|
6979
|
+
if (!data) return;
|
|
6980
|
+
const status = response?.status;
|
|
6981
|
+
if (status && !TERMINAL_STATUSES.has(status)) {
|
|
6982
|
+
pendingResponses.set(data.responseId, data);
|
|
6983
|
+
return;
|
|
6984
|
+
}
|
|
6985
|
+
pendingResponses.delete(data.responseId);
|
|
6986
|
+
if (isDuplicate(data.responseId, options)) return;
|
|
6987
|
+
markRecorded(data.responseId);
|
|
6988
|
+
const span = startResponseSpan(data, options);
|
|
6989
|
+
const failure = failureMessage(response);
|
|
6990
|
+
if (failure !== void 0) markFailed(span, failure);
|
|
6991
|
+
else span.setStatus({ code: _opentelemetry_api.SpanStatusCode.OK });
|
|
6992
|
+
span.end();
|
|
6993
|
+
};
|
|
6994
|
+
/**
|
|
6995
|
+
* Start the span of a call that failed before returning a response; the
|
|
6996
|
+
* caller records the error and ends it.
|
|
6997
|
+
*/
|
|
6998
|
+
const processRequestError = (startTime, params, options) => {
|
|
6999
|
+
const data = tracedDataFromRequest(startTime, params);
|
|
7000
|
+
pendingResponses.delete(data.responseId);
|
|
7001
|
+
return startResponseSpan(data, options);
|
|
7002
|
+
};
|
|
7003
|
+
/**
|
|
7004
|
+
* `cancel` of a background response we were tracking: emit its span.
|
|
7005
|
+
*/
|
|
7006
|
+
const processCancel = (response, options) => {
|
|
7007
|
+
const responseId = response?.id;
|
|
7008
|
+
const existing = responseId ? pendingResponses.get(responseId) : void 0;
|
|
7009
|
+
if (!existing) return;
|
|
7010
|
+
pendingResponses.delete(responseId);
|
|
7011
|
+
markRecorded(responseId);
|
|
7012
|
+
const data = tracedDataFromResponse(existing.startTime, response, {}) ?? existing;
|
|
7013
|
+
const span = startResponseSpan(data, options);
|
|
7014
|
+
markFailed(span, "Response cancelled");
|
|
7015
|
+
span.end();
|
|
7016
|
+
};
|
|
7017
|
+
/**
|
|
7018
|
+
* Fold one `ResponseStreamEvent` into the stream state.
|
|
7019
|
+
*/
|
|
7020
|
+
const processStreamEvent = (state, event) => {
|
|
7021
|
+
switch (event?.type) {
|
|
7022
|
+
case "response.output_text.delta":
|
|
7023
|
+
state.outputText += event.delta ?? "";
|
|
7024
|
+
break;
|
|
7025
|
+
case "response.output_item.done":
|
|
7026
|
+
if (event.item) state.outputBlocks[event.item.id ?? `output_${event.output_index}`] = event.item;
|
|
7027
|
+
break;
|
|
7028
|
+
case "error": state.errorMessage = event.message ?? "Response stream error";
|
|
7029
|
+
}
|
|
7030
|
+
if (event?.response && typeof event.response === "object") state.snapshot = event.response;
|
|
7031
|
+
};
|
|
7032
|
+
/**
|
|
7033
|
+
* Emit the span of a finished (or abandoned) stream. Returns the span so the
|
|
7034
|
+
* caller can record a thrown iteration error before ending it, or undefined
|
|
7035
|
+
* when no span is due: the stream re-read a response that already has one,
|
|
7036
|
+
* or it left a background response unfinished, which is stashed for the
|
|
7037
|
+
* `retrieve` / `cancel` that sees it finish.
|
|
7038
|
+
*/
|
|
7039
|
+
const finishStream = (startTime, state, params, options) => {
|
|
7040
|
+
const snapshot = state.snapshot;
|
|
7041
|
+
if (isDuplicate(snapshot?.id ?? params?.response_id ?? "", options)) return;
|
|
7042
|
+
let data = snapshot && tracedDataFromResponse(startTime, snapshot, params) || tracedDataFromRequest(startTime, params);
|
|
7043
|
+
data = {
|
|
7044
|
+
...data,
|
|
7045
|
+
outputBlocks: {
|
|
7046
|
+
...state.outputBlocks,
|
|
7047
|
+
...data.outputBlocks
|
|
7048
|
+
},
|
|
7049
|
+
outputText: data.outputText || state.outputText || void 0
|
|
7050
|
+
};
|
|
7051
|
+
if (snapshot && data.responseId) {
|
|
7052
|
+
if ((params?.background || options.isRetrieve) && !TERMINAL_STATUSES.has(snapshot.status) && state.errorMessage === void 0) {
|
|
7053
|
+
pendingResponses.set(data.responseId, data);
|
|
7054
|
+
return;
|
|
7055
|
+
}
|
|
7056
|
+
pendingResponses.delete(data.responseId);
|
|
7057
|
+
markRecorded(data.responseId);
|
|
7058
|
+
}
|
|
7059
|
+
const span = startResponseSpan(data, options);
|
|
7060
|
+
const failure = state.errorMessage ?? failureMessage(snapshot);
|
|
7061
|
+
if (failure !== void 0) markFailed(span, failure);
|
|
7062
|
+
else span.setStatus({ code: _opentelemetry_api.SpanStatusCode.OK });
|
|
7063
|
+
return span;
|
|
7064
|
+
};
|
|
7065
|
+
//#endregion
|
|
7066
|
+
//#region src/opentelemetry-lib/instrumentation/openai/index.ts
|
|
7067
|
+
const logger$16 = require_utils.initializeLogger();
|
|
7068
|
+
const PATCH_DATA_SYMBOL = Symbol("lmnr.openai.patchData");
|
|
7069
|
+
const hasOwnPatchData = (proto) => Object.getOwnPropertyDescriptor(proto, PATCH_DATA_SYMBOL) !== void 0;
|
|
7070
|
+
const markSpanError = (span, error) => {
|
|
7071
|
+
span.recordException(error);
|
|
7072
|
+
span.setAttribute("error.type", error?.constructor?.name ?? "Error");
|
|
7073
|
+
span.setStatus({ code: _opentelemetry_api.SpanStatusCode.ERROR });
|
|
7074
|
+
};
|
|
7075
|
+
/**
|
|
7076
|
+
* Attribute extraction must never break the user's call.
|
|
7077
|
+
*/
|
|
7078
|
+
const safely = (fn) => {
|
|
7079
|
+
try {
|
|
7080
|
+
fn();
|
|
7081
|
+
} catch (error) {
|
|
7082
|
+
logger$16.debug(`Failed to set openai span attributes: ${String(error)}`);
|
|
7083
|
+
}
|
|
7084
|
+
};
|
|
7085
|
+
const isAsyncIterable$1 = (value) => value !== null && typeof value === "object" && typeof value[Symbol.asyncIterator] === "function";
|
|
7086
|
+
/**
|
|
7087
|
+
* Calls made by the OpenAI Agents SDK are already traced by its trace
|
|
7088
|
+
* processor, which sets this key around the model call.
|
|
7089
|
+
*/
|
|
7090
|
+
const isDisabledByAgents = () => Boolean(LaminarContextManager.getContext().getValue(DISABLE_OPENAI_RESPONSES_INSTRUMENTATION_CONTEXT_KEY) ?? _opentelemetry_api.context.active().getValue(DISABLE_OPENAI_RESPONSES_INSTRUMENTATION_CONTEXT_KEY));
|
|
7091
|
+
const stripUndefined = (value) => {
|
|
7092
|
+
if (!value || typeof value !== "object") return value;
|
|
7093
|
+
return Object.fromEntries(Object.entries(value).filter(([, v]) => v !== void 0));
|
|
7094
|
+
};
|
|
7095
|
+
/**
|
|
7096
|
+
* Wrap the events of an SDK `Stream` as the caller iterates them. The stream
|
|
7097
|
+
* object itself is kept, so `tee()`, `toReadableStream()`, `controller` and
|
|
7098
|
+
* the SDK's stream helpers (`ChatCompletionStream`, `ResponseStream`) keep
|
|
7099
|
+
* working. `onDone` runs exactly once: on completion, on an iteration error,
|
|
7100
|
+
* or when the caller stops early.
|
|
7101
|
+
*/
|
|
7102
|
+
const instrumentStream = (stream, onEvent, onDone) => {
|
|
7103
|
+
let done = false;
|
|
7104
|
+
const finish = (error) => {
|
|
7105
|
+
if (done) return;
|
|
7106
|
+
done = true;
|
|
7107
|
+
onDone(error);
|
|
7108
|
+
};
|
|
7109
|
+
const wrap = (makeIterator) => async function* () {
|
|
7110
|
+
const iterable = { [Symbol.asyncIterator]: makeIterator };
|
|
7111
|
+
try {
|
|
7112
|
+
for await (const event of iterable) {
|
|
7113
|
+
safely(() => onEvent(event));
|
|
7114
|
+
yield event;
|
|
7115
|
+
}
|
|
7116
|
+
} catch (error) {
|
|
7117
|
+
finish(error);
|
|
7118
|
+
throw error;
|
|
7119
|
+
} finally {
|
|
7120
|
+
finish();
|
|
7121
|
+
}
|
|
7122
|
+
};
|
|
7123
|
+
if (typeof stream.iterator === "function") {
|
|
7124
|
+
stream.iterator = wrap(stream.iterator);
|
|
7125
|
+
return;
|
|
7126
|
+
}
|
|
7127
|
+
stream[Symbol.asyncIterator] = wrap(stream[Symbol.asyncIterator].bind(stream));
|
|
7128
|
+
};
|
|
7129
|
+
/**
|
|
7130
|
+
* Hand the result of the original call to `succeed` / `fail`. An
|
|
7131
|
+
* `APIPromise` stays one, so the caller keeps `withResponse()` /
|
|
7132
|
+
* `asResponse()`.
|
|
7133
|
+
*/
|
|
7134
|
+
const settle = (result, succeed, fail) => {
|
|
7135
|
+
if (typeof result?._thenUnwrap === "function") {
|
|
7136
|
+
const responsePromise = typeof result._responseProps === "function" ? result._responseProps() : result.responsePromise;
|
|
7137
|
+
if (typeof responsePromise?.then === "function") responsePromise.then(void 0, fail);
|
|
7138
|
+
return result._thenUnwrap(succeed);
|
|
7139
|
+
}
|
|
7140
|
+
if (typeof result?.then === "function") return result.then(succeed, (error) => {
|
|
7141
|
+
fail(error);
|
|
7142
|
+
throw error;
|
|
7143
|
+
});
|
|
7144
|
+
return succeed(result);
|
|
7145
|
+
};
|
|
7146
|
+
const setChatRequest = (span, params, instance, traceContent) => {
|
|
7147
|
+
setRequestAttributes$2(span, params, instance);
|
|
7148
|
+
if (traceContent) setChatPromptAttributes(span, params);
|
|
7149
|
+
};
|
|
7150
|
+
const setCompletionRequest = (span, params, instance, traceContent) => {
|
|
7151
|
+
setRequestAttributes$2(span, params, instance);
|
|
7152
|
+
if (traceContent) setCompletionPromptAttributes(span, params);
|
|
7153
|
+
};
|
|
7154
|
+
const setChoicesResponse = (span, response, traceContent) => {
|
|
7155
|
+
setResponseAttributes$2(span, response);
|
|
7156
|
+
if (traceContent && !response?.error) setChoicesAttributes(span, response);
|
|
7157
|
+
};
|
|
7158
|
+
const CHAT = {
|
|
7159
|
+
spanName: "openai.chat",
|
|
7160
|
+
skipForAgents: true,
|
|
7161
|
+
setRequest: setChatRequest,
|
|
7162
|
+
setResponse: setChoicesResponse,
|
|
7163
|
+
accumulate: accumulateChatChunk
|
|
7164
|
+
};
|
|
7165
|
+
const COMPLETION = {
|
|
7166
|
+
spanName: "openai.completion",
|
|
7167
|
+
setRequest: setCompletionRequest,
|
|
7168
|
+
setResponse: setChoicesResponse,
|
|
7169
|
+
accumulate: accumulateCompletionChunk
|
|
7170
|
+
};
|
|
7171
|
+
const EMBEDDINGS = {
|
|
7172
|
+
spanName: "openai.embeddings",
|
|
7173
|
+
setRequest: (span, params, instance, traceContent) => {
|
|
7174
|
+
setRequestAttributes$2(span, params, instance);
|
|
7175
|
+
if (traceContent) setEmbeddingsPromptAttributes(span, params);
|
|
7176
|
+
},
|
|
7177
|
+
setResponse: (span, response) => setResponseAttributes$2(span, response)
|
|
7178
|
+
};
|
|
7179
|
+
const DECISION = {
|
|
7180
|
+
spanName: "openai.decision",
|
|
7181
|
+
setRequest: setDecisionRequestAttributes,
|
|
7182
|
+
setResponse: setDecisionResponseAttributes
|
|
7183
|
+
};
|
|
7184
|
+
const unwrapAxiosBody = (result) => result?.data;
|
|
7185
|
+
const V3_CHAT = {
|
|
7186
|
+
...CHAT,
|
|
7187
|
+
accumulate: void 0,
|
|
7188
|
+
unwrapBody: unwrapAxiosBody
|
|
7189
|
+
};
|
|
7190
|
+
const V3_COMPLETION = {
|
|
7191
|
+
...COMPLETION,
|
|
7192
|
+
accumulate: void 0,
|
|
7193
|
+
unwrapBody: unwrapAxiosBody
|
|
7194
|
+
};
|
|
7195
|
+
const wrapMethod = (patchData, handler) => function(params, ...rest) {
|
|
7196
|
+
const { original } = patchData;
|
|
7197
|
+
if ((0, _opentelemetry_core.isTracingSuppressed)(_opentelemetry_api.context.active()) || handler.skipForAgents && isDisabledByAgents()) return original.call(this, params, ...rest);
|
|
7198
|
+
const traceContent = patchData.traceContent;
|
|
7199
|
+
const span = Laminar.startSpan({
|
|
7200
|
+
name: handler.spanName,
|
|
7201
|
+
spanType: "LLM"
|
|
7202
|
+
});
|
|
7203
|
+
safely(() => handler.setRequest(span, params, this, traceContent));
|
|
7204
|
+
let result;
|
|
7205
|
+
try {
|
|
7206
|
+
result = Laminar.withSpan(span, () => ({ value: original.call(this, params, ...rest) })).value;
|
|
7207
|
+
} catch (error) {
|
|
7208
|
+
markSpanError(span, error);
|
|
7209
|
+
span.end();
|
|
7210
|
+
throw error;
|
|
7211
|
+
}
|
|
7212
|
+
let ended = false;
|
|
7213
|
+
const fail = (error) => {
|
|
7214
|
+
if (ended) return;
|
|
7215
|
+
ended = true;
|
|
7216
|
+
markSpanError(span, error);
|
|
7217
|
+
span.end();
|
|
7218
|
+
};
|
|
7219
|
+
const succeed = (data) => {
|
|
7220
|
+
if (ended) return data;
|
|
7221
|
+
ended = true;
|
|
7222
|
+
const body = handler.unwrapBody ? handler.unwrapBody(data) : data;
|
|
7223
|
+
if (params?.stream) {
|
|
7224
|
+
const { accumulate } = handler;
|
|
7225
|
+
if (accumulate && isAsyncIterable$1(body)) {
|
|
7226
|
+
const complete = { choices: [] };
|
|
7227
|
+
instrumentStream(body, (chunk) => accumulate(complete, chunk), (error) => {
|
|
7228
|
+
safely(() => handler.setResponse(span, complete, traceContent));
|
|
7229
|
+
if (error !== void 0) markSpanError(span, error);
|
|
7230
|
+
else span.setStatus({ code: _opentelemetry_api.SpanStatusCode.OK });
|
|
7231
|
+
span.end();
|
|
7232
|
+
});
|
|
7233
|
+
return data;
|
|
7234
|
+
}
|
|
7235
|
+
} else safely(() => handler.setResponse(span, body, traceContent));
|
|
7236
|
+
span.setStatus({ code: _opentelemetry_api.SpanStatusCode.OK });
|
|
7237
|
+
span.end();
|
|
7238
|
+
return data;
|
|
7239
|
+
};
|
|
7240
|
+
return settle(result, succeed, fail);
|
|
7241
|
+
};
|
|
7242
|
+
const responsesParams = (kind, args) => {
|
|
7243
|
+
switch (kind) {
|
|
7244
|
+
case "create": return stripUndefined(args[0]) ?? {};
|
|
7245
|
+
case "retrieve": return {
|
|
7246
|
+
response_id: args[0],
|
|
7247
|
+
...stripUndefined(args[1] ?? {})
|
|
7248
|
+
};
|
|
7249
|
+
case "cancel": return { response_id: args[0] };
|
|
5105
7250
|
}
|
|
5106
7251
|
};
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
7252
|
+
/**
|
|
7253
|
+
* `responses.create` / `retrieve` / `cancel`. Unlike the other APIs, the
|
|
7254
|
+
* span is built after the fact from the final response (see
|
|
7255
|
+
* `responses.ts`), so a background response spans from its `create` to the
|
|
7256
|
+
* `retrieve` / `cancel` that sees it finish.
|
|
7257
|
+
*/
|
|
7258
|
+
const wrapResponsesMethod = (patchData, kind) => function(...args) {
|
|
7259
|
+
const { original } = patchData;
|
|
7260
|
+
if ((0, _opentelemetry_core.isTracingSuppressed)(_opentelemetry_api.context.active()) || isDisabledByAgents()) return original.apply(this, args);
|
|
7261
|
+
const startTime = (0, _opentelemetry_core.hrTime)();
|
|
7262
|
+
const params = responsesParams(kind, args);
|
|
7263
|
+
const options = {
|
|
7264
|
+
instance: this,
|
|
7265
|
+
traceContent: patchData.traceContent,
|
|
7266
|
+
parentContext: LaminarContextManager.getContext(),
|
|
7267
|
+
isRetrieve: kind === "retrieve"
|
|
5116
7268
|
};
|
|
5117
|
-
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
}
|
|
5126
|
-
throw: (err) => {
|
|
5127
|
-
const it = getIterator();
|
|
5128
|
-
return it.throw ? systemInstructionsStorage.run(value, () => it.throw(err)) : Promise.reject(err);
|
|
5129
|
-
}
|
|
7269
|
+
let ended = false;
|
|
7270
|
+
const fail = (error) => {
|
|
7271
|
+
if (ended || kind === "cancel") return;
|
|
7272
|
+
ended = true;
|
|
7273
|
+
safely(() => {
|
|
7274
|
+
const span = processRequestError(startTime, params, options);
|
|
7275
|
+
markSpanError(span, error);
|
|
7276
|
+
span.end();
|
|
7277
|
+
});
|
|
5130
7278
|
};
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
const
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
7279
|
+
const succeed = (data) => {
|
|
7280
|
+
if (ended) return data;
|
|
7281
|
+
ended = true;
|
|
7282
|
+
if (kind === "cancel") safely(() => processCancel(data, options));
|
|
7283
|
+
else if (isAsyncIterable$1(data)) {
|
|
7284
|
+
const state = createStreamState();
|
|
7285
|
+
instrumentStream(data, (event) => processStreamEvent(state, event), (error) => safely(() => {
|
|
7286
|
+
const span = finishStream(startTime, state, params, options);
|
|
7287
|
+
if (!span) return;
|
|
7288
|
+
if (error !== void 0) markSpanError(span, error);
|
|
7289
|
+
span.end();
|
|
7290
|
+
}));
|
|
7291
|
+
} else safely(() => processResponse(startTime, data, params, options));
|
|
7292
|
+
return data;
|
|
7293
|
+
};
|
|
7294
|
+
let result;
|
|
7295
|
+
try {
|
|
7296
|
+
result = original.apply(this, args);
|
|
7297
|
+
} catch (error) {
|
|
7298
|
+
fail(error);
|
|
7299
|
+
throw error;
|
|
5143
7300
|
}
|
|
5144
|
-
return
|
|
7301
|
+
return settle(result, succeed, fail);
|
|
5145
7302
|
};
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
7303
|
+
/**
|
|
7304
|
+
* Resolve the `OpenAI` client class from the module namespace, its default
|
|
7305
|
+
* export, or the class itself.
|
|
7306
|
+
*/
|
|
7307
|
+
const resolveClientClass = (mod) => {
|
|
7308
|
+
for (const candidate of [
|
|
7309
|
+
mod?.OpenAI,
|
|
7310
|
+
mod?.default,
|
|
7311
|
+
mod
|
|
7312
|
+
]) if (typeof candidate?.Chat?.Completions === "function") return candidate;
|
|
5151
7313
|
};
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
|
|
5157
|
-
|
|
5158
|
-
|
|
7314
|
+
/**
|
|
7315
|
+
* Resolve the openai v3 `OpenAIApi` class from the module namespace, its
|
|
7316
|
+
* default export, or the class itself.
|
|
7317
|
+
*/
|
|
7318
|
+
const resolveV3ApiClass = (mod) => {
|
|
7319
|
+
for (const candidate of [
|
|
7320
|
+
mod?.OpenAIApi,
|
|
7321
|
+
mod?.default?.OpenAIApi,
|
|
7322
|
+
mod
|
|
7323
|
+
]) if (typeof candidate?.prototype?.createChatCompletion === "function") return candidate;
|
|
5159
7324
|
};
|
|
5160
|
-
const
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
7325
|
+
const handlerTarget = (proto, method, handler) => ({
|
|
7326
|
+
proto,
|
|
7327
|
+
method,
|
|
7328
|
+
wrap: (patchData) => wrapMethod(patchData, handler)
|
|
7329
|
+
});
|
|
7330
|
+
const responsesTarget = (proto, kind) => ({
|
|
7331
|
+
proto,
|
|
7332
|
+
method: kind,
|
|
7333
|
+
wrap: (patchData) => wrapResponsesMethod(patchData, kind)
|
|
7334
|
+
});
|
|
7335
|
+
/**
|
|
7336
|
+
* Everything to patch in the given module; prototypes are undefined where
|
|
7337
|
+
* the installed SDK version lacks the resource.
|
|
7338
|
+
*/
|
|
7339
|
+
const patchTargets = (moduleExports) => {
|
|
7340
|
+
const client = resolveClientClass(moduleExports);
|
|
7341
|
+
if (client) {
|
|
7342
|
+
const responses = client.Responses?.prototype;
|
|
7343
|
+
return [
|
|
7344
|
+
handlerTarget(client.Chat?.Completions?.prototype, "create", CHAT),
|
|
7345
|
+
handlerTarget(client.Completions?.prototype, "create", COMPLETION),
|
|
7346
|
+
handlerTarget(client.Embeddings?.prototype, "create", EMBEDDINGS),
|
|
7347
|
+
handlerTarget(client.Decisions?.prototype, "create", DECISION),
|
|
7348
|
+
responsesTarget(responses, "create"),
|
|
7349
|
+
responsesTarget(responses, "retrieve"),
|
|
7350
|
+
responsesTarget(responses, "cancel")
|
|
7351
|
+
];
|
|
5165
7352
|
}
|
|
7353
|
+
const v3Api = resolveV3ApiClass(moduleExports)?.prototype;
|
|
7354
|
+
if (v3Api) return [handlerTarget(v3Api, "createChatCompletion", V3_CHAT), handlerTarget(v3Api, "createCompletion", V3_COMPLETION)];
|
|
7355
|
+
return [];
|
|
5166
7356
|
};
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
7357
|
+
/**
|
|
7358
|
+
* Instruments the `openai` package:
|
|
7359
|
+
* - openai >= 4: `chat.completions.create`, `completions.create`,
|
|
7360
|
+
* `embeddings.create`, `decisions.create` and `responses.create` /
|
|
7361
|
+
* `retrieve` / `cancel`. `chat.completions.parse()` / `stream()` /
|
|
7362
|
+
* `runTools()` and `responses.parse()` / `stream()` delegate to `create`,
|
|
7363
|
+
* so they are traced through it without nesting a second span.
|
|
7364
|
+
* - openai v3: `OpenAIApi#createChatCompletion` / `createCompletion`.
|
|
7365
|
+
*/
|
|
7366
|
+
var OpenAIInstrumentation = class extends _opentelemetry_instrumentation.InstrumentationBase {
|
|
7367
|
+
constructor(config) {
|
|
7368
|
+
super("@lmnr/openai-instrumentation", require_utils.version, { enabled: true });
|
|
7369
|
+
this.traceContent = config?.traceContent ?? true;
|
|
7370
|
+
}
|
|
7371
|
+
init() {
|
|
7372
|
+
return new _opentelemetry_instrumentation.InstrumentationNodeModuleDefinition("openai", [">=3.1.0"], this.patch.bind(this), this.unpatch.bind(this));
|
|
7373
|
+
}
|
|
7374
|
+
/**
|
|
7375
|
+
* Accepts the `openai` module namespace or the `OpenAI` class (the
|
|
7376
|
+
* module's default export). For openai v3, the module or its `OpenAIApi`
|
|
7377
|
+
* class.
|
|
7378
|
+
*/
|
|
7379
|
+
manuallyInstrument(module) {
|
|
7380
|
+
if (!patchTargets(module).length) {
|
|
7381
|
+
_opentelemetry_api.diag.warn("Could not find the OpenAI client in openai manual instrumentation input. Pass the openai module.");
|
|
7382
|
+
return;
|
|
7383
|
+
}
|
|
7384
|
+
_opentelemetry_api.diag.debug("Manually instrumenting openai");
|
|
7385
|
+
this.patch(module);
|
|
7386
|
+
}
|
|
7387
|
+
patch(moduleExports) {
|
|
7388
|
+
_opentelemetry_api.diag.debug("Patching openai");
|
|
7389
|
+
for (const target of patchTargets(moduleExports)) this.patchMethod(target);
|
|
7390
|
+
return moduleExports;
|
|
7391
|
+
}
|
|
7392
|
+
unpatch(moduleExports) {
|
|
7393
|
+
_opentelemetry_api.diag.debug("Unpatching openai");
|
|
7394
|
+
for (const { proto, method } of patchTargets(moduleExports)) {
|
|
7395
|
+
if (!proto || !hasOwnPatchData(proto)) continue;
|
|
7396
|
+
const patchData = proto[PATCH_DATA_SYMBOL];
|
|
7397
|
+
if (!patchData[method]) continue;
|
|
7398
|
+
proto[method] = patchData[method].original;
|
|
7399
|
+
delete patchData[method];
|
|
7400
|
+
if (!Object.keys(patchData).length) delete proto[PATCH_DATA_SYMBOL];
|
|
7401
|
+
}
|
|
7402
|
+
}
|
|
7403
|
+
/**
|
|
7404
|
+
* Replace `proto[method]` with a traced wrapper. Re-patching an already
|
|
7405
|
+
* patched method only refreshes `traceContent`, so repeated
|
|
7406
|
+
* initialization never double-wraps.
|
|
7407
|
+
*/
|
|
7408
|
+
patchMethod({ proto, method, wrap }) {
|
|
7409
|
+
if (!proto || typeof proto[method] !== "function") return;
|
|
7410
|
+
if (!hasOwnPatchData(proto)) Object.defineProperty(proto, PATCH_DATA_SYMBOL, {
|
|
7411
|
+
value: {},
|
|
7412
|
+
writable: true,
|
|
7413
|
+
configurable: true,
|
|
7414
|
+
enumerable: false
|
|
5185
7415
|
});
|
|
5186
|
-
|
|
7416
|
+
const existing = proto[PATCH_DATA_SYMBOL][method];
|
|
7417
|
+
if (existing) {
|
|
7418
|
+
existing.traceContent = this.traceContent;
|
|
7419
|
+
return;
|
|
7420
|
+
}
|
|
7421
|
+
const patchData = {
|
|
7422
|
+
traceContent: this.traceContent,
|
|
7423
|
+
original: proto[method]
|
|
7424
|
+
};
|
|
7425
|
+
proto[PATCH_DATA_SYMBOL][method] = patchData;
|
|
7426
|
+
proto[method] = wrap(patchData);
|
|
5187
7427
|
}
|
|
5188
|
-
if (typeof data === "object") return [data];
|
|
5189
|
-
return [{
|
|
5190
|
-
role,
|
|
5191
|
-
content: String(data)
|
|
5192
|
-
}];
|
|
5193
7428
|
};
|
|
5194
7429
|
//#endregion
|
|
5195
7430
|
//#region src/opentelemetry-lib/instrumentation/openai-agents/messages.ts
|
|
@@ -7223,14 +9458,10 @@ const getStagehandInstrumentation = (playwrightInstrumentation, client, sessionR
|
|
|
7223
9458
|
return stagehandInstrumentation;
|
|
7224
9459
|
};
|
|
7225
9460
|
const initInstrumentations = (client, suppressContentTracing, sessionRecordingOptions) => {
|
|
7226
|
-
const enrichTokens = false;
|
|
7227
9461
|
const instrumentations = [];
|
|
7228
|
-
instrumentations.push(new
|
|
7229
|
-
|
|
7230
|
-
|
|
7231
|
-
}));
|
|
7232
|
-
instrumentations.push(new _traceloop_instrumentation_anthropic.AnthropicInstrumentation({ traceContent: !suppressContentTracing }));
|
|
7233
|
-
instrumentations.push(new _traceloop_instrumentation_cohere.CohereInstrumentation({ traceContent: !suppressContentTracing }));
|
|
9462
|
+
instrumentations.push(new OpenAIInstrumentation({ traceContent: !suppressContentTracing }));
|
|
9463
|
+
instrumentations.push(new AnthropicInstrumentation({ traceContent: !suppressContentTracing }));
|
|
9464
|
+
instrumentations.push(new CohereInstrumentation({ traceContent: !suppressContentTracing }));
|
|
7234
9465
|
instrumentations.push(new _traceloop_instrumentation_bedrock.BedrockInstrumentation({ traceContent: !suppressContentTracing }));
|
|
7235
9466
|
instrumentations.push(new _traceloop_instrumentation_pinecone.PineconeInstrumentation());
|
|
7236
9467
|
instrumentations.push(new _traceloop_instrumentation_langchain.LangChainInstrumentation({ traceContent: !suppressContentTracing }));
|
|
@@ -7244,6 +9475,7 @@ const initInstrumentations = (client, suppressContentTracing, sessionRecordingOp
|
|
|
7244
9475
|
instrumentations.push(new PuppeteerInstrumentation(client, sessionRecordingOptions));
|
|
7245
9476
|
}
|
|
7246
9477
|
instrumentations.push(new GoogleGenAiInstrumentation({ traceContent: !suppressContentTracing }));
|
|
9478
|
+
instrumentations.push(new GoogleAIPlatformInstrumentation({ traceContent: !suppressContentTracing }));
|
|
7247
9479
|
instrumentations.push(new KernelInstrumentation());
|
|
7248
9480
|
instrumentations.push(new ClaudeAgentSDKInstrumentation());
|
|
7249
9481
|
instrumentations.push(new OpencodeInstrumentation());
|
|
@@ -7253,34 +9485,27 @@ const initInstrumentations = (client, suppressContentTracing, sessionRecordingOp
|
|
|
7253
9485
|
return instrumentations;
|
|
7254
9486
|
};
|
|
7255
9487
|
const manuallyInitInstrumentations = (client, instrumentModules, suppressContentTracing, sessionRecordingOptions) => {
|
|
7256
|
-
const enrichTokens = false;
|
|
7257
9488
|
const instrumentations = [];
|
|
7258
9489
|
let playwrightInstrumentation;
|
|
7259
9490
|
if (instrumentModules?.OpenAI && instrumentModules?.openAI) throw new Error("`openAI` is deprecated, but both `OpenAI` and `openAI` are provided. Please use `OpenAI` only.");
|
|
7260
9491
|
if (instrumentModules?.openAI) {
|
|
7261
|
-
const openAIInstrumentation = new
|
|
7262
|
-
enrichTokens,
|
|
7263
|
-
traceContent: !suppressContentTracing
|
|
7264
|
-
});
|
|
9492
|
+
const openAIInstrumentation = new OpenAIInstrumentation({ traceContent: !suppressContentTracing });
|
|
7265
9493
|
instrumentations.push(openAIInstrumentation);
|
|
7266
9494
|
openAIInstrumentation.manuallyInstrument(instrumentModules.openAI);
|
|
7267
9495
|
}
|
|
7268
9496
|
if (instrumentModules?.OpenAI) {
|
|
7269
|
-
const openAIInstrumentation = new
|
|
7270
|
-
enrichTokens,
|
|
7271
|
-
traceContent: !suppressContentTracing
|
|
7272
|
-
});
|
|
9497
|
+
const openAIInstrumentation = new OpenAIInstrumentation({ traceContent: !suppressContentTracing });
|
|
7273
9498
|
instrumentations.push(openAIInstrumentation);
|
|
7274
9499
|
openAIInstrumentation.manuallyInstrument(instrumentModules.OpenAI);
|
|
7275
9500
|
}
|
|
7276
9501
|
if (instrumentModules?.anthropic) {
|
|
7277
|
-
const anthropicInstrumentation = new
|
|
9502
|
+
const anthropicInstrumentation = new AnthropicInstrumentation({ traceContent: !suppressContentTracing });
|
|
7278
9503
|
instrumentations.push(anthropicInstrumentation);
|
|
7279
9504
|
anthropicInstrumentation.manuallyInstrument(instrumentModules.anthropic);
|
|
7280
9505
|
}
|
|
7281
9506
|
if (instrumentModules?.azureOpenAI) logger$7.warn("Azure OpenAI instrumentation is no longer supported. Not enabling any instrumentation.");
|
|
7282
9507
|
if (instrumentModules?.cohere) {
|
|
7283
|
-
const cohereInstrumentation = new
|
|
9508
|
+
const cohereInstrumentation = new CohereInstrumentation({ traceContent: !suppressContentTracing });
|
|
7284
9509
|
instrumentations.push(cohereInstrumentation);
|
|
7285
9510
|
cohereInstrumentation.manuallyInstrument(instrumentModules.cohere);
|
|
7286
9511
|
}
|
|
@@ -7339,6 +9564,11 @@ const manuallyInitInstrumentations = (client, instrumentModules, suppressContent
|
|
|
7339
9564
|
instrumentations.push(googleGenAiInstrumentation);
|
|
7340
9565
|
googleGenAiInstrumentation.manuallyInstrument(instrumentModules.google_genai);
|
|
7341
9566
|
}
|
|
9567
|
+
if (instrumentModules?.google_aiplatform) {
|
|
9568
|
+
const googleAIPlatformInstrumentation = new GoogleAIPlatformInstrumentation({ traceContent: !suppressContentTracing });
|
|
9569
|
+
instrumentations.push(googleAIPlatformInstrumentation);
|
|
9570
|
+
googleAIPlatformInstrumentation.manuallyInstrument(instrumentModules.google_aiplatform);
|
|
9571
|
+
}
|
|
7342
9572
|
if (instrumentModules?.openrouter) {
|
|
7343
9573
|
const openRouterInstrumentation = new OpenRouterInstrumentation({ traceContent: !suppressContentTracing });
|
|
7344
9574
|
instrumentations.push(openRouterInstrumentation);
|
|
@@ -8015,17 +10245,6 @@ const withSessionMetadata = (metadata) => {
|
|
|
8015
10245
|
[SESSION_METADATA_KEY]: sessionId
|
|
8016
10246
|
};
|
|
8017
10247
|
};
|
|
8018
|
-
const getAverageScores = (results) => {
|
|
8019
|
-
const perScoreValues = {};
|
|
8020
|
-
for (const result of results) for (const key in result.scores) {
|
|
8021
|
-
const score = result.scores[key];
|
|
8022
|
-
if (perScoreValues[key] && score !== null) perScoreValues[key].push(score);
|
|
8023
|
-
else perScoreValues[key] = score !== null ? [score] : [];
|
|
8024
|
-
}
|
|
8025
|
-
const averageScores = {};
|
|
8026
|
-
for (const key in perScoreValues) averageScores[key] = perScoreValues[key].reduce((a, b) => a + b, 0) / perScoreValues[key].length;
|
|
8027
|
-
return averageScores;
|
|
8028
|
-
};
|
|
8029
10248
|
/**
|
|
8030
10249
|
* HumanEvaluator is a class to register a human evaluator.
|
|
8031
10250
|
*/
|
|
@@ -8070,7 +10289,7 @@ var Evaluation = class {
|
|
|
8070
10289
|
this.concurrencyLimit = DEFAULT_CONCURRENCY;
|
|
8071
10290
|
this.traceDisableBatch = false;
|
|
8072
10291
|
this.traceExportBatchSize = MAX_EXPORT_BATCH_SIZE;
|
|
8073
|
-
this.uploadPromises =
|
|
10292
|
+
this.uploadPromises = /* @__PURE__ */ new Set();
|
|
8074
10293
|
if (Object.keys(evaluators).length === 0) throw new Error("No evaluators provided");
|
|
8075
10294
|
const evaluatorNameRegex = /^[\w\s-]+$/;
|
|
8076
10295
|
for (const key in evaluators) if (!evaluatorNameRegex.test(key)) throw new Error(`Invalid evaluator key: "${key}".Keys must only contain letters, digits, hyphens, underscores, or spaces.`);
|
|
@@ -8135,16 +10354,15 @@ var Evaluation = class {
|
|
|
8135
10354
|
logger$3.warn(`Error getting dataset ${source.name}: ` + require_utils.errorMessage(error));
|
|
8136
10355
|
}
|
|
8137
10356
|
}
|
|
8138
|
-
let resultDatapoints;
|
|
8139
10357
|
try {
|
|
8140
10358
|
const length = await this.getLength();
|
|
8141
10359
|
const evaluation = await this.client.evals.init(this.name, this.groupName, withSessionMetadata(this.metadata));
|
|
8142
10360
|
const url = getEvaluationUrl(evaluation.projectId, evaluation.id, this.progressReporter.baseUrl, this.frontendPort);
|
|
8143
10361
|
process.stdout.write(`\nCheck results at ${url}\n`);
|
|
8144
10362
|
this.progressReporter.start({ length });
|
|
8145
|
-
|
|
8146
|
-
|
|
8147
|
-
if (this.
|
|
10363
|
+
const averageScores = await this.evaluateInBatches(evaluation.id);
|
|
10364
|
+
if (this.uploadPromises.size > 0) await Promise.all(this.uploadPromises);
|
|
10365
|
+
if (this.uploadError) throw this.uploadError.error;
|
|
8148
10366
|
this.progressReporter.stop({
|
|
8149
10367
|
averageScores,
|
|
8150
10368
|
projectId: evaluation.projectId,
|
|
@@ -8182,24 +10400,42 @@ var Evaluation = class {
|
|
|
8182
10400
|
});
|
|
8183
10401
|
return LaminarContextManager.runWithIsolatedContext([entityContext], async () => {
|
|
8184
10402
|
const semaphore = new require_utils.Semaphore(this.concurrencyLimit);
|
|
8185
|
-
const
|
|
10403
|
+
const inFlight = /* @__PURE__ */ new Set();
|
|
10404
|
+
const scoreSums = {};
|
|
10405
|
+
const scoreCounts = {};
|
|
10406
|
+
let firstError;
|
|
8186
10407
|
const evaluateTask = async (datapoint, index) => {
|
|
8187
10408
|
try {
|
|
8188
10409
|
const result = await this.evaluateDatapoint(evalId, datapoint, index);
|
|
10410
|
+
for (const [key, value] of Object.entries(result.scores ?? {})) {
|
|
10411
|
+
if (value === null) continue;
|
|
10412
|
+
scoreSums[key] = (scoreSums[key] ?? 0) + value;
|
|
10413
|
+
scoreCounts[key] = (scoreCounts[key] ?? 0) + 1;
|
|
10414
|
+
}
|
|
8189
10415
|
this.progressReporter.update(1);
|
|
8190
|
-
|
|
10416
|
+
} catch (error) {
|
|
10417
|
+
firstError ??= { error };
|
|
8191
10418
|
} finally {
|
|
8192
10419
|
semaphore.release();
|
|
8193
10420
|
}
|
|
8194
10421
|
};
|
|
8195
|
-
|
|
10422
|
+
let index = 0;
|
|
10423
|
+
for await (const datapoint of this.data) {
|
|
8196
10424
|
await semaphore.acquire();
|
|
8197
|
-
const
|
|
8198
|
-
|
|
10425
|
+
const task = evaluateTask(datapoint, index++).finally(() => inFlight.delete(task));
|
|
10426
|
+
inFlight.add(task);
|
|
8199
10427
|
}
|
|
8200
|
-
|
|
10428
|
+
await Promise.all(inFlight);
|
|
10429
|
+
if (firstError) throw firstError.error;
|
|
10430
|
+
return Object.fromEntries(Object.keys(scoreSums).map((key) => [key, scoreSums[key] / scoreCounts[key]]));
|
|
8201
10431
|
});
|
|
8202
10432
|
}
|
|
10433
|
+
trackUpload(upload) {
|
|
10434
|
+
const tracked = upload.then(() => void 0, (error) => {
|
|
10435
|
+
this.uploadError ??= { error };
|
|
10436
|
+
}).finally(() => this.uploadPromises.delete(tracked));
|
|
10437
|
+
this.uploadPromises.add(tracked);
|
|
10438
|
+
}
|
|
8203
10439
|
async evaluateDatapoint(evalId, datapoint, index) {
|
|
8204
10440
|
return observe({
|
|
8205
10441
|
name: "evaluation",
|
|
@@ -8282,12 +10518,11 @@ var Evaluation = class {
|
|
|
8282
10518
|
datapointId: datapoint.id,
|
|
8283
10519
|
createdAt: datapoint.createdAt
|
|
8284
10520
|
};
|
|
8285
|
-
|
|
10521
|
+
this.trackUpload(this.client.evals.saveDatapoints({
|
|
8286
10522
|
evalId,
|
|
8287
10523
|
datapoints: [resultDatapoint],
|
|
8288
10524
|
groupName: this.groupName
|
|
8289
|
-
});
|
|
8290
|
-
this.uploadPromises.push(uploadPromise);
|
|
10525
|
+
}));
|
|
8291
10526
|
return resultDatapoint;
|
|
8292
10527
|
});
|
|
8293
10528
|
}
|
|
@@ -8334,165 +10569,10 @@ async function evaluate({ data, executor, evaluators, groupName, name, metadata,
|
|
|
8334
10569
|
//#region src/integrations/eve.ts
|
|
8335
10570
|
const logger$2 = require_utils.initializeLogger();
|
|
8336
10571
|
const EVE_REPORTER_TRACER_NAME = "@lmnr-ai/lmnr/eve-reporter";
|
|
8337
|
-
const
|
|
8338
|
-
const
|
|
8339
|
-
|
|
8340
|
-
const
|
|
8341
|
-
/**
|
|
8342
|
-
* Root span name for every propagated eval, and it never changes. eve calls
|
|
8343
|
-
* `ClientSession.send` before any hook that knows which eval is in flight, and
|
|
8344
|
-
* eve exposes no `onEvalStart`, so the id simply is not knowable here. Renaming
|
|
8345
|
-
* the span later would invalidate the `lmnr.span.path` the processor already
|
|
8346
|
-
* stamped on it and on every descendant, and would make the path we propagate
|
|
8347
|
-
* to the agent stale. The eval id rides on `lmnr.eve.eval.id`, on the trace
|
|
8348
|
-
* metadata, and on the datapoint instead.
|
|
8349
|
-
*/
|
|
8350
|
-
const ROOT_SPAN_NAME = "eve eval";
|
|
8351
|
-
const EXECUTOR_SPAN_NAME = "executor";
|
|
8352
|
-
/** Prototypes already patched — `send` must never be wrapped twice. */
|
|
8353
|
-
const patchedSendPrototypes = /* @__PURE__ */ new WeakSet();
|
|
8354
|
-
/**
|
|
8355
|
-
* Per-eve-client-session state. Keyed off the `ClientSession` instance rather
|
|
8356
|
-
* than an AsyncLocalStorage frame or a "current eval" global: eve runs up to
|
|
8357
|
-
* `maxConcurrency` evals at once, and each eval builds its own `ClientSession`
|
|
8358
|
-
* (`EvalSessionManager` calls `client.session()` per `EvalSessionDriver`), so
|
|
8359
|
-
* the instance is the only correct correlation key available inside `send`.
|
|
8360
|
-
*/
|
|
8361
|
-
const traceByClientSession = /* @__PURE__ */ new WeakMap();
|
|
8362
|
-
/** eve session id -> trace, read by `onEvalComplete` via `result.sessionId`. */
|
|
8363
|
-
const traceByEveSessionId = /* @__PURE__ */ new Map();
|
|
8364
|
-
/** Set by the running reporter's `onRunStart`; cleared by `onRunComplete`. */
|
|
8365
|
-
let activeSessionTraceFactory = null;
|
|
8366
|
-
/** Mirrors eve's own `normalizeSendTurnInput`, then merges the trace headers. */
|
|
8367
|
-
const withTraceHeaders = (input, headers) => {
|
|
8368
|
-
const payload = typeof input === "string" ? { message: input } : { ...input };
|
|
8369
|
-
payload.headers = {
|
|
8370
|
-
...payload.headers ?? {},
|
|
8371
|
-
...headers
|
|
8372
|
-
};
|
|
8373
|
-
return payload;
|
|
8374
|
-
};
|
|
8375
|
-
const openSessionTrace = (clientSession) => {
|
|
8376
|
-
try {
|
|
8377
|
-
const existing = traceByClientSession.get(clientSession);
|
|
8378
|
-
if (existing) return existing;
|
|
8379
|
-
const minted = activeSessionTraceFactory?.();
|
|
8380
|
-
if (minted) traceByClientSession.set(clientSession, minted);
|
|
8381
|
-
return minted;
|
|
8382
|
-
} catch (error) {
|
|
8383
|
-
logger$2.warn(`Laminar eve reporter: failed to open a session trace: ` + require_utils.errorMessage(error));
|
|
8384
|
-
return;
|
|
8385
|
-
}
|
|
8386
|
-
};
|
|
8387
|
-
/**
|
|
8388
|
-
* eve assigns the session id on the POST that `send` awaits, so the returned
|
|
8389
|
-
* `MessageResponse` is the earliest place the runner can bind the eve session
|
|
8390
|
-
* id to the trace it minted.
|
|
8391
|
-
*/
|
|
8392
|
-
const recordEveSessionId = (response, sessionTrace) => {
|
|
8393
|
-
const sessionId = response?.sessionId;
|
|
8394
|
-
if (typeof sessionId !== "string" || sessionId.length === 0) return;
|
|
8395
|
-
sessionTrace.sessionIds.add(sessionId);
|
|
8396
|
-
traceByEveSessionId.set(sessionId, sessionTrace);
|
|
8397
|
-
};
|
|
8398
|
-
/**
|
|
8399
|
-
* Build the propagation headers for the EXECUTOR span. Plain HTTP strings — eve
|
|
8400
|
-
* forwards `SendTurnPayload.headers` verbatim to `fetch`.
|
|
8401
|
-
*
|
|
8402
|
-
* The Laminar context carries the full path. Laminar nests spans by
|
|
8403
|
-
* `lmnr.span.ids_path`, not by OTel `parentSpanId`, so a receiver that adopts
|
|
8404
|
-
* only the ids lands its spans as a second root on our trace. The path is what
|
|
8405
|
-
* makes the agent's subtree render UNDER the executor. Both paths are constants
|
|
8406
|
-
* here — neither the root nor the executor span is ever renamed.
|
|
8407
|
-
*/
|
|
8408
|
-
const buildTraceHeaders = (rootSpan, executorSpan) => {
|
|
8409
|
-
const spanContext = executorSpan.spanContext();
|
|
8410
|
-
const laminarContext = {
|
|
8411
|
-
traceId: require_utils.otelTraceIdToUUID(spanContext.traceId),
|
|
8412
|
-
spanId: require_utils.otelSpanIdToUUID(spanContext.spanId),
|
|
8413
|
-
isRemote: true,
|
|
8414
|
-
spanPath: [ROOT_SPAN_NAME, EXECUTOR_SPAN_NAME],
|
|
8415
|
-
spanIdsPath: [require_utils.otelSpanIdToUUID(rootSpan.spanContext().spanId), require_utils.otelSpanIdToUUID(spanContext.spanId)]
|
|
8416
|
-
};
|
|
8417
|
-
return {
|
|
8418
|
-
[TRACEPARENT_HEADER$1]: `00-${spanContext.traceId}-${spanContext.spanId}-01`,
|
|
8419
|
-
[LAMINAR_SPAN_CONTEXT_HEADER$1]: JSON.stringify(laminarContext)
|
|
8420
|
-
};
|
|
8421
|
-
};
|
|
8422
|
-
/**
|
|
8423
|
-
* Bind this session's ROOT span into the calling eval's async context, so work
|
|
8424
|
-
* the eval does in the RUNNER process after `send` resolves lands on this trace
|
|
8425
|
-
* instead of opening its own. The concrete case is `t.judge.autoevals.*`: eve
|
|
8426
|
-
* runs the judge's model call in the runner, and Laminar's AI SDK integration
|
|
8427
|
-
* parents it to `LaminarContextManager.getContext()`.
|
|
8428
|
-
*
|
|
8429
|
-
* The root — not a pre-opened judge span — is the binding target on purpose.
|
|
8430
|
-
* An eval may record any number of judge assertions, and each one runs its own
|
|
8431
|
-
* model call. Binding the root gives every judge its own child of the root; a
|
|
8432
|
-
* single pre-opened parent would collect all of them under whichever judge
|
|
8433
|
-
* happened to run first.
|
|
8434
|
-
*
|
|
8435
|
-
* `pushContext` uses `AsyncLocalStorage.enterWith`, so this MUST run
|
|
8436
|
-
* synchronously before `send`'s first `await` — at that point we are still
|
|
8437
|
-
* executing in the caller's async context, so the store we set is the one
|
|
8438
|
-
* `test(t)` continues with. Each eval's `test(t)` runs in its own async chain,
|
|
8439
|
-
* so concurrent evals never see each other's root span; do NOT replace this
|
|
8440
|
-
* with the process-global context stack, which would cross-attribute.
|
|
8441
|
-
*
|
|
8442
|
-
* Judge spans reach Laminar only when the runner opts in — `Laminar.initialize()`
|
|
8443
|
-
* plus a globally registered AI SDK telemetry integration. Without that the
|
|
8444
|
-
* binding is inert, never wrong.
|
|
8445
|
-
*
|
|
8446
|
-
* Never popped: the store dies with the eval's async chain.
|
|
8447
|
-
*/
|
|
8448
|
-
const bindEvalContext = (sessionTrace) => {
|
|
8449
|
-
try {
|
|
8450
|
-
LaminarContextManager.pushContext(_opentelemetry_api.trace.setSpan(_opentelemetry_api.ROOT_CONTEXT, sessionTrace.rootSpan));
|
|
8451
|
-
} catch (error) {
|
|
8452
|
-
logger$2.warn(`Laminar eve reporter: failed to bind the eval context: ` + require_utils.errorMessage(error));
|
|
8453
|
-
}
|
|
8454
|
-
};
|
|
8455
|
-
/**
|
|
8456
|
-
* Wrap `ClientSession.prototype.send` so every eve turn carries the trace
|
|
8457
|
-
* context of a runner-minted Laminar span. Idempotent per prototype.
|
|
8458
|
-
*
|
|
8459
|
-
* @internal Exported for tests, which drive it with a stub class so the suite
|
|
8460
|
-
* keeps no dependency on eve.
|
|
8461
|
-
*/
|
|
8462
|
-
const patchEveClientSession = (sessionClass) => {
|
|
8463
|
-
const prototype = sessionClass?.prototype;
|
|
8464
|
-
const original = prototype?.send;
|
|
8465
|
-
if (!prototype || typeof original !== "function") return false;
|
|
8466
|
-
if (patchedSendPrototypes.has(prototype)) return true;
|
|
8467
|
-
prototype.send = async function(input) {
|
|
8468
|
-
const sessionTrace = openSessionTrace(this);
|
|
8469
|
-
if (!sessionTrace) return original.call(this, input);
|
|
8470
|
-
bindEvalContext(sessionTrace);
|
|
8471
|
-
const response = await original.call(this, withTraceHeaders(input, sessionTrace.headers));
|
|
8472
|
-
try {
|
|
8473
|
-
recordEveSessionId(response, sessionTrace);
|
|
8474
|
-
} catch (error) {
|
|
8475
|
-
logger$2.warn(`Laminar eve reporter: failed to record the eve session id: ` + require_utils.errorMessage(error));
|
|
8476
|
-
}
|
|
8477
|
-
return response;
|
|
8478
|
-
};
|
|
8479
|
-
patchedSendPrototypes.add(prototype);
|
|
8480
|
-
return true;
|
|
8481
|
-
};
|
|
8482
|
-
/**
|
|
8483
|
-
* Load eve's `ClientSession` without depending on eve. Mirrors the guarded
|
|
8484
|
-
* dynamic import in eve's own Braintrust reporter (`loadBraintrustSdk`), except
|
|
8485
|
-
* a missing eve degrades instead of throwing — the reporter must keep working
|
|
8486
|
-
* when it is registered outside an eve run.
|
|
8487
|
-
*/
|
|
8488
|
-
const loadEveClientSession = async () => {
|
|
8489
|
-
try {
|
|
8490
|
-
const sessionClass = (await import(EVE_CLIENT_MODULE)).ClientSession;
|
|
8491
|
-
return typeof sessionClass === "function" ? sessionClass : void 0;
|
|
8492
|
-
} catch {
|
|
8493
|
-
return;
|
|
8494
|
-
}
|
|
8495
|
-
};
|
|
10572
|
+
const OTEL_TRACE_ID_RE = /^[0-9a-f]{32}$/iu;
|
|
10573
|
+
const OTEL_SPAN_ID_RE = /^[0-9a-f]{16}$/iu;
|
|
10574
|
+
const ZERO_TRACE_ID = "0".repeat(32);
|
|
10575
|
+
const ZERO_SPAN_ID = "0".repeat(16);
|
|
8496
10576
|
const didAssertionPass = (assertion) => {
|
|
8497
10577
|
if (typeof assertion.passed === "boolean") return assertion.passed;
|
|
8498
10578
|
if (typeof assertion.score === "number") {
|
|
@@ -8501,14 +10581,8 @@ const didAssertionPass = (assertion) => {
|
|
|
8501
10581
|
}
|
|
8502
10582
|
return true;
|
|
8503
10583
|
};
|
|
8504
|
-
/**
|
|
8505
|
-
|
|
8506
|
-
* Detailed assertion outcomes live in metadata; sparse per-assertion score keys
|
|
8507
|
-
* make Laminar's eval UI look incomplete when different eval files assert
|
|
8508
|
-
* different things.
|
|
8509
|
-
*/
|
|
8510
|
-
const resultToScores = (result) => {
|
|
8511
|
-
const assertions = result.assertions ?? [];
|
|
10584
|
+
/** Keep Laminar's score columns stable across heterogeneous eve evals. */
|
|
10585
|
+
const resultToScores = (result, assertions) => {
|
|
8512
10586
|
const gateAssertions = assertions.filter((assertion) => assertion.severity === "gate");
|
|
8513
10587
|
const thresholdedSoftAssertions = assertions.filter((assertion) => assertion.severity === "soft" && typeof assertion.threshold === "number");
|
|
8514
10588
|
return {
|
|
@@ -8526,7 +10600,6 @@ const assertionsMetadata = (assertions) => assertions.map((assertion) => ({
|
|
|
8526
10600
|
message: assertion.message,
|
|
8527
10601
|
metadata: assertion.metadata
|
|
8528
10602
|
}));
|
|
8529
|
-
/** Surface failed assertions in metadata so a failing run is debuggable. */
|
|
8530
10603
|
const failedAssertionsMetadata = (assertions) => {
|
|
8531
10604
|
const failed = assertions.filter((assertion) => !didAssertionPass(assertion)).map((assertion) => ({
|
|
8532
10605
|
name: assertion.name,
|
|
@@ -8534,79 +10607,62 @@ const failedAssertionsMetadata = (assertions) => {
|
|
|
8534
10607
|
}));
|
|
8535
10608
|
return failed.length > 0 ? { failedAssertions: failed } : {};
|
|
8536
10609
|
};
|
|
8537
|
-
const
|
|
10610
|
+
const validTraceContext = (context) => context != null && typeof context.traceId === "string" && OTEL_TRACE_ID_RE.test(context.traceId) && context.traceId !== ZERO_TRACE_ID && typeof context.spanId === "string" && OTEL_SPAN_ID_RE.test(context.spanId) && context.spanId !== ZERO_SPAN_ID && typeof context.traceFlags === "number" && Number.isInteger(context.traceFlags) && context.traceFlags >= 0 && context.traceFlags <= 255;
|
|
10611
|
+
const normalizeTraceContexts = (contexts) => Array.isArray(contexts) ? contexts.filter(validTraceContext) : [];
|
|
10612
|
+
const isSampledTraceContext = (context) => (context.traceFlags & 1) === 1;
|
|
10613
|
+
const selectTraceContext = (contexts) => contexts.find((context) => context.primary === true && isSampledTraceContext(context)) ?? contexts.find(isSampledTraceContext);
|
|
10614
|
+
const traceContextMetadata = (contexts) => contexts.map((context) => ({
|
|
10615
|
+
traceId: context.traceId,
|
|
10616
|
+
spanId: context.spanId,
|
|
10617
|
+
traceFlags: context.traceFlags,
|
|
10618
|
+
primary: context.primary,
|
|
10619
|
+
sessionId: context.sessionId
|
|
10620
|
+
}));
|
|
8538
10621
|
/**
|
|
8539
|
-
*
|
|
8540
|
-
* or per-eval via the `reporters` field — eve runs and grades each eval, then
|
|
8541
|
-
* hands the graded result here, and this reporter ships it to Laminar as an
|
|
8542
|
-
* evaluation run with one datapoint per eval.
|
|
10622
|
+
* Reports eve eval results to Laminar.
|
|
8543
10623
|
*
|
|
8544
|
-
*
|
|
8545
|
-
*
|
|
8546
|
-
*
|
|
8547
|
-
*
|
|
8548
|
-
*
|
|
8549
|
-
*
|
|
10624
|
+
* Eve 0.35+ gives reporters every native trace context observed during an eval.
|
|
10625
|
+
* The reporter links the datapoint to the first sampled primary trace (or the
|
|
10626
|
+
* first sampled trace when there is no sampled primary), records every valid
|
|
10627
|
+
* context in metadata, and adds
|
|
10628
|
+
* a small EVALUATION span to the selected native trace for the grade. When eve
|
|
10629
|
+
* reports no usable context, a reporter-owned fallback trace preserves the
|
|
10630
|
+
* result without pretending it contains agent work.
|
|
8550
10631
|
*
|
|
8551
|
-
*
|
|
10632
|
+
* The agent must export its native OpenTelemetry spans to the same Laminar
|
|
10633
|
+
* project. In the eve agent, declare a destination like:
|
|
8552
10634
|
*
|
|
8553
|
-
* 1. `"propagated"` — the normal path, above. The agent joins the trace only
|
|
8554
|
-
* when `agent/instrumentation.ts` sets `traceChannelRequests: true` and the
|
|
8555
|
-
* agent process runs with `WORKFLOW_TRACE_MODE=continuous`.
|
|
8556
|
-
* 2. `"reporter-fallback"` — no session trace was minted (propagation disabled,
|
|
8557
|
-
* or eve produced no session id). The reporter opens its own EVALUATION span
|
|
8558
|
-
* so the grade still lands somewhere debuggable; it holds no agent work.
|
|
8559
|
-
*
|
|
8560
|
-
* `t.judge.autoevals.*` model calls run in the RUNNER process, and they reach
|
|
8561
|
-
* Laminar only when the runner registers an AI SDK telemetry integration. eve
|
|
8562
|
-
* imports `generateText` inside its own bundle, so `wrapAISDK` cannot reach it —
|
|
8563
|
-
* but AI SDK v7 reads registered integrations off `globalThis`, which does. One
|
|
8564
|
-
* line in `evals.config.ts`:
|
|
8565
|
-
*
|
|
8566
|
-
* ```ts
|
|
8567
|
-
* registerTelemetry(new LaminarAiSdkTelemetry());
|
|
8568
|
-
* ```
|
|
8569
|
-
*
|
|
8570
|
-
* Judge spans and eve eval spans then share ONE tracer provider — whichever of
|
|
8571
|
-
* the two initialized Laminar first (see {@link LaminarReporter.ensureTracing}).
|
|
8572
|
-
* Each judge gets its own span under the eval's root — see
|
|
8573
|
-
* {@link bindEvalContext}, and {@link LaminarReporter.flush} for how spans ship.
|
|
8574
|
-
* Without that line the reporter still records every assertion's score as an
|
|
8575
|
-
* EVALUATOR span; only the model call is missing.
|
|
8576
|
-
*
|
|
8577
|
-
* @example
|
|
8578
10635
|
* ```ts
|
|
8579
|
-
* import {
|
|
8580
|
-
* import {
|
|
10636
|
+
* import { LaminarSpanProcessor } from "@lmnr-ai/lmnr";
|
|
10637
|
+
* import { otelIntegration } from "eve/instrumentation/otel";
|
|
8581
10638
|
*
|
|
8582
|
-
* export default
|
|
8583
|
-
*
|
|
10639
|
+
* export default otelIntegration({
|
|
10640
|
+
* spanProcessors: [new LaminarSpanProcessor()],
|
|
8584
10641
|
* });
|
|
8585
10642
|
* ```
|
|
8586
10643
|
*/
|
|
8587
10644
|
var LaminarReporter = class {
|
|
8588
10645
|
constructor(options = {}) {
|
|
8589
10646
|
this.evalsById = /* @__PURE__ */ new Map();
|
|
8590
|
-
this.
|
|
8591
|
-
this.
|
|
8592
|
-
this.sessionTraceFactory = () => this.mintSessionTrace();
|
|
10647
|
+
this.evalIndexById = /* @__PURE__ */ new Map();
|
|
10648
|
+
this.nextFallbackIndex = 0;
|
|
8593
10649
|
this.options = options;
|
|
8594
10650
|
}
|
|
8595
10651
|
async onRunStart(evaluations, target) {
|
|
8596
10652
|
this.evalId = void 0;
|
|
8597
|
-
|
|
8598
|
-
|
|
8599
|
-
|
|
10653
|
+
const identifiedEvaluations = evaluations.map((evaluation, index) => ({
|
|
10654
|
+
evaluation,
|
|
10655
|
+
index
|
|
10656
|
+
})).filter((item) => typeof item.evaluation.id === "string");
|
|
10657
|
+
this.evalsById = new Map(identifiedEvaluations.map(({ evaluation }) => [evaluation.id, evaluation]));
|
|
10658
|
+
this.evalIndexById = new Map(identifiedEvaluations.map(({ evaluation, index }) => [evaluation.id, index]));
|
|
10659
|
+
this.nextFallbackIndex = evaluations.length;
|
|
8600
10660
|
try {
|
|
8601
10661
|
this.client = this.options.client ?? new require_utils.LaminarClient({
|
|
8602
10662
|
baseUrl: this.options.baseUrl,
|
|
8603
10663
|
projectApiKey: this.options.projectApiKey
|
|
8604
10664
|
});
|
|
8605
10665
|
this.ensureTracing();
|
|
8606
|
-
if (this.options.propagateTraceContext !== false) {
|
|
8607
|
-
activeSessionTraceFactory = this.sessionTraceFactory;
|
|
8608
|
-
patchEveClientSession(await loadEveClientSession());
|
|
8609
|
-
}
|
|
8610
10666
|
const evaluation = await this.client.evals.init(this.options.name, this.options.groupName, {
|
|
8611
10667
|
...this.options.metadata ?? {},
|
|
8612
10668
|
source: "eve",
|
|
@@ -8619,26 +10675,25 @@ var LaminarReporter = class {
|
|
|
8619
10675
|
process.stdout.write(`\nLaminar: check eve eval results at ${url}\n`);
|
|
8620
10676
|
} catch (error) {
|
|
8621
10677
|
logger$2.error(`Laminar eve reporter: failed to start run: ${require_utils.errorMessage(error)}`);
|
|
8622
|
-
if (!this.evalId)
|
|
8623
|
-
this.client = void 0;
|
|
8624
|
-
if (activeSessionTraceFactory === this.sessionTraceFactory) activeSessionTraceFactory = null;
|
|
8625
|
-
}
|
|
10678
|
+
if (!this.evalId) this.client = void 0;
|
|
8626
10679
|
}
|
|
8627
10680
|
}
|
|
8628
|
-
async onEvalComplete(result) {
|
|
10681
|
+
async onEvalComplete(result, context) {
|
|
8629
10682
|
const client = this.client;
|
|
8630
10683
|
const evalId = this.evalId;
|
|
8631
10684
|
if (!client || !evalId) return;
|
|
8632
|
-
const index = this.
|
|
10685
|
+
const index = typeof result.id === "string" ? this.evalIndexById.get(result.id) ?? this.nextFallbackIndex++ : this.nextFallbackIndex++;
|
|
8633
10686
|
const task = result.result ?? {};
|
|
8634
10687
|
const derived = task.derived ?? {};
|
|
8635
|
-
const assertions = result.assertions
|
|
8636
|
-
const
|
|
8637
|
-
const
|
|
8638
|
-
|
|
10688
|
+
const assertions = Array.isArray(result.assertions) ? result.assertions.filter((assertion) => assertion != null && typeof assertion === "object") : [];
|
|
10689
|
+
const evalDefinition = typeof result.id === "string" ? this.evalsById.get(result.id) : void 0;
|
|
10690
|
+
const evalDescription = context?.evaluation?.description ?? evalDefinition?.description;
|
|
10691
|
+
const contexts = normalizeTraceContexts(context?.traceContexts ?? task.traceContexts ?? []);
|
|
10692
|
+
const gradeTrace = this.openGradeTrace({
|
|
10693
|
+
contexts,
|
|
8639
10694
|
description: evalDescription,
|
|
8640
|
-
|
|
8641
|
-
|
|
10695
|
+
evalId: result.id ?? String(index),
|
|
10696
|
+
verdict: result.verdict
|
|
8642
10697
|
});
|
|
8643
10698
|
try {
|
|
8644
10699
|
const datapointId = await client.evals.createDatapoint({
|
|
@@ -8646,17 +10701,19 @@ var LaminarReporter = class {
|
|
|
8646
10701
|
data: result.id ?? null,
|
|
8647
10702
|
target: task.finalMessage ?? null,
|
|
8648
10703
|
index,
|
|
8649
|
-
traceId:
|
|
10704
|
+
traceId: gradeTrace.traceId,
|
|
8650
10705
|
metadata: {
|
|
8651
10706
|
name: result.id,
|
|
8652
10707
|
verdict: result.verdict,
|
|
8653
10708
|
status: task.status,
|
|
8654
10709
|
description: evalDescription,
|
|
8655
10710
|
sessionId: task.sessionId,
|
|
8656
|
-
traceResolution:
|
|
10711
|
+
traceResolution: gradeTrace.source,
|
|
10712
|
+
selectedTraceContext: gradeTrace.selected ? traceContextMetadata([gradeTrace.selected])[0] : void 0,
|
|
10713
|
+
traceContexts: traceContextMetadata(gradeTrace.contexts),
|
|
8657
10714
|
skipReason: result.skipReason,
|
|
8658
10715
|
error: result.error,
|
|
8659
|
-
toolCalls: (derived.toolCalls
|
|
10716
|
+
toolCalls: (Array.isArray(derived.toolCalls) ? derived.toolCalls : []).map((call) => call?.name).filter((name) => typeof name === "string"),
|
|
8660
10717
|
toolCallCount: derived.toolCallCount,
|
|
8661
10718
|
parked: derived.parked,
|
|
8662
10719
|
failureCode: derived.failureCode,
|
|
@@ -8665,44 +10722,30 @@ var LaminarReporter = class {
|
|
|
8665
10722
|
...failedAssertionsMetadata(assertions)
|
|
8666
10723
|
}
|
|
8667
10724
|
});
|
|
8668
|
-
const executorOutput = task.output ?? task.finalMessage ?? null;
|
|
8669
10725
|
await client.evals.updateDatapoint({
|
|
8670
10726
|
evalId,
|
|
8671
10727
|
datapointId,
|
|
8672
|
-
scores: resultToScores(result),
|
|
8673
|
-
executorOutput
|
|
10728
|
+
scores: resultToScores(result, assertions),
|
|
10729
|
+
executorOutput: task.output ?? task.finalMessage ?? null
|
|
8674
10730
|
});
|
|
8675
10731
|
} catch (error) {
|
|
8676
|
-
|
|
10732
|
+
gradeTrace.span.recordException(error);
|
|
8677
10733
|
logger$2.error(`Laminar eve reporter: failed to report eval "${result.id ?? index}": ${require_utils.errorMessage(error)}`);
|
|
8678
10734
|
} finally {
|
|
8679
|
-
|
|
8680
|
-
|
|
8681
|
-
evalId: result.id,
|
|
8682
|
-
description: evalDescription,
|
|
8683
|
-
verdict: result.verdict,
|
|
8684
|
-
status: task.status,
|
|
10735
|
+
this.finishGradeTrace(gradeTrace, {
|
|
10736
|
+
assertions,
|
|
8685
10737
|
output: task.output ?? task.finalMessage ?? null,
|
|
8686
|
-
|
|
10738
|
+
status: task.status
|
|
8687
10739
|
});
|
|
8688
10740
|
await this.flush();
|
|
8689
10741
|
}
|
|
8690
10742
|
}
|
|
8691
|
-
async onRunComplete() {
|
|
8692
|
-
this.finishOpenSessionTraces();
|
|
8693
|
-
if (activeSessionTraceFactory === this.sessionTraceFactory) activeSessionTraceFactory = null;
|
|
10743
|
+
async onRunComplete(_summary) {
|
|
8694
10744
|
await this.flush();
|
|
8695
10745
|
this.evalId = void 0;
|
|
8696
10746
|
this.evalsById.clear();
|
|
10747
|
+
this.evalIndexById.clear();
|
|
8697
10748
|
}
|
|
8698
|
-
/**
|
|
8699
|
-
* Point the process's Laminar tracing at this reporter's project, unless
|
|
8700
|
-
* something already initialized it. Mirrors `LaminarAiSdkTelemetry`, which
|
|
8701
|
-
* initializes the same way for judge spans — so eve eval spans and judge
|
|
8702
|
-
* spans share ONE tracer provider however the run was set up. Without this,
|
|
8703
|
-
* `getTracer()` returns a noop whenever nobody registered the AI SDK
|
|
8704
|
-
* integration, and every eve span would be dropped silently.
|
|
8705
|
-
*/
|
|
8706
10749
|
ensureTracing() {
|
|
8707
10750
|
if (Laminar.initialized()) {
|
|
8708
10751
|
const ourKey = this.client?.apiKey ?? this.options.projectApiKey;
|
|
@@ -8718,140 +10761,79 @@ var LaminarReporter = class {
|
|
|
8718
10761
|
disableBatch: true
|
|
8719
10762
|
});
|
|
8720
10763
|
}
|
|
8721
|
-
/**
|
|
8722
|
-
* Flush the process's Laminar pipeline — eve eval spans AND judge spans, which
|
|
8723
|
-
* now share one provider. eve ends `eve eval` with `process.exit()` (skipping
|
|
8724
|
-
* `beforeExit`), so these calls are the only points at which spans ship.
|
|
8725
|
-
* Never `shutdown()`: the pipeline may belong to the host, not to us.
|
|
8726
|
-
*/
|
|
8727
10764
|
async flush() {
|
|
8728
10765
|
try {
|
|
8729
10766
|
await Laminar.flush();
|
|
8730
10767
|
} catch (error) {
|
|
8731
|
-
logger$2.warn(`Laminar eve reporter: failed to flush spans:
|
|
10768
|
+
logger$2.warn(`Laminar eve reporter: failed to flush spans: ${require_utils.errorMessage(error)}`);
|
|
8732
10769
|
}
|
|
8733
10770
|
}
|
|
8734
|
-
|
|
8735
|
-
|
|
8736
|
-
* root plus the EXECUTOR child the agent's turn is parented to. Called from
|
|
8737
|
-
* the patched `send`, so it must never throw into the user's eval.
|
|
8738
|
-
*/
|
|
8739
|
-
mintSessionTrace() {
|
|
10771
|
+
openGradeTrace({ contexts, description, evalId, verdict }) {
|
|
10772
|
+
const selected = selectTraceContext(contexts);
|
|
8740
10773
|
const tracer = getTracer();
|
|
8741
|
-
const
|
|
8742
|
-
|
|
8743
|
-
|
|
8744
|
-
"lmnr.eve.reporter": EVE_REPORTER_TRACER_NAME
|
|
8745
|
-
} }, _opentelemetry_api.ROOT_CONTEXT);
|
|
8746
|
-
const executorSpan = tracer.startSpan(EXECUTOR_SPAN_NAME, { attributes: {
|
|
8747
|
-
[require_utils.SPAN_TYPE]: "EXECUTOR",
|
|
8748
|
-
[require_utils.TRACE_TYPE]: "EVALUATION"
|
|
8749
|
-
} }, _opentelemetry_api.trace.setSpan(_opentelemetry_api.ROOT_CONTEXT, rootSpan));
|
|
8750
|
-
const sessionTrace = {
|
|
8751
|
-
traceId: require_utils.otelTraceIdToUUID(rootSpan.spanContext().traceId),
|
|
8752
|
-
rootSpan,
|
|
8753
|
-
executorSpan,
|
|
8754
|
-
tracer,
|
|
8755
|
-
headers: buildTraceHeaders(rootSpan, executorSpan),
|
|
8756
|
-
sessionIds: /* @__PURE__ */ new Set(),
|
|
8757
|
-
ended: false
|
|
8758
|
-
};
|
|
8759
|
-
this.openSessionTraces.add(sessionTrace);
|
|
8760
|
-
return sessionTrace;
|
|
8761
|
-
}
|
|
8762
|
-
/** Grade, close and release one session trace. Idempotent. */
|
|
8763
|
-
finishSessionTrace(sessionTrace, grade) {
|
|
8764
|
-
if (sessionTrace.ended) return;
|
|
8765
|
-
sessionTrace.ended = true;
|
|
8766
|
-
this.openSessionTraces.delete(sessionTrace);
|
|
8767
|
-
for (const sessionId of sessionTrace.sessionIds) traceByEveSessionId.delete(sessionId);
|
|
8768
|
-
try {
|
|
8769
|
-
sessionTrace.rootSpan.setAttributes({
|
|
8770
|
-
"lmnr.eve.eval.id": grade.evalId ?? "",
|
|
8771
|
-
"lmnr.eve.eval.verdict": grade.verdict ?? "",
|
|
8772
|
-
"lmnr.eve.eval.status": grade.status ?? "",
|
|
8773
|
-
[require_utils.SPAN_INPUT]: JSON.stringify({
|
|
8774
|
-
evalId: grade.evalId,
|
|
8775
|
-
description: grade.description
|
|
8776
|
-
}),
|
|
8777
|
-
[require_utils.SPAN_OUTPUT]: JSON.stringify(grade.output ?? null),
|
|
8778
|
-
...require_utils.metadataToAttributes({
|
|
8779
|
-
source: "eve",
|
|
8780
|
-
eveEvalId: grade.evalId ?? "",
|
|
8781
|
-
eveEvalDescription: grade.description ?? "",
|
|
8782
|
-
eveEvalVerdict: grade.verdict ?? "",
|
|
8783
|
-
eveSessionId: [...sessionTrace.sessionIds].join(",")
|
|
8784
|
-
})
|
|
8785
|
-
});
|
|
8786
|
-
sessionTrace.executorSpan.setAttribute(require_utils.SPAN_OUTPUT, JSON.stringify(grade.output ?? null));
|
|
8787
|
-
this.recordEvaluatorSpans(sessionTrace, grade.assertions ?? []);
|
|
8788
|
-
sessionTrace.executorSpan.end();
|
|
8789
|
-
sessionTrace.rootSpan.end();
|
|
8790
|
-
} catch (error) {
|
|
8791
|
-
logger$2.warn(`Laminar eve reporter: failed to finish the session trace: ` + require_utils.errorMessage(error));
|
|
8792
|
-
}
|
|
8793
|
-
}
|
|
8794
|
-
/**
|
|
8795
|
-
* One EVALUATOR span per eve assertion, judge or not. They are created at
|
|
8796
|
-
* grade time (the assertions only exist then) as short children of the
|
|
8797
|
-
* still-open root. No explicit parent path is needed: the root keeps the name
|
|
8798
|
-
* it was minted with, so the processor's cached path for it is still correct.
|
|
8799
|
-
*/
|
|
8800
|
-
recordEvaluatorSpans(sessionTrace, assertions) {
|
|
8801
|
-
const rootContext = _opentelemetry_api.trace.setSpan(_opentelemetry_api.ROOT_CONTEXT, sessionTrace.rootSpan);
|
|
8802
|
-
for (const assertion of assertions) {
|
|
8803
|
-
const name = assertion.name ?? "assertion";
|
|
8804
|
-
const output = JSON.stringify({
|
|
8805
|
-
score: assertion.score ?? null,
|
|
8806
|
-
passed: didAssertionPass(assertion),
|
|
8807
|
-
severity: assertion.severity,
|
|
8808
|
-
threshold: assertion.threshold,
|
|
8809
|
-
message: assertion.message
|
|
8810
|
-
});
|
|
8811
|
-
sessionTrace.tracer.startSpan(name, { attributes: {
|
|
8812
|
-
[require_utils.SPAN_TYPE]: "EVALUATOR",
|
|
8813
|
-
[require_utils.TRACE_TYPE]: "EVALUATION",
|
|
8814
|
-
[require_utils.SPAN_OUTPUT]: output
|
|
8815
|
-
} }, rootContext).end();
|
|
8816
|
-
}
|
|
8817
|
-
}
|
|
8818
|
-
finishOpenSessionTraces() {
|
|
8819
|
-
for (const sessionTrace of [...this.openSessionTraces]) this.finishSessionTrace(sessionTrace, {});
|
|
8820
|
-
}
|
|
8821
|
-
/**
|
|
8822
|
-
* Resolve the trace this datapoint links to. There is no lookup: either
|
|
8823
|
-
* `patchEveClientSession` already minted the session's trace (and we know its
|
|
8824
|
-
* id), or we open a reporter-owned EVALUATION span to hold the grade.
|
|
8825
|
-
*/
|
|
8826
|
-
resolveDatapointTrace({ evalId, description, verdict, sessionId }) {
|
|
8827
|
-
const sessionTrace = sessionId ? traceByEveSessionId.get(sessionId) : void 0;
|
|
8828
|
-
if (sessionTrace) return {
|
|
8829
|
-
traceId: sessionTrace.traceId,
|
|
8830
|
-
source: "propagated",
|
|
8831
|
-
sessionTrace
|
|
8832
|
-
};
|
|
8833
|
-
const span = getTracer().startSpan(`eve eval ${evalId}`, { attributes: {
|
|
10774
|
+
const parentContext = selected ? _opentelemetry_api.trace.setSpanContext(_opentelemetry_api.ROOT_CONTEXT, this.toSpanContext(selected)) : _opentelemetry_api.ROOT_CONTEXT;
|
|
10775
|
+
const source = selected ? "eve-native" : "reporter-fallback";
|
|
10776
|
+
const span = tracer.startSpan(`eve eval ${evalId}`, { attributes: {
|
|
8834
10777
|
[require_utils.SPAN_TYPE]: "EVALUATION",
|
|
8835
10778
|
[require_utils.TRACE_TYPE]: "EVALUATION",
|
|
8836
10779
|
"lmnr.eve.reporter": EVE_REPORTER_TRACER_NAME,
|
|
8837
10780
|
"lmnr.eve.eval.id": evalId,
|
|
8838
10781
|
"lmnr.eve.eval.verdict": verdict ?? "",
|
|
8839
|
-
|
|
8840
|
-
"lmnr.eve.trace_resolution": "reporter-fallback",
|
|
10782
|
+
"lmnr.eve.trace_resolution": source,
|
|
8841
10783
|
...require_utils.metadataToAttributes({
|
|
8842
10784
|
source: "eve",
|
|
8843
10785
|
eveEvalId: evalId,
|
|
8844
10786
|
eveEvalDescription: description ?? "",
|
|
8845
10787
|
eveEvalVerdict: verdict ?? "",
|
|
8846
|
-
eveSessionId: sessionId ?? ""
|
|
10788
|
+
eveSessionId: selected?.sessionId ?? "",
|
|
10789
|
+
eveTraceCount: contexts.length
|
|
10790
|
+
}),
|
|
10791
|
+
[require_utils.SPAN_INPUT]: JSON.stringify({
|
|
10792
|
+
evalId,
|
|
10793
|
+
description
|
|
8847
10794
|
})
|
|
8848
|
-
} },
|
|
10795
|
+
} }, parentContext);
|
|
10796
|
+
return {
|
|
10797
|
+
traceId: require_utils.otelTraceIdToUUID(span.spanContext().traceId),
|
|
10798
|
+
source,
|
|
10799
|
+
span,
|
|
10800
|
+
tracer,
|
|
10801
|
+
selected,
|
|
10802
|
+
contexts
|
|
10803
|
+
};
|
|
10804
|
+
}
|
|
10805
|
+
toSpanContext(context) {
|
|
8849
10806
|
return {
|
|
8850
|
-
traceId:
|
|
8851
|
-
|
|
8852
|
-
|
|
10807
|
+
traceId: context.traceId.toLowerCase(),
|
|
10808
|
+
spanId: context.spanId.toLowerCase(),
|
|
10809
|
+
traceFlags: context.traceFlags,
|
|
10810
|
+
isRemote: true
|
|
8853
10811
|
};
|
|
8854
10812
|
}
|
|
10813
|
+
finishGradeTrace(gradeTrace, grade) {
|
|
10814
|
+
try {
|
|
10815
|
+
gradeTrace.span.setAttributes({
|
|
10816
|
+
"lmnr.eve.eval.status": grade.status ?? "",
|
|
10817
|
+
[require_utils.SPAN_OUTPUT]: JSON.stringify(grade.output ?? null)
|
|
10818
|
+
});
|
|
10819
|
+
const parentContext = _opentelemetry_api.trace.setSpan(_opentelemetry_api.ROOT_CONTEXT, gradeTrace.span);
|
|
10820
|
+
for (const assertion of grade.assertions) gradeTrace.tracer.startSpan(assertion.name ?? "assertion", { attributes: {
|
|
10821
|
+
[require_utils.SPAN_TYPE]: "EVALUATOR",
|
|
10822
|
+
[require_utils.TRACE_TYPE]: "EVALUATION",
|
|
10823
|
+
[require_utils.SPAN_OUTPUT]: JSON.stringify({
|
|
10824
|
+
score: assertion.score ?? null,
|
|
10825
|
+
passed: didAssertionPass(assertion),
|
|
10826
|
+
severity: assertion.severity,
|
|
10827
|
+
threshold: assertion.threshold,
|
|
10828
|
+
message: assertion.message
|
|
10829
|
+
})
|
|
10830
|
+
} }, parentContext).end();
|
|
10831
|
+
} catch (error) {
|
|
10832
|
+
logger$2.warn(`Laminar eve reporter: failed to finish the grade trace: ${require_utils.errorMessage(error)}`);
|
|
10833
|
+
} finally {
|
|
10834
|
+
gradeTrace.span.end();
|
|
10835
|
+
}
|
|
10836
|
+
}
|
|
8855
10837
|
};
|
|
8856
10838
|
//#endregion
|
|
8857
10839
|
//#region src/debug/hash.ts
|