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