@lmnr-ai/lmnr 0.8.44 → 0.8.45
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 +23 -25
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +1 -1
- package/dist/cli.d.mts +1 -1
- package/dist/cli.mjs +7 -8
- package/dist/cli.mjs.map +1 -1
- package/dist/{evaluations-RbBZb5yl.d.cts → evaluations-BHEE6fmA.d.cts} +17934 -8631
- package/dist/{evaluations-CVo0ms_r.d.mts → evaluations-BbIIQMLr.d.mts} +17934 -8631
- package/dist/{file-utils-1olUHMKC.mjs → file-utils-BtzEDUy6.mjs} +2 -2
- package/dist/file-utils-BtzEDUy6.mjs.map +1 -0
- package/dist/{file-utils-CMO5MuV9.cjs → file-utils-pZG20hHb.cjs} +6 -6
- package/dist/file-utils-pZG20hHb.cjs.map +1 -0
- package/dist/index.cjs +574 -435
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1533 -437
- package/dist/index.d.mts +1533 -437
- package/dist/index.mjs +287 -148
- package/dist/index.mjs.map +1 -1
- package/dist/opentelemetry-lib/instrumentation/temporal/workflow-interceptors.cjs +159 -62
- package/dist/opentelemetry-lib/instrumentation/temporal/workflow-interceptors.cjs.map +1 -1
- package/dist/opentelemetry-lib/instrumentation/temporal/workflow-interceptors.d.cts +3388 -250
- package/dist/opentelemetry-lib/instrumentation/temporal/workflow-interceptors.d.mts +3388 -250
- package/dist/opentelemetry-lib/instrumentation/temporal/workflow-interceptors.mjs +159 -62
- package/dist/opentelemetry-lib/instrumentation/temporal/workflow-interceptors.mjs.map +1 -1
- package/dist/{dist-BtB31bk1.cjs → utils-CatgN6Pb.cjs} +558 -558
- package/dist/utils-CatgN6Pb.cjs.map +1 -0
- package/dist/{dist-Cjres0a5.mjs → utils-UmlDPMxe.mjs} +552 -552
- package/dist/utils-UmlDPMxe.mjs.map +1 -0
- package/package.json +30 -30
- package/dist/dist-BtB31bk1.cjs.map +0 -1
- package/dist/dist-Cjres0a5.mjs.map +0 -1
- package/dist/file-utils-1olUHMKC.mjs.map +0 -1
- package/dist/file-utils-CMO5MuV9.cjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { r as __require } from "./rolldown-runtime-2rV9d50f.mjs";
|
|
2
|
-
import { A as
|
|
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-UmlDPMxe.mjs";
|
|
3
3
|
import { n as TRACEPARENT_HEADER, t as LAMINAR_SPAN_CONTEXT_HEADER } from "./consts-CTTu3jhI.mjs";
|
|
4
|
+
import * as path from "path";
|
|
4
5
|
import { DiagConsoleLogger, DiagLogLevel, ROOT_CONTEXT, SpanKind, SpanStatusCode, TraceFlags, context, createContextKey, diag, isSpanContextValid, trace } from "@opentelemetry/api";
|
|
5
6
|
import { AsyncLocalStorageContextManager } from "@opentelemetry/context-async-hooks";
|
|
6
7
|
import { InstrumentationBase, InstrumentationNodeModuleDefinition, registerInstrumentations } from "@opentelemetry/instrumentation";
|
|
7
8
|
import { AlwaysOnSampler, BatchSpanProcessor, SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
8
9
|
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
|
|
9
10
|
import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from "@opentelemetry/semantic-conventions";
|
|
10
|
-
import * as path from "path";
|
|
11
11
|
import { AnthropicInstrumentation } from "@traceloop/instrumentation-anthropic";
|
|
12
12
|
import { BedrockInstrumentation } from "@traceloop/instrumentation-bedrock";
|
|
13
13
|
import { ChromaDBInstrumentation } from "@traceloop/instrumentation-chromadb";
|
|
@@ -176,7 +176,7 @@ var LaminarDataset = class extends EvaluationDataset {
|
|
|
176
176
|
*/
|
|
177
177
|
async push(paths, recursive = false) {
|
|
178
178
|
if (!this.client) throw new Error("Client not set");
|
|
179
|
-
const { loadFromPaths } = await import("./file-utils-
|
|
179
|
+
const { loadFromPaths } = await import("./file-utils-BtzEDUy6.mjs").then((n) => n.t);
|
|
180
180
|
const data = await loadFromPaths(Array.isArray(paths) ? paths : [paths], recursive);
|
|
181
181
|
if (data.length === 0) {
|
|
182
182
|
console.warn("No data to push. Skipping");
|
|
@@ -192,7 +192,7 @@ var LaminarDataset = class extends EvaluationDataset {
|
|
|
192
192
|
};
|
|
193
193
|
//#endregion
|
|
194
194
|
//#region src/opentelemetry-lib/tracing/compat.ts
|
|
195
|
-
const logger$
|
|
195
|
+
const logger$26 = initializeLogger();
|
|
196
196
|
const makeSpanOtelV2Compatible = (span) => {
|
|
197
197
|
const spanAny = span;
|
|
198
198
|
if (spanAny.instrumentationScope && !spanAny.instrumentationLibrary) Object.assign(span, { instrumentationLibrary: spanAny.instrumentationScope });
|
|
@@ -238,7 +238,7 @@ const createResource = (attributes) => {
|
|
|
238
238
|
if (resources.Resource) return new resources.Resource(attributes);
|
|
239
239
|
throw new Error("Unable to create Resource: neither v1 nor v2 API found");
|
|
240
240
|
} catch {
|
|
241
|
-
logger$
|
|
241
|
+
logger$26.debug("Laminar failed to @opentelemetry/resources, continuing without it");
|
|
242
242
|
return;
|
|
243
243
|
}
|
|
244
244
|
};
|
|
@@ -271,6 +271,23 @@ function convertUint8ArrayToBase64(array) {
|
|
|
271
271
|
return btoa(latin1string);
|
|
272
272
|
}
|
|
273
273
|
/**
|
|
274
|
+
* Checks whether `key` looks like an AI SDK stream property WITHOUT reading it.
|
|
275
|
+
*
|
|
276
|
+
* Reading is not safe here: AI SDK exposes `textStream` / `fullStream` as getters that build a
|
|
277
|
+
* fresh stream on every access. On `streamText` that tees (and mutates) the internal base stream,
|
|
278
|
+
* leaving an abandoned branch that buffers the whole response; on `streamObject` it `pipeThrough`s
|
|
279
|
+
* the base stream directly, permanently LOCKING it so the caller's own access throws. So we only
|
|
280
|
+
* inspect the property descriptor: AI SDK's are accessors, whereas a same-named host API (e.g.
|
|
281
|
+
* Node 26's `Response.prototype.textStream`) is a data property holding a method.
|
|
282
|
+
*/
|
|
283
|
+
const hasStreamAccessor = (obj, key) => {
|
|
284
|
+
for (let o = obj; o !== null; o = Object.getPrototypeOf(o)) {
|
|
285
|
+
const descriptor = Object.getOwnPropertyDescriptor(o, key);
|
|
286
|
+
if (descriptor) return typeof descriptor.value !== "function";
|
|
287
|
+
}
|
|
288
|
+
return false;
|
|
289
|
+
};
|
|
290
|
+
/**
|
|
274
291
|
* This function checks if an object is stream or stream-like, and returns information
|
|
275
292
|
* about the stream type and how to handle it.
|
|
276
293
|
* @param response - The result of a function, either an AI SDK result, a raw stream, or any other
|
|
@@ -279,10 +296,6 @@ function convertUint8ArrayToBase64(array) {
|
|
|
279
296
|
*/
|
|
280
297
|
const getStream = (response) => {
|
|
281
298
|
if (!response || typeof response !== "object") return { type: null };
|
|
282
|
-
if ("textStream" in response || "fullStream" in response) return {
|
|
283
|
-
type: "aisdk-result",
|
|
284
|
-
result: response
|
|
285
|
-
};
|
|
286
299
|
if (response instanceof Response) return {
|
|
287
300
|
type: "response",
|
|
288
301
|
response
|
|
@@ -291,6 +304,10 @@ const getStream = (response) => {
|
|
|
291
304
|
type: "readable-stream",
|
|
292
305
|
stream: response
|
|
293
306
|
};
|
|
307
|
+
if (hasStreamAccessor(response, "textStream") || hasStreamAccessor(response, "fullStream")) return {
|
|
308
|
+
type: "aisdk-result",
|
|
309
|
+
result: response
|
|
310
|
+
};
|
|
294
311
|
if (Symbol.asyncIterator in response && !(response instanceof ReadableStream)) return {
|
|
295
312
|
type: "async-iterable",
|
|
296
313
|
iterable: response
|
|
@@ -423,7 +440,7 @@ const consumeResponse = async (response) => {
|
|
|
423
440
|
};
|
|
424
441
|
//#endregion
|
|
425
442
|
//#region src/opentelemetry-lib/tracing/stream-utils.ts
|
|
426
|
-
const logger$
|
|
443
|
+
const logger$25 = initializeLogger();
|
|
427
444
|
const pendingStreamProcessing = /* @__PURE__ */ new Set();
|
|
428
445
|
/**
|
|
429
446
|
* Wait for all pending stream processing to complete with a timeout
|
|
@@ -431,7 +448,7 @@ const pendingStreamProcessing = /* @__PURE__ */ new Set();
|
|
|
431
448
|
*/
|
|
432
449
|
const waitForPendingStreams = async (timeoutMs = 5e3) => {
|
|
433
450
|
if (pendingStreamProcessing.size === 0) return;
|
|
434
|
-
logger$
|
|
451
|
+
logger$25.debug(`Waiting for ${pendingStreamProcessing.size} pending stream(s)`);
|
|
435
452
|
const initialCount = pendingStreamProcessing.size;
|
|
436
453
|
const pendingPromises = Array.from(pendingStreamProcessing);
|
|
437
454
|
let timedOut = false;
|
|
@@ -442,7 +459,7 @@ const waitForPendingStreams = async (timeoutMs = 5e3) => {
|
|
|
442
459
|
}, timeoutMs);
|
|
443
460
|
});
|
|
444
461
|
await Promise.race([Promise.allSettled(pendingPromises), timeoutPromise]);
|
|
445
|
-
if (timedOut && pendingStreamProcessing.size > 0) logger$
|
|
462
|
+
if (timedOut && pendingStreamProcessing.size > 0) logger$25.warn(`Timeout waiting for ${pendingStreamProcessing.size} pending stream(s) after ${timeoutMs}ms (started with ${initialCount})`);
|
|
446
463
|
};
|
|
447
464
|
/**
|
|
448
465
|
* Track a background promise and ensure cleanup
|
|
@@ -458,34 +475,34 @@ const trackStreamProcessing = (task) => {
|
|
|
458
475
|
* while passing through the original result to the caller
|
|
459
476
|
*/
|
|
460
477
|
const handleStreamResult = (originalResult, streamInfo, span, ignoreOutput, serialize) => {
|
|
461
|
-
logger$
|
|
478
|
+
logger$25.debug(`Handling stream result of type: ${streamInfo.type}`);
|
|
462
479
|
if (streamInfo.type === "aisdk-result") {
|
|
463
480
|
trackStreamProcessing(async () => {
|
|
464
|
-
logger$
|
|
481
|
+
logger$25.debug("Starting background processing for AI SDK result");
|
|
465
482
|
let partialOutput = {};
|
|
466
483
|
try {
|
|
467
484
|
partialOutput = await consumeAISDKResult(streamInfo.result);
|
|
468
485
|
if (shouldSendTraces() && !ignoreOutput) try {
|
|
469
486
|
span.setAttribute(SPAN_OUTPUT, serialize(partialOutput));
|
|
470
487
|
} catch (error) {
|
|
471
|
-
logger$
|
|
488
|
+
logger$25.warn("Failed to serialize AI SDK stream output: " + errorMessage(error));
|
|
472
489
|
}
|
|
473
490
|
} catch (error) {
|
|
474
491
|
if (Object.keys(partialOutput).length > 0 && shouldSendTraces() && !ignoreOutput) try {
|
|
475
492
|
span.setAttribute(SPAN_OUTPUT, serialize(partialOutput));
|
|
476
493
|
} catch (serError) {
|
|
477
|
-
logger$
|
|
494
|
+
logger$25.warn("Failed to serialize partial AI SDK output: " + errorMessage(serError));
|
|
478
495
|
}
|
|
479
496
|
span.recordException(error);
|
|
480
497
|
} finally {
|
|
481
|
-
logger$
|
|
498
|
+
logger$25.debug("Ending span for AI SDK result");
|
|
482
499
|
span.end();
|
|
483
500
|
}
|
|
484
501
|
});
|
|
485
502
|
return originalResult;
|
|
486
503
|
}
|
|
487
504
|
if (streamInfo.type === "readable-stream") {
|
|
488
|
-
logger$
|
|
505
|
+
logger$25.debug("Setting up background processing for ReadableStream");
|
|
489
506
|
const { stream, dataPromise } = consumeAndTeeReadableStream(streamInfo.stream);
|
|
490
507
|
trackStreamProcessing(async () => {
|
|
491
508
|
try {
|
|
@@ -497,18 +514,18 @@ const handleStreamResult = (originalResult, streamInfo, span, ignoreOutput, seri
|
|
|
497
514
|
};
|
|
498
515
|
span.setAttribute(SPAN_OUTPUT, serialize(output));
|
|
499
516
|
} catch (serError) {
|
|
500
|
-
logger$
|
|
517
|
+
logger$25.warn("Failed to serialize stream output: " + errorMessage(serError));
|
|
501
518
|
}
|
|
502
519
|
if (error) span.recordException(error);
|
|
503
520
|
} finally {
|
|
504
|
-
logger$
|
|
521
|
+
logger$25.debug("Ending span for ReadableStream");
|
|
505
522
|
span.end();
|
|
506
523
|
}
|
|
507
524
|
});
|
|
508
525
|
return stream;
|
|
509
526
|
}
|
|
510
527
|
if (streamInfo.type === "async-iterable") {
|
|
511
|
-
logger$
|
|
528
|
+
logger$25.debug("Setting up background processing for AsyncIterable");
|
|
512
529
|
const { iterable, dataPromise } = consumeAndTeeAsyncIterable(streamInfo.iterable);
|
|
513
530
|
trackStreamProcessing(async () => {
|
|
514
531
|
try {
|
|
@@ -520,18 +537,18 @@ const handleStreamResult = (originalResult, streamInfo, span, ignoreOutput, seri
|
|
|
520
537
|
};
|
|
521
538
|
span.setAttribute(SPAN_OUTPUT, serialize(output));
|
|
522
539
|
} catch (serError) {
|
|
523
|
-
logger$
|
|
540
|
+
logger$25.warn("Failed to serialize async iterable output: " + errorMessage(serError));
|
|
524
541
|
}
|
|
525
542
|
if (error) span.recordException(error);
|
|
526
543
|
} finally {
|
|
527
|
-
logger$
|
|
544
|
+
logger$25.debug("Ending span for AsyncIterable");
|
|
528
545
|
span.end();
|
|
529
546
|
}
|
|
530
547
|
});
|
|
531
548
|
return iterable;
|
|
532
549
|
}
|
|
533
550
|
if (streamInfo.type === "response") {
|
|
534
|
-
logger$
|
|
551
|
+
logger$25.debug("Setting up background processing for Response");
|
|
535
552
|
const originalResponse = streamInfo.response;
|
|
536
553
|
if (!originalResponse.body) {
|
|
537
554
|
span.end();
|
|
@@ -553,19 +570,19 @@ const handleStreamResult = (originalResult, streamInfo, span, ignoreOutput, seri
|
|
|
553
570
|
};
|
|
554
571
|
span.setAttribute(SPAN_OUTPUT, serialize(output));
|
|
555
572
|
} catch (serError) {
|
|
556
|
-
logger$
|
|
573
|
+
logger$25.warn("Failed to serialize response output: " + errorMessage(serError));
|
|
557
574
|
}
|
|
558
575
|
if (error) span.recordException(error);
|
|
559
576
|
} catch (err) {
|
|
560
577
|
span.recordException(err);
|
|
561
578
|
} finally {
|
|
562
|
-
logger$
|
|
579
|
+
logger$25.debug("Ending span for Response");
|
|
563
580
|
span.end();
|
|
564
581
|
}
|
|
565
582
|
});
|
|
566
583
|
return newResponse;
|
|
567
584
|
}
|
|
568
|
-
logger$
|
|
585
|
+
logger$25.warn("Stream type not handled, ending span immediately");
|
|
569
586
|
span.end();
|
|
570
587
|
return originalResult;
|
|
571
588
|
};
|
|
@@ -643,7 +660,7 @@ const writeDebugSessionFile = (file, dir = process.cwd()) => {
|
|
|
643
660
|
* contract. This file is part of the cross-language parity surface — keep its
|
|
644
661
|
* logic line-comparable with the Python `config.py`.
|
|
645
662
|
*/
|
|
646
|
-
const logger$
|
|
663
|
+
const logger$24 = initializeLogger();
|
|
647
664
|
const TRUTHY = new Set([
|
|
648
665
|
"true",
|
|
649
666
|
"1",
|
|
@@ -679,7 +696,7 @@ const parseCacheUntil = (value) => {
|
|
|
679
696
|
if (value === void 0 || value === "") return null;
|
|
680
697
|
const spanId = normalizeSpanId(value);
|
|
681
698
|
if (spanId !== null) return spanId;
|
|
682
|
-
logger$
|
|
699
|
+
logger$24.warn(`LMNR_DEBUG_CACHE_UNTIL=${JSON.stringify(value)} is not a span id; ignoring`);
|
|
683
700
|
return null;
|
|
684
701
|
};
|
|
685
702
|
/**
|
|
@@ -1030,7 +1047,7 @@ const resetDebugRuntime = () => {
|
|
|
1030
1047
|
};
|
|
1031
1048
|
//#endregion
|
|
1032
1049
|
//#region src/opentelemetry-lib/tracing/span.ts
|
|
1033
|
-
const logger$
|
|
1050
|
+
const logger$23 = initializeLogger();
|
|
1034
1051
|
/**
|
|
1035
1052
|
* Build the debug block to propagate, or undefined when debug mode is off.
|
|
1036
1053
|
*
|
|
@@ -1180,7 +1197,7 @@ var LaminarSpan = class LaminarSpan {
|
|
|
1180
1197
|
} catch {}
|
|
1181
1198
|
metadata[key.replace(`${ASSOCIATION_PROPERTIES}.metadata.`, "")] = value;
|
|
1182
1199
|
}
|
|
1183
|
-
} else logger$
|
|
1200
|
+
} else logger$23.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.");
|
|
1184
1201
|
return {
|
|
1185
1202
|
spanId: otelSpanIdToUUID(this._span.spanContext().spanId),
|
|
1186
1203
|
traceId: otelTraceIdToUUID(this._span.spanContext().traceId),
|
|
@@ -1207,14 +1224,14 @@ var LaminarSpan = class LaminarSpan {
|
|
|
1207
1224
|
}
|
|
1208
1225
|
get tags() {
|
|
1209
1226
|
if (!this._span.attributes) {
|
|
1210
|
-
logger$
|
|
1227
|
+
logger$23.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.");
|
|
1211
1228
|
return [];
|
|
1212
1229
|
}
|
|
1213
1230
|
return this._span.attributes[`lmnr.association.properties.tags`] ?? [];
|
|
1214
1231
|
}
|
|
1215
1232
|
get laminarAssociationProperties() {
|
|
1216
1233
|
if (!this._span.attributes) {
|
|
1217
|
-
logger$
|
|
1234
|
+
logger$23.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.");
|
|
1218
1235
|
return {};
|
|
1219
1236
|
}
|
|
1220
1237
|
try {
|
|
@@ -1410,7 +1427,7 @@ var LaminarContextManager = class {
|
|
|
1410
1427
|
};
|
|
1411
1428
|
//#endregion
|
|
1412
1429
|
//#region src/opentelemetry-lib/instrumentation/claude-agent-sdk/proxy.ts
|
|
1413
|
-
const logger$
|
|
1430
|
+
const logger$22 = initializeLogger();
|
|
1414
1431
|
const DEFAULT_ANTHROPIC_BASE_URL = "https://api.anthropic.com";
|
|
1415
1432
|
const DEFAULT_CC_PROXY_PORT = 45667;
|
|
1416
1433
|
const CC_PROXY_PORT_ATTEMPTS = 50;
|
|
@@ -1662,7 +1679,7 @@ const resolveTargetUrlFromEnv = (envDict, fallback = DEFAULT_ANTHROPIC_BASE_URL,
|
|
|
1662
1679
|
if (foundryBaseUrl) return foundryBaseUrl.replace(/\/$/, "");
|
|
1663
1680
|
const foundryResource = getEnvValue(FOUNDRY_RESOURCE_ENV);
|
|
1664
1681
|
if (foundryResource) return `https://${foundryResource}.services.ai.azure.com/anthropic`;
|
|
1665
|
-
logger$
|
|
1682
|
+
logger$22.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.`);
|
|
1666
1683
|
return null;
|
|
1667
1684
|
}
|
|
1668
1685
|
if (isTruthyEnv(getEnvValue(BEDROCK_USE_ENV))) {
|
|
@@ -1671,7 +1688,7 @@ const resolveTargetUrlFromEnv = (envDict, fallback = DEFAULT_ANTHROPIC_BASE_URL,
|
|
|
1671
1688
|
let region = getEnvValue(BEDROCK_AWS_REGION_ENV);
|
|
1672
1689
|
if (!region) region = getRegionFromAwsConfig(getEnvValue("AWS_PROFILE") || "default") ?? void 0;
|
|
1673
1690
|
if (region) return `https://bedrock-runtime.${region}.amazonaws.com`;
|
|
1674
|
-
logger$
|
|
1691
|
+
logger$22.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.`);
|
|
1675
1692
|
return null;
|
|
1676
1693
|
}
|
|
1677
1694
|
if (isTruthyEnv(getEnvValue(VERTEX_USE_ENV))) {
|
|
@@ -1754,11 +1771,11 @@ const waitForPort = (port, timeoutMs = 5e3) => new Promise((resolve) => {
|
|
|
1754
1771
|
const registerGlobalProxyShutdown = () => {
|
|
1755
1772
|
if (!globalShutdownRegistered) {
|
|
1756
1773
|
process.on("exit", () => {
|
|
1757
|
-
logger$
|
|
1774
|
+
logger$22.debug("process.on(\"exit\") called - stopping all active proxies");
|
|
1758
1775
|
for (const proxyServer of activeProxyServers) try {
|
|
1759
1776
|
proxyServer.stopServer();
|
|
1760
1777
|
} catch (e) {
|
|
1761
|
-
logger$
|
|
1778
|
+
logger$22.debug(`Failed to stop proxy: ${errorMessage(e)}`);
|
|
1762
1779
|
}
|
|
1763
1780
|
activeProxyServers.clear();
|
|
1764
1781
|
});
|
|
@@ -1773,28 +1790,28 @@ const createProxyInstance = async ({ env, cwd, settingSources, settings, targetU
|
|
|
1773
1790
|
try {
|
|
1774
1791
|
const port = await findAvailablePort(DEFAULT_CC_PROXY_PORT, CC_PROXY_PORT_ATTEMPTS);
|
|
1775
1792
|
if (port === null) {
|
|
1776
|
-
logger$
|
|
1793
|
+
logger$22.warn("Unable to allocate port for cc-proxy.");
|
|
1777
1794
|
return null;
|
|
1778
1795
|
}
|
|
1779
1796
|
const targetUrl = resolvedTargetUrl ?? resolveTargetUrlFromEnv(env, void 0, cwd, settingSources, settings);
|
|
1780
1797
|
if (!targetUrl) {
|
|
1781
|
-
logger$
|
|
1798
|
+
logger$22.warn("Unable to resolve target URL for cc-proxy (provider misconfigured).");
|
|
1782
1799
|
return null;
|
|
1783
1800
|
}
|
|
1784
|
-
logger$
|
|
1801
|
+
logger$22.debug(`Creating proxy instance on port ${port} targeting: ${targetUrl}`);
|
|
1785
1802
|
try {
|
|
1786
1803
|
const { ProxyServer } = __require("@lmnr-ai/claude-code-proxy");
|
|
1787
1804
|
const proxyServer = new ProxyServer(port);
|
|
1788
1805
|
proxyServer.runServer(targetUrl);
|
|
1789
1806
|
if (!await waitForPort(port)) {
|
|
1790
|
-
logger$
|
|
1807
|
+
logger$22.warn(`cc-proxy failed to start on port ${port}`);
|
|
1791
1808
|
proxyServer.stopServer();
|
|
1792
1809
|
return null;
|
|
1793
1810
|
}
|
|
1794
1811
|
const proxyBaseUrl = `http://127.0.0.1:${port}`;
|
|
1795
1812
|
activeProxyServers.add(proxyServer);
|
|
1796
1813
|
registerGlobalProxyShutdown();
|
|
1797
|
-
logger$
|
|
1814
|
+
logger$22.info(`Started claude proxy server on: ${proxyBaseUrl}`);
|
|
1798
1815
|
return {
|
|
1799
1816
|
server: proxyServer,
|
|
1800
1817
|
baseUrl: proxyBaseUrl,
|
|
@@ -1802,11 +1819,11 @@ const createProxyInstance = async ({ env, cwd, settingSources, settings, targetU
|
|
|
1802
1819
|
targetUrl
|
|
1803
1820
|
};
|
|
1804
1821
|
} catch (e) {
|
|
1805
|
-
logger$
|
|
1822
|
+
logger$22.warn(`Unable to start cc-proxy: ${errorMessage(e)}`);
|
|
1806
1823
|
return null;
|
|
1807
1824
|
}
|
|
1808
1825
|
} catch (e) {
|
|
1809
|
-
logger$
|
|
1826
|
+
logger$22.warn(`Failed to create proxy instance: ${errorMessage(e)}`);
|
|
1810
1827
|
return null;
|
|
1811
1828
|
}
|
|
1812
1829
|
};
|
|
@@ -1816,11 +1833,11 @@ const createProxyInstance = async ({ env, cwd, settingSources, settings, targetU
|
|
|
1816
1833
|
const stopProxyInstance = (instance) => {
|
|
1817
1834
|
if (!instance) return;
|
|
1818
1835
|
try {
|
|
1819
|
-
logger$
|
|
1836
|
+
logger$22.debug(`Stopping proxy instance on port ${instance.port}`);
|
|
1820
1837
|
instance.server.stopServer();
|
|
1821
1838
|
activeProxyServers.delete(instance.server);
|
|
1822
1839
|
} catch (e) {
|
|
1823
|
-
logger$
|
|
1840
|
+
logger$22.debug(`Failed to stop proxy instance: ${errorMessage(e)}`);
|
|
1824
1841
|
}
|
|
1825
1842
|
};
|
|
1826
1843
|
/**
|
|
@@ -1828,11 +1845,11 @@ const stopProxyInstance = (instance) => {
|
|
|
1828
1845
|
* Used during shutdown to ensure cleanup
|
|
1829
1846
|
*/
|
|
1830
1847
|
const forceReleaseProxy = () => {
|
|
1831
|
-
logger$
|
|
1848
|
+
logger$22.debug(`Force stopping all ${activeProxyServers.size} active proxy servers`);
|
|
1832
1849
|
for (const proxyServer of activeProxyServers) try {
|
|
1833
1850
|
proxyServer.stopServer();
|
|
1834
1851
|
} catch (e) {
|
|
1835
|
-
logger$
|
|
1852
|
+
logger$22.debug(`Failed to stop proxy: ${errorMessage(e)}`);
|
|
1836
1853
|
}
|
|
1837
1854
|
activeProxyServers.clear();
|
|
1838
1855
|
};
|
|
@@ -1882,14 +1899,14 @@ const setTraceToProxyInstance = async (instance) => {
|
|
|
1882
1899
|
spanPath: payload.span_path,
|
|
1883
1900
|
laminarUrl: payload.laminar_url
|
|
1884
1901
|
});
|
|
1885
|
-
logger$
|
|
1902
|
+
logger$22.debug(`Set trace context to proxy on port ${instance.port}`);
|
|
1886
1903
|
} catch (e) {
|
|
1887
|
-
logger$
|
|
1904
|
+
logger$22.debug(`Unable to set trace context to proxy on port ${instance.port}: ` + errorMessage(e));
|
|
1888
1905
|
}
|
|
1889
1906
|
};
|
|
1890
1907
|
//#endregion
|
|
1891
1908
|
//#region src/opentelemetry-lib/instrumentation/claude-agent-sdk/index.ts
|
|
1892
|
-
const logger$
|
|
1909
|
+
const logger$21 = initializeLogger();
|
|
1893
1910
|
/**
|
|
1894
1911
|
* Create an instrumented version of the claude-agent-sdk query function.
|
|
1895
1912
|
* This can be used when importing the query function before Laminar initialization.
|
|
@@ -1925,9 +1942,9 @@ function instrumentClaudeAgentQuery(originalQuery) {
|
|
|
1925
1942
|
targetUrl
|
|
1926
1943
|
});
|
|
1927
1944
|
if (proxyInstance && targetUrl) {
|
|
1928
|
-
logger$
|
|
1945
|
+
logger$21.debug(`Using proxy on port ${proxyInstance.port}`);
|
|
1929
1946
|
await Laminar.withSpan(span, async () => {
|
|
1930
|
-
logger$
|
|
1947
|
+
logger$21.debug("Setting trace to proxy instance...");
|
|
1931
1948
|
await setTraceToProxyInstance(proxyInstance);
|
|
1932
1949
|
});
|
|
1933
1950
|
const varsToRemove = getEnvVarsToRemove(mergedEnv, sessionCwd, settingSources);
|
|
@@ -1942,13 +1959,13 @@ function instrumentClaudeAgentQuery(originalQuery) {
|
|
|
1942
1959
|
if (isProviderEnabledInEnv(subprocessEnv, "CLAUDE_CODE_USE_BEDROCK")) subprocessEnv.ANTHROPIC_BEDROCK_BASE_URL = proxyInstance.baseUrl;
|
|
1943
1960
|
if (isProviderEnabledInEnv(subprocessEnv, "CLAUDE_CODE_USE_VERTEX")) subprocessEnv.ANTHROPIC_VERTEX_BASE_URL = proxyInstance.baseUrl;
|
|
1944
1961
|
const flagSettings = buildProxyFlagSettings(callerSettings, proxyInstance.baseUrl, sessionCwd, settingSources);
|
|
1945
|
-
if (flagSettings === null) logger$
|
|
1962
|
+
if (flagSettings === null) logger$21.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.`);
|
|
1946
1963
|
else {
|
|
1947
1964
|
params.options.settings = flagSettings;
|
|
1948
|
-
const conflicting = Object.entries(readClaudeSettingsEnv(sessionCwd, settingSources)).filter(([key, value]) => PROXY_BASE_URL_ENV_KEYS.includes(key) && value !== "" && value !== proxyInstance
|
|
1949
|
-
if (conflicting.length > 0) logger$
|
|
1965
|
+
const conflicting = Object.entries(readClaudeSettingsEnv(sessionCwd, settingSources)).filter(([key, value]) => PROXY_BASE_URL_ENV_KEYS.includes(key) && value !== "" && value !== proxyInstance?.baseUrl).map(([key]) => key);
|
|
1966
|
+
if (conflicting.length > 0) logger$21.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.`);
|
|
1950
1967
|
}
|
|
1951
|
-
} else logger$
|
|
1968
|
+
} else logger$21.debug("No claude proxy server available. Proceeding without proxy.");
|
|
1952
1969
|
const originalGenerator = originalQuery(params);
|
|
1953
1970
|
for await (const message of originalGenerator) {
|
|
1954
1971
|
collected.push(message);
|
|
@@ -1977,7 +1994,7 @@ var ClaudeAgentSDKInstrumentation = class extends InstrumentationBase {
|
|
|
1977
1994
|
}
|
|
1978
1995
|
manuallyInstrument(claudeAgentModule) {
|
|
1979
1996
|
if (claudeAgentModule.query && typeof claudeAgentModule.query === "function") this._wrap(claudeAgentModule, "query", this.patchQuery());
|
|
1980
|
-
else logger$
|
|
1997
|
+
else logger$21.debug("query function not found in claudeAgentSDK module, skipping instrumentation");
|
|
1981
1998
|
}
|
|
1982
1999
|
patchQuery() {
|
|
1983
2000
|
return (original) => instrumentClaudeAgentQuery(original);
|
|
@@ -1995,7 +2012,7 @@ var ClaudeAgentSDKInstrumentation = class extends InstrumentationBase {
|
|
|
1995
2012
|
//#endregion
|
|
1996
2013
|
//#region src/laminar.ts
|
|
1997
2014
|
loadEnv();
|
|
1998
|
-
const logger$
|
|
2015
|
+
const logger$20 = initializeLogger();
|
|
1999
2016
|
var Laminar = class Laminar {
|
|
2000
2017
|
static {
|
|
2001
2018
|
this.isInitialized = false;
|
|
@@ -2047,6 +2064,13 @@ var Laminar = class Laminar {
|
|
|
2047
2064
|
* @param {string} props.logLevel - OTel log level. Defaults to "error".
|
|
2048
2065
|
* @param {number} props.maxExportBatchSize - Maximum number of spans to export in a single batch.
|
|
2049
2066
|
* Ignored when `disableBatch` is true.
|
|
2067
|
+
* @param {boolean} props.flushBySize - Whether to also flush batches by approximate payload
|
|
2068
|
+
* size, not just by span count and schedule delay. The batch is flushed when the next span
|
|
2069
|
+
* would push it past `maxExportBatchSizeBytes`, so a few large spans are exported without
|
|
2070
|
+
* waiting for `maxExportBatchSize` spans to accumulate. Useful when spans carry large prompts
|
|
2071
|
+
* or completions and exports get rejected for being too big. Defaults to false.
|
|
2072
|
+
* @param {number} props.maxExportBatchSizeBytes - Approximate maximum size, in bytes, of the
|
|
2073
|
+
* spans buffered in one batch. Only used when `flushBySize` is true. Defaults to 32 MiB.
|
|
2050
2074
|
* @param {boolean} props.forceHttp - Whether to force HTTP export. Not recommended.
|
|
2051
2075
|
* @param {SessionRecordingOptions} props.sessionRecordingOptions - Options for browser
|
|
2052
2076
|
* session recording.
|
|
@@ -2079,9 +2103,9 @@ var Laminar = class Laminar {
|
|
|
2079
2103
|
*
|
|
2080
2104
|
* @throws {Error} - If project API key is not set
|
|
2081
2105
|
*/
|
|
2082
|
-
static initialize({ projectApiKey, baseUrl, baseHttpUrl, httpPort, grpcPort, instrumentModules, disableBatch, traceExportTimeoutMillis, logLevel, maxExportBatchSize, forceHttp, sessionRecordingOptions, metadata, inheritGlobalContext, spanProcessor } = {}) {
|
|
2106
|
+
static initialize({ projectApiKey, baseUrl, baseHttpUrl, httpPort, grpcPort, instrumentModules, disableBatch, traceExportTimeoutMillis, logLevel, maxExportBatchSize, maxExportBatchSizeBytes, flushBySize, forceHttp, sessionRecordingOptions, metadata, inheritGlobalContext, spanProcessor } = {}) {
|
|
2083
2107
|
if (this.isInitialized) {
|
|
2084
|
-
logger$
|
|
2108
|
+
logger$20.warn("Laminar has already been initialized. Skipping initialization.");
|
|
2085
2109
|
return;
|
|
2086
2110
|
}
|
|
2087
2111
|
const key = projectApiKey ?? process?.env?.LMNR_PROJECT_API_KEY;
|
|
@@ -2105,7 +2129,7 @@ var Laminar = class Laminar {
|
|
|
2105
2129
|
...metadata ?? {}
|
|
2106
2130
|
};
|
|
2107
2131
|
if (inheritGlobalContext) LaminarContextManager.inheritGlobalContext = true;
|
|
2108
|
-
if (spanProcessor && !(spanProcessor instanceof LaminarSpanProcessor)) logger$
|
|
2132
|
+
if (spanProcessor && !(spanProcessor instanceof LaminarSpanProcessor)) logger$20.warn("Span processor is not a LaminarSpanProcessor. Some functionality may be impaired.");
|
|
2109
2133
|
this.isInitialized = true;
|
|
2110
2134
|
const urlWithoutSlash = url?.replace(/\/$/, "").replace(/:\d{1,5}$/g, "");
|
|
2111
2135
|
const httpUrlWithoutSlash = httpUrl?.replace(/\/$/, "").replace(/:\d{1,5}$/g, "");
|
|
@@ -2121,6 +2145,8 @@ var Laminar = class Laminar {
|
|
|
2121
2145
|
logLevel: logLevel ?? "error",
|
|
2122
2146
|
disableBatch,
|
|
2123
2147
|
maxExportBatchSize,
|
|
2148
|
+
maxExportBatchSizeBytes,
|
|
2149
|
+
flushBySize,
|
|
2124
2150
|
traceExportTimeoutMillis,
|
|
2125
2151
|
sessionRecordingOptions,
|
|
2126
2152
|
spanProcessor
|
|
@@ -2150,11 +2176,11 @@ var Laminar = class Laminar {
|
|
|
2150
2176
|
try {
|
|
2151
2177
|
getRuntime()?.recordTraceId(otelTraceIdToUUID(otelSpanContext.traceId));
|
|
2152
2178
|
} catch (e) {
|
|
2153
|
-
logger$
|
|
2179
|
+
logger$20.debug("Failed to record debug trace id from env: " + errorMessage(e));
|
|
2154
2180
|
}
|
|
2155
|
-
logger$
|
|
2181
|
+
logger$20.debug("Initialized Laminar parent context from LMNR_SPAN_CONTEXT.");
|
|
2156
2182
|
} catch (e) {
|
|
2157
|
-
logger$
|
|
2183
|
+
logger$20.warn("LMNR_SPAN_CONTEXT is set but could not be used: " + errorMessage(e));
|
|
2158
2184
|
}
|
|
2159
2185
|
}
|
|
2160
2186
|
/**
|
|
@@ -2198,14 +2224,14 @@ var Laminar = class Laminar {
|
|
|
2198
2224
|
if (projectId) {
|
|
2199
2225
|
runtime.recordProjectId(projectId);
|
|
2200
2226
|
const sessionUrl = runtime.debuggerSessionUrl();
|
|
2201
|
-
logger$
|
|
2227
|
+
logger$20.info(`Laminar debugger session: ${sessionUrl}`);
|
|
2202
2228
|
if (runtime.shouldOpenBrowser) spawn(process.platform === "win32" ? "start" : process.platform === "darwin" ? "open" : "xdg-open", [sessionUrl], {
|
|
2203
2229
|
detached: true,
|
|
2204
2230
|
stdio: "ignore"
|
|
2205
2231
|
}).unref();
|
|
2206
2232
|
}
|
|
2207
2233
|
}).catch((e) => {
|
|
2208
|
-
logger$
|
|
2234
|
+
logger$20.warn("Failed to register debug session: " + errorMessage(e));
|
|
2209
2235
|
});
|
|
2210
2236
|
this.globalMetadata = {
|
|
2211
2237
|
...this.globalMetadata,
|
|
@@ -2215,7 +2241,7 @@ var Laminar = class Laminar {
|
|
|
2215
2241
|
this.debugExitHook = () => runtime.emitPointer();
|
|
2216
2242
|
process.once("exit", this.debugExitHook);
|
|
2217
2243
|
} catch (e) {
|
|
2218
|
-
logger$
|
|
2244
|
+
logger$20.warn("Failed to initialize debug runtime: " + errorMessage(e));
|
|
2219
2245
|
}
|
|
2220
2246
|
}
|
|
2221
2247
|
/**
|
|
@@ -2260,7 +2286,7 @@ var Laminar = class Laminar {
|
|
|
2260
2286
|
runtime.rolloutSessions.register({ sessionId: runtime.sessionId }).then((projectId) => {
|
|
2261
2287
|
if (projectId) runtime.recordProjectId(projectId);
|
|
2262
2288
|
}).catch((e) => {
|
|
2263
|
-
logger$
|
|
2289
|
+
logger$20.debug("Failed to register downstream debug session: " + errorMessage(e));
|
|
2264
2290
|
});
|
|
2265
2291
|
this.globalMetadata = {
|
|
2266
2292
|
...this.globalMetadata,
|
|
@@ -2269,7 +2295,7 @@ var Laminar = class Laminar {
|
|
|
2269
2295
|
LaminarContextManager.setGlobalMetadata(this.globalMetadata);
|
|
2270
2296
|
}
|
|
2271
2297
|
} catch (e) {
|
|
2272
|
-
logger$
|
|
2298
|
+
logger$20.debug("Failed to arm debug runtime from context: " + errorMessage(e));
|
|
2273
2299
|
}
|
|
2274
2300
|
}
|
|
2275
2301
|
/**
|
|
@@ -2284,7 +2310,7 @@ var Laminar = class Laminar {
|
|
|
2284
2310
|
*/
|
|
2285
2311
|
static patch(modules) {
|
|
2286
2312
|
if (!this.isInitialized) {
|
|
2287
|
-
logger$
|
|
2313
|
+
logger$20.warn("Laminar must be initialized before patching modules. Skipping patch.");
|
|
2288
2314
|
return;
|
|
2289
2315
|
}
|
|
2290
2316
|
if (!modules || Object.keys(modules).length === 0) throw new Error("Pass at least one module to patch");
|
|
@@ -2544,7 +2570,7 @@ var Laminar = class Laminar {
|
|
|
2544
2570
|
const spanContext = tryToOtelSpanContext(laminarContext);
|
|
2545
2571
|
entityContext = trace.setSpan(entityContext, trace.wrapSpanContext(spanContext));
|
|
2546
2572
|
} catch (e) {
|
|
2547
|
-
logger$
|
|
2573
|
+
logger$20.warn("Failed to parse parent span context: " + errorMessage(e));
|
|
2548
2574
|
}
|
|
2549
2575
|
const tagProperties = tags ? { [`${ASSOCIATION_PROPERTIES}.tags`]: Array.from(new Set(tags)) } : {};
|
|
2550
2576
|
const ctxAssociationProperties = LaminarContextManager.getAssociationProperties();
|
|
@@ -2647,13 +2673,13 @@ var Laminar = class Laminar {
|
|
|
2647
2673
|
}
|
|
2648
2674
|
static async flush() {
|
|
2649
2675
|
if (this.isInitialized) {
|
|
2650
|
-
logger$
|
|
2676
|
+
logger$20.debug("Flushing spans");
|
|
2651
2677
|
await forceFlush();
|
|
2652
2678
|
}
|
|
2653
2679
|
}
|
|
2654
2680
|
static async shutdown() {
|
|
2655
2681
|
if (this.isInitialized) {
|
|
2656
|
-
logger$
|
|
2682
|
+
logger$20.debug("Shutting down Laminar");
|
|
2657
2683
|
getRuntime()?.emitPointer();
|
|
2658
2684
|
await forceFlush();
|
|
2659
2685
|
this.isInitialized = false;
|
|
@@ -2788,7 +2814,7 @@ return module.exports;
|
|
|
2788
2814
|
//#endregion
|
|
2789
2815
|
//#region src/browser/utils.ts
|
|
2790
2816
|
const LMNR_SEND_EVENTS_FUNCTION_NAME = "lmnrSendEvents";
|
|
2791
|
-
const logger$
|
|
2817
|
+
const logger$19 = initializeLogger();
|
|
2792
2818
|
/**
|
|
2793
2819
|
* If the first argument is a string, return an object with the name of the method
|
|
2794
2820
|
* and the argument as the value.
|
|
@@ -2811,7 +2837,7 @@ const takeFullSnapshot = async (page) => await page.evaluate(() => {
|
|
|
2811
2837
|
window.lmnrRrweb.record.takeFullSnapshot();
|
|
2812
2838
|
return true;
|
|
2813
2839
|
} catch (error) {
|
|
2814
|
-
logger$
|
|
2840
|
+
logger$19.error("Error taking full snapshot: " + errorMessage(error));
|
|
2815
2841
|
return false;
|
|
2816
2842
|
}
|
|
2817
2843
|
return false;
|
|
@@ -2822,7 +2848,7 @@ const injectSessionRecorder = async (page, sessionRecordingOptions) => {
|
|
|
2822
2848
|
try {
|
|
2823
2849
|
return await script();
|
|
2824
2850
|
} catch (error) {
|
|
2825
|
-
logger$
|
|
2851
|
+
logger$19.error("Operation " + script.name + " failed: " + errorMessage(error));
|
|
2826
2852
|
}
|
|
2827
2853
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
2828
2854
|
}
|
|
@@ -2832,20 +2858,20 @@ const injectSessionRecorder = async (page, sessionRecordingOptions) => {
|
|
|
2832
2858
|
try {
|
|
2833
2859
|
isRrwebPresent = await castedPage.evaluate(() => typeof window.lmnrRrweb !== "undefined");
|
|
2834
2860
|
} catch (error) {
|
|
2835
|
-
logger$
|
|
2861
|
+
logger$19.debug("Failed to check if session recorder is loaded: " + errorMessage(error));
|
|
2836
2862
|
}
|
|
2837
2863
|
if (!isRrwebPresent) {
|
|
2838
2864
|
if (!await tryRunScript(async function injectSessionRecorder() {
|
|
2839
2865
|
await castedPage.evaluate(RECORDER);
|
|
2840
2866
|
return true;
|
|
2841
2867
|
})) {
|
|
2842
|
-
logger$
|
|
2868
|
+
logger$19.error("Failed to load session recorder");
|
|
2843
2869
|
return;
|
|
2844
2870
|
}
|
|
2845
2871
|
try {
|
|
2846
2872
|
await castedPage.evaluate(injectScript, sessionRecordingOptions);
|
|
2847
2873
|
} catch (error) {
|
|
2848
|
-
logger$
|
|
2874
|
+
logger$19.debug("Failed to inject session recorder: " + errorMessage(error));
|
|
2849
2875
|
}
|
|
2850
2876
|
}
|
|
2851
2877
|
};
|
|
@@ -2934,9 +2960,9 @@ const modelToProviderMap = {
|
|
|
2934
2960
|
"gpt-4.1-mini": "openai",
|
|
2935
2961
|
"gpt-4.1-nano": "openai",
|
|
2936
2962
|
"o4-mini": "openai",
|
|
2937
|
-
|
|
2963
|
+
o3: "openai",
|
|
2938
2964
|
"o3-mini": "openai",
|
|
2939
|
-
|
|
2965
|
+
o1: "openai",
|
|
2940
2966
|
"o1-mini": "openai",
|
|
2941
2967
|
"gpt-4o": "openai",
|
|
2942
2968
|
"gpt-4o-mini": "openai",
|
|
@@ -3108,7 +3134,7 @@ async function sendEvents(chunk, client, chunkBuffers, sessionId, traceId) {
|
|
|
3108
3134
|
try {
|
|
3109
3135
|
const { batchId, chunkIndex, totalChunks, data } = chunk;
|
|
3110
3136
|
if (!sessionId || !traceId) {
|
|
3111
|
-
logger$
|
|
3137
|
+
logger$19.debug("Missing sessionId or traceId in chunk");
|
|
3112
3138
|
return;
|
|
3113
3139
|
}
|
|
3114
3140
|
if (!chunkBuffers.has(batchId)) chunkBuffers.set(batchId, {
|
|
@@ -3127,7 +3153,7 @@ async function sendEvents(chunk, client, chunkBuffers, sessionId, traceId) {
|
|
|
3127
3153
|
traceId,
|
|
3128
3154
|
events
|
|
3129
3155
|
}).catch((error) => {
|
|
3130
|
-
logger$
|
|
3156
|
+
logger$19.debug("Failed to send events: " + errorMessage(error));
|
|
3131
3157
|
});
|
|
3132
3158
|
chunkBuffers.delete(batchId);
|
|
3133
3159
|
}
|
|
@@ -3135,16 +3161,16 @@ async function sendEvents(chunk, client, chunkBuffers, sessionId, traceId) {
|
|
|
3135
3161
|
const toDelete = [];
|
|
3136
3162
|
for (const [bid, buffer] of chunkBuffers.entries()) if (currentTime - buffer.timestamp > OLD_BUFFER_TIMEOUT) toDelete.push(bid);
|
|
3137
3163
|
for (const bid of toDelete) {
|
|
3138
|
-
logger$
|
|
3164
|
+
logger$19.debug(`Cleaning up incomplete chunk buffer: ${bid}`);
|
|
3139
3165
|
chunkBuffers.delete(bid);
|
|
3140
3166
|
}
|
|
3141
3167
|
} catch (error) {
|
|
3142
|
-
logger$
|
|
3168
|
+
logger$19.debug("Could not send events: " + errorMessage(error));
|
|
3143
3169
|
}
|
|
3144
3170
|
}
|
|
3145
3171
|
//#endregion
|
|
3146
3172
|
//#region src/browser/playwright.ts
|
|
3147
|
-
const logger$
|
|
3173
|
+
const logger$18 = initializeLogger();
|
|
3148
3174
|
var PlaywrightInstrumentation = class extends InstrumentationBase {
|
|
3149
3175
|
constructor(client, sessionRecordingOptions) {
|
|
3150
3176
|
super("@lmnr/playwright-instrumentation", version, { enabled: true });
|
|
@@ -3252,7 +3278,7 @@ var PlaywrightInstrumentation = class extends InstrumentationBase {
|
|
|
3252
3278
|
try {
|
|
3253
3279
|
await injectSessionRecorder(page, this._sessionRecordingOptions);
|
|
3254
3280
|
} catch (error) {
|
|
3255
|
-
logger$
|
|
3281
|
+
logger$18.error("Error in onLoad handler: " + errorMessage(error));
|
|
3256
3282
|
}
|
|
3257
3283
|
});
|
|
3258
3284
|
const chunkBuffers = /* @__PURE__ */ new Map();
|
|
@@ -3262,7 +3288,7 @@ var PlaywrightInstrumentation = class extends InstrumentationBase {
|
|
|
3262
3288
|
await sendEvents(chunk, this._client, chunkBuffers, sessionId, traceId);
|
|
3263
3289
|
});
|
|
3264
3290
|
} catch (error) {
|
|
3265
|
-
logger$
|
|
3291
|
+
logger$18.debug("Could not expose function lmnrSendEvents: " + errorMessage(error));
|
|
3266
3292
|
}
|
|
3267
3293
|
}
|
|
3268
3294
|
};
|
|
@@ -3687,7 +3713,7 @@ var StagehandV2Instrumentation = class extends InstrumentationBase {
|
|
|
3687
3713
|
};
|
|
3688
3714
|
//#endregion
|
|
3689
3715
|
//#region src/browser/puppeteer.ts
|
|
3690
|
-
const logger$
|
|
3716
|
+
const logger$17 = initializeLogger();
|
|
3691
3717
|
var PuppeteerInstrumentation = class extends InstrumentationBase {
|
|
3692
3718
|
constructor(client, sessionRecordingOptions) {
|
|
3693
3719
|
super("@lmnr/puppeteer-instrumentation", version, { enabled: true });
|
|
@@ -3732,10 +3758,10 @@ var PuppeteerInstrumentation = class extends InstrumentationBase {
|
|
|
3732
3758
|
context.on("targetcreated", (target) => {
|
|
3733
3759
|
target.page().then((page) => {
|
|
3734
3760
|
if (page) plugin.patchPage(page, sessionId).catch((error) => {
|
|
3735
|
-
logger$
|
|
3761
|
+
logger$17.error("Failed to patch page: " + errorMessage(error));
|
|
3736
3762
|
});
|
|
3737
3763
|
}).catch((error) => {
|
|
3738
|
-
logger$
|
|
3764
|
+
logger$17.error("Failed to patch page: " + errorMessage(error));
|
|
3739
3765
|
});
|
|
3740
3766
|
});
|
|
3741
3767
|
await Promise.all((await context.pages()).map((page) => plugin.patchPage(page, sessionId)));
|
|
@@ -3753,19 +3779,19 @@ var PuppeteerInstrumentation = class extends InstrumentationBase {
|
|
|
3753
3779
|
context.on("targetcreated", (target) => {
|
|
3754
3780
|
target.page().then((page) => {
|
|
3755
3781
|
if (page) plugin.patchPage(page, sessionId).catch((error) => {
|
|
3756
|
-
logger$
|
|
3782
|
+
logger$17.error("Failed to patch page: " + errorMessage(error));
|
|
3757
3783
|
});
|
|
3758
3784
|
}).catch((error) => {
|
|
3759
|
-
logger$
|
|
3785
|
+
logger$17.error("Failed to patch page: " + errorMessage(error));
|
|
3760
3786
|
});
|
|
3761
3787
|
});
|
|
3762
3788
|
context.on("targetchanged", (target) => {
|
|
3763
3789
|
target.page().then((page) => {
|
|
3764
3790
|
if (page) plugin.patchPage(page, sessionId).catch((error) => {
|
|
3765
|
-
logger$
|
|
3791
|
+
logger$17.error("Failed to patch page: " + errorMessage(error));
|
|
3766
3792
|
});
|
|
3767
3793
|
}).catch((error) => {
|
|
3768
|
-
logger$
|
|
3794
|
+
logger$17.error("Failed to patch page: " + errorMessage(error));
|
|
3769
3795
|
});
|
|
3770
3796
|
});
|
|
3771
3797
|
for (const page of await context.pages()) await plugin.patchPage(page, sessionId);
|
|
@@ -3789,7 +3815,7 @@ var PuppeteerInstrumentation = class extends InstrumentationBase {
|
|
|
3789
3815
|
const traceId = otelTraceId ? otelTraceIdToUUID(otelTraceId) : NIL_UUID;
|
|
3790
3816
|
page.on("domcontentloaded", () => {
|
|
3791
3817
|
injectSessionRecorder(page, this._sessionRecordingOptions).catch((error) => {
|
|
3792
|
-
logger$
|
|
3818
|
+
logger$17.error("Error in onLoad handler: " + errorMessage(error));
|
|
3793
3819
|
});
|
|
3794
3820
|
});
|
|
3795
3821
|
await injectSessionRecorder(page, this._sessionRecordingOptions);
|
|
@@ -3799,13 +3825,13 @@ var PuppeteerInstrumentation = class extends InstrumentationBase {
|
|
|
3799
3825
|
await sendEvents(chunk, this._client, chunkBuffers, sessionId, traceId);
|
|
3800
3826
|
});
|
|
3801
3827
|
} catch (error) {
|
|
3802
|
-
logger$
|
|
3828
|
+
logger$17.debug("Could not expose function lmnrSendEvents: " + errorMessage(error));
|
|
3803
3829
|
}
|
|
3804
3830
|
}
|
|
3805
3831
|
};
|
|
3806
3832
|
//#endregion
|
|
3807
3833
|
//#region src/browser/stagehand/v3/constants.ts
|
|
3808
|
-
const logger$
|
|
3834
|
+
const logger$16 = initializeLogger();
|
|
3809
3835
|
const CDP_OPERATION_TIMEOUT_MS = 1e4;
|
|
3810
3836
|
const SKIP_URL_PATTERNS = [
|
|
3811
3837
|
"about:blank",
|
|
@@ -3856,7 +3882,7 @@ async function getOrCreateIsolatedWorld(page) {
|
|
|
3856
3882
|
frameId
|
|
3857
3883
|
};
|
|
3858
3884
|
} catch (error) {
|
|
3859
|
-
logger$
|
|
3885
|
+
logger$16.debug("Failed to create isolated world: " + errorMessage(error));
|
|
3860
3886
|
return null;
|
|
3861
3887
|
}
|
|
3862
3888
|
}
|
|
@@ -3873,7 +3899,7 @@ async function isRecorderPresent(page, contextId) {
|
|
|
3873
3899
|
if (!result) return true;
|
|
3874
3900
|
return result.result?.value === true;
|
|
3875
3901
|
} catch (error) {
|
|
3876
|
-
logger$
|
|
3902
|
+
logger$16.debug("Failed to check if recorder is present: " + errorMessage(error));
|
|
3877
3903
|
return true;
|
|
3878
3904
|
}
|
|
3879
3905
|
}
|
|
@@ -3886,21 +3912,21 @@ async function injectRecorderViaCDP(page, state, conn, sessionRecordingOptions,
|
|
|
3886
3912
|
try {
|
|
3887
3913
|
url = page.url();
|
|
3888
3914
|
} catch (error) {
|
|
3889
|
-
logger$
|
|
3915
|
+
logger$16.debug("Failed to get page URL, page might be closed: " + errorMessage(error));
|
|
3890
3916
|
return null;
|
|
3891
3917
|
}
|
|
3892
3918
|
if (shouldSkipUrl(url)) {
|
|
3893
|
-
logger$
|
|
3919
|
+
logger$16.debug(`Skipping recorder injection for URL: ${url}`);
|
|
3894
3920
|
return null;
|
|
3895
3921
|
}
|
|
3896
3922
|
const worldResult = await getOrCreateIsolatedWorld(page);
|
|
3897
3923
|
if (!worldResult) {
|
|
3898
|
-
logger$
|
|
3924
|
+
logger$16.debug("Failed to get isolated world for page");
|
|
3899
3925
|
return null;
|
|
3900
3926
|
}
|
|
3901
3927
|
const { contextId, frameId } = worldResult;
|
|
3902
3928
|
if (await isRecorderPresent(page, contextId)) {
|
|
3903
|
-
logger$
|
|
3929
|
+
logger$16.debug("Recorder already present, skipping injection");
|
|
3904
3930
|
state.contextIdToSession.set(contextId, {
|
|
3905
3931
|
sessionId: state.sessionId,
|
|
3906
3932
|
traceId: state.traceId
|
|
@@ -3913,7 +3939,7 @@ async function injectRecorderViaCDP(page, state, conn, sessionRecordingOptions,
|
|
|
3913
3939
|
contextId
|
|
3914
3940
|
});
|
|
3915
3941
|
} catch (error) {
|
|
3916
|
-
logger$
|
|
3942
|
+
logger$16.debug("Failed to inject rrweb: " + errorMessage(error));
|
|
3917
3943
|
return null;
|
|
3918
3944
|
}
|
|
3919
3945
|
const optionsJson = JSON.stringify(sessionRecordingOptions ?? {});
|
|
@@ -3924,14 +3950,14 @@ async function injectRecorderViaCDP(page, state, conn, sessionRecordingOptions,
|
|
|
3924
3950
|
contextId
|
|
3925
3951
|
});
|
|
3926
3952
|
} catch (error) {
|
|
3927
|
-
logger$
|
|
3953
|
+
logger$16.debug("Failed to inject recording setup: " + errorMessage(error));
|
|
3928
3954
|
return null;
|
|
3929
3955
|
}
|
|
3930
3956
|
try {
|
|
3931
3957
|
await sendCDP(page, "Runtime.addBinding", { name: "lmnrSendEvents" });
|
|
3932
|
-
logger$
|
|
3958
|
+
logger$16.debug(`Added binding 'lmnrSendEvents' for page ${frameId}, context ${contextId}`);
|
|
3933
3959
|
} catch (error) {
|
|
3934
|
-
logger$
|
|
3960
|
+
logger$16.debug("Binding may already exist: " + errorMessage(error));
|
|
3935
3961
|
}
|
|
3936
3962
|
if (bindingHandler) try {
|
|
3937
3963
|
const pageTargetId = page.targetId();
|
|
@@ -3941,26 +3967,26 @@ async function injectRecorderViaCDP(page, state, conn, sessionRecordingOptions,
|
|
|
3941
3967
|
const sessionTyped = session;
|
|
3942
3968
|
if (sessionTyped.on) {
|
|
3943
3969
|
sessionTyped.on("Runtime.bindingCalled", bindingHandler);
|
|
3944
|
-
logger$
|
|
3970
|
+
logger$16.debug(`Set up binding handler on session ${sessionId}`);
|
|
3945
3971
|
}
|
|
3946
3972
|
} catch (error) {
|
|
3947
|
-
logger$
|
|
3973
|
+
logger$16.debug(`Failed to set up binding handler on session ${sessionId}: ` + errorMessage(error));
|
|
3948
3974
|
}
|
|
3949
3975
|
state.pageSessionHandlers.set(pageTargetId, bindingHandler);
|
|
3950
3976
|
}
|
|
3951
3977
|
} catch (error) {
|
|
3952
|
-
logger$
|
|
3978
|
+
logger$16.debug("Failed to set up binding handler: " + errorMessage(error));
|
|
3953
3979
|
}
|
|
3954
3980
|
state.contextIdToSession.set(contextId, {
|
|
3955
3981
|
sessionId: state.sessionId,
|
|
3956
3982
|
traceId: state.traceId
|
|
3957
3983
|
});
|
|
3958
|
-
logger$
|
|
3984
|
+
logger$16.debug(`Registered context mapping: contextId=${contextId}, sessionId=${state.sessionId}`);
|
|
3959
3985
|
state.instrumentedPageIds.add(frameId);
|
|
3960
|
-
logger$
|
|
3986
|
+
logger$16.debug(`Successfully injected recorder into page ${frameId}`);
|
|
3961
3987
|
return contextId;
|
|
3962
3988
|
} catch (error) {
|
|
3963
|
-
logger$
|
|
3989
|
+
logger$16.debug("Error injecting recorder: " + errorMessage(error));
|
|
3964
3990
|
return null;
|
|
3965
3991
|
}
|
|
3966
3992
|
}
|
|
@@ -3974,13 +4000,13 @@ function createBindingHandler(state) {
|
|
|
3974
4000
|
if (event.name !== "lmnrSendEvents") return;
|
|
3975
4001
|
const sessionInfo = state.contextIdToSession.get(event.executionContextId);
|
|
3976
4002
|
if (!sessionInfo) {
|
|
3977
|
-
logger$
|
|
4003
|
+
logger$16.debug("No session info found for context ID: " + event.executionContextId);
|
|
3978
4004
|
if (state.contextIdToSession.size > 0) {
|
|
3979
4005
|
const [, fallbackInfo] = state.contextIdToSession.entries().next().value;
|
|
3980
4006
|
if (fallbackInfo) try {
|
|
3981
4007
|
sendEvents(JSON.parse(event.payload), state.client, state.chunkBuffers, fallbackInfo.sessionId, fallbackInfo.traceId);
|
|
3982
4008
|
} catch (error) {
|
|
3983
|
-
logger$
|
|
4009
|
+
logger$16.debug("Failed to parse binding payload: " + errorMessage(error));
|
|
3984
4010
|
}
|
|
3985
4011
|
}
|
|
3986
4012
|
return;
|
|
@@ -3988,7 +4014,7 @@ function createBindingHandler(state) {
|
|
|
3988
4014
|
try {
|
|
3989
4015
|
sendEvents(JSON.parse(event.payload), state.client, state.chunkBuffers, sessionInfo.sessionId, sessionInfo.traceId);
|
|
3990
4016
|
} catch (error) {
|
|
3991
|
-
logger$
|
|
4017
|
+
logger$16.debug("Failed to handle binding event: " + errorMessage(error));
|
|
3992
4018
|
}
|
|
3993
4019
|
};
|
|
3994
4020
|
}
|
|
@@ -4004,10 +4030,10 @@ function createTargetCreatedHandler(context, state, sessionRecordingOptions) {
|
|
|
4004
4030
|
const pageId = page.mainFrameId();
|
|
4005
4031
|
if (!state.instrumentedPageIds.has(pageId)) await injectRecorderViaCDP(page, state, context.conn, sessionRecordingOptions, state.bindingHandler ?? void 0);
|
|
4006
4032
|
} catch (error) {
|
|
4007
|
-
logger$
|
|
4033
|
+
logger$16.debug("Error injecting recorder into new page: " + errorMessage(error));
|
|
4008
4034
|
}
|
|
4009
4035
|
} catch (error) {
|
|
4010
|
-
logger$
|
|
4036
|
+
logger$16.debug("Error handling target created event: " + errorMessage(error));
|
|
4011
4037
|
}
|
|
4012
4038
|
};
|
|
4013
4039
|
}
|
|
@@ -4019,7 +4045,7 @@ function createTargetInfoChangedHandler(context, state, sessionRecordingOptions)
|
|
|
4019
4045
|
if (event.targetInfo.type !== "page") return;
|
|
4020
4046
|
const url = event.targetInfo.url;
|
|
4021
4047
|
if (shouldSkipUrl(url)) return;
|
|
4022
|
-
logger$
|
|
4048
|
+
logger$16.debug(`Target URL changed to ${url}, checking if recorder injection needed`);
|
|
4023
4049
|
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
4024
4050
|
try {
|
|
4025
4051
|
for (const page of context.pages()) if (page.targetId() === event.targetInfo.targetId) {
|
|
@@ -4027,7 +4053,7 @@ function createTargetInfoChangedHandler(context, state, sessionRecordingOptions)
|
|
|
4027
4053
|
break;
|
|
4028
4054
|
}
|
|
4029
4055
|
} catch (error) {
|
|
4030
|
-
logger$
|
|
4056
|
+
logger$16.debug("Error handling target info change: " + errorMessage(error));
|
|
4031
4057
|
}
|
|
4032
4058
|
};
|
|
4033
4059
|
}
|
|
@@ -4188,12 +4214,12 @@ var StagehandInstrumentation = class extends InstrumentationBase {
|
|
|
4188
4214
|
*/
|
|
4189
4215
|
async setupSessionRecording(stagehandInstance, sessionId, traceId) {
|
|
4190
4216
|
if (!this._client) {
|
|
4191
|
-
logger$
|
|
4217
|
+
logger$16.debug("No Laminar client available, skipping session recording setup");
|
|
4192
4218
|
return;
|
|
4193
4219
|
}
|
|
4194
4220
|
const context = stagehandInstance.context;
|
|
4195
4221
|
if (!context || !context.conn) {
|
|
4196
|
-
logger$
|
|
4222
|
+
logger$16.debug("No context or connection available, skipping session recording setup");
|
|
4197
4223
|
return;
|
|
4198
4224
|
}
|
|
4199
4225
|
const recorderState = {
|
|
@@ -4223,11 +4249,11 @@ var StagehandInstrumentation = class extends InstrumentationBase {
|
|
|
4223
4249
|
for (const page of pages) try {
|
|
4224
4250
|
if (await injectRecorderViaCDP(page, recorderState, context.conn, this._sessionRecordingOptions, bindingHandler) !== null) injectedCount++;
|
|
4225
4251
|
} catch (error) {
|
|
4226
|
-
logger$
|
|
4252
|
+
logger$16.debug("Error injecting recorder into page: " + errorMessage(error));
|
|
4227
4253
|
}
|
|
4228
|
-
logger$
|
|
4254
|
+
logger$16.debug(`Injected session recorder into ${injectedCount} of ${pages.length} existing page(s)`);
|
|
4229
4255
|
} catch (error) {
|
|
4230
|
-
logger$
|
|
4256
|
+
logger$16.debug("Error accessing pages for recorder injection: " + errorMessage(error));
|
|
4231
4257
|
}
|
|
4232
4258
|
}
|
|
4233
4259
|
patchStagehandClose() {
|
|
@@ -4257,12 +4283,12 @@ var StagehandInstrumentation = class extends InstrumentationBase {
|
|
|
4257
4283
|
if (recorderState.targetCreatedHandler) try {
|
|
4258
4284
|
context.conn.off("Target.targetCreated", recorderState.targetCreatedHandler);
|
|
4259
4285
|
} catch (error) {
|
|
4260
|
-
logger$
|
|
4286
|
+
logger$16.debug("Error removing target created handler: " + errorMessage(error));
|
|
4261
4287
|
}
|
|
4262
4288
|
if (recorderState.targetInfoChangedHandler) try {
|
|
4263
4289
|
context.conn.off("Target.targetInfoChanged", recorderState.targetInfoChangedHandler);
|
|
4264
4290
|
} catch (error) {
|
|
4265
|
-
logger$
|
|
4291
|
+
logger$16.debug("Error removing target info changed handler: " + errorMessage(error));
|
|
4266
4292
|
}
|
|
4267
4293
|
}
|
|
4268
4294
|
recorderState.contextIdToSession.clear();
|
|
@@ -4273,7 +4299,7 @@ var StagehandInstrumentation = class extends InstrumentationBase {
|
|
|
4273
4299
|
recorderState.targetCreatedHandler = null;
|
|
4274
4300
|
recorderState.targetInfoChangedHandler = null;
|
|
4275
4301
|
this.stagehandInstanceToRecorderState.delete(stagehandInstance);
|
|
4276
|
-
logger$
|
|
4302
|
+
logger$16.debug("Cleaned up session recording for Stagehand instance");
|
|
4277
4303
|
}
|
|
4278
4304
|
patchStagehandGlobalMethod(methodName, sessionId, parentSpan) {
|
|
4279
4305
|
return (original) => async function method(...args) {
|
|
@@ -4498,12 +4524,11 @@ var StagehandInstrumentation = class extends InstrumentationBase {
|
|
|
4498
4524
|
* LLM-backed calls and surface token usage from the response where available.
|
|
4499
4525
|
*/
|
|
4500
4526
|
patchStagehandApiClient(apiClient, parentSpan, stagehandInstance) {
|
|
4501
|
-
const instrumentation = this;
|
|
4502
4527
|
let baseProvider = stagehandInstance?.llmClient?.type;
|
|
4503
4528
|
const baseModel = stagehandInstance?.llmClient?.modelName;
|
|
4504
4529
|
if (baseModel && !baseProvider) baseProvider = modelToProvider(baseModel) ?? "aisdk";
|
|
4505
4530
|
const wrapApiMethod = (methodName) => {
|
|
4506
|
-
safeWrap(
|
|
4531
|
+
safeWrap(this, apiClient, methodName, (original) => async function method(...args) {
|
|
4507
4532
|
const currentSpan = trace.getSpan(LaminarContextManager.getContext()) ?? trace.getActiveSpan();
|
|
4508
4533
|
const input = nameArgsOrCopy(args);
|
|
4509
4534
|
const spanName = `stagehand.apiClient.${methodName}`;
|
|
@@ -4916,7 +4941,6 @@ const WRAPPED_BROWSER_METHODS = [
|
|
|
4916
4941
|
"create",
|
|
4917
4942
|
"retrieve",
|
|
4918
4943
|
"list",
|
|
4919
|
-
"delete",
|
|
4920
4944
|
"deleteByID",
|
|
4921
4945
|
"loadExtensions"
|
|
4922
4946
|
];
|
|
@@ -5144,7 +5168,7 @@ const normalizeMessages = (data, role = "user") => {
|
|
|
5144
5168
|
};
|
|
5145
5169
|
//#endregion
|
|
5146
5170
|
//#region src/opentelemetry-lib/instrumentation/openai-agents/messages.ts
|
|
5147
|
-
const logger$
|
|
5171
|
+
const logger$15 = initializeLogger();
|
|
5148
5172
|
const setLmnrSpanIo = (lmnrSpan, inputData, outputData) => {
|
|
5149
5173
|
if (inputData !== void 0 && inputData !== null) lmnrSpan.setAttribute(SPAN_INPUT, JSON.stringify(inputData));
|
|
5150
5174
|
if (outputData !== void 0 && outputData !== null) lmnrSpan.setAttribute(SPAN_OUTPUT, JSON.stringify(outputData));
|
|
@@ -5173,7 +5197,7 @@ const setGenAiOutputMessagesFromResponse = (lmnrSpan, response) => {
|
|
|
5173
5197
|
if (!outputItems) return;
|
|
5174
5198
|
const id = response.id;
|
|
5175
5199
|
if (!Array.isArray(outputItems)) {
|
|
5176
|
-
logger$
|
|
5200
|
+
logger$15.debug("Laminar OpenAI agents instrumentation, failed to parse output items. Expected array");
|
|
5177
5201
|
outputItems = [];
|
|
5178
5202
|
}
|
|
5179
5203
|
const result = {
|
|
@@ -5368,7 +5392,7 @@ const applySpeechGroupSpanData = (lmnrSpan, spanData) => {
|
|
|
5368
5392
|
};
|
|
5369
5393
|
//#endregion
|
|
5370
5394
|
//#region src/opentelemetry-lib/instrumentation/openai-agents/processor.ts
|
|
5371
|
-
const logger$
|
|
5395
|
+
const logger$14 = initializeLogger();
|
|
5372
5396
|
var LaminarAgentsTraceProcessor = class {
|
|
5373
5397
|
constructor() {
|
|
5374
5398
|
this.traces = /* @__PURE__ */ new Map();
|
|
@@ -5386,7 +5410,7 @@ var LaminarAgentsTraceProcessor = class {
|
|
|
5386
5410
|
} catch {}
|
|
5387
5411
|
this.applyTraceMetadata(state.rootSpan, agentsTrace);
|
|
5388
5412
|
} catch (e) {
|
|
5389
|
-
logger$
|
|
5413
|
+
logger$14.debug(`Error in onTraceStart: ${String(e)}`);
|
|
5390
5414
|
}
|
|
5391
5415
|
}
|
|
5392
5416
|
async onTraceEnd(agentsTrace) {
|
|
@@ -5435,7 +5459,7 @@ var LaminarAgentsTraceProcessor = class {
|
|
|
5435
5459
|
});
|
|
5436
5460
|
const key = span.spanId;
|
|
5437
5461
|
if (!key) {
|
|
5438
|
-
logger$
|
|
5462
|
+
logger$14.debug("Span missing spanId, cannot track");
|
|
5439
5463
|
try {
|
|
5440
5464
|
lmnrSpan.end();
|
|
5441
5465
|
} catch {}
|
|
@@ -5454,7 +5478,7 @@ var LaminarAgentsTraceProcessor = class {
|
|
|
5454
5478
|
activationContext
|
|
5455
5479
|
});
|
|
5456
5480
|
} catch (e) {
|
|
5457
|
-
logger$
|
|
5481
|
+
logger$14.debug(`Error in onSpanStart: ${String(e)}`);
|
|
5458
5482
|
if (lmnrSpan !== void 0) try {
|
|
5459
5483
|
lmnrSpan.end();
|
|
5460
5484
|
} catch {}
|
|
@@ -5572,7 +5596,7 @@ var LaminarAgentsTraceProcessor = class {
|
|
|
5572
5596
|
};
|
|
5573
5597
|
//#endregion
|
|
5574
5598
|
//#region src/opentelemetry-lib/instrumentation/openai-agents/index.ts
|
|
5575
|
-
const logger$
|
|
5599
|
+
const logger$13 = initializeLogger();
|
|
5576
5600
|
const wrapGetResponse = (original) => function(request, ...rest) {
|
|
5577
5601
|
const systemInstructions = request?.systemInstructions;
|
|
5578
5602
|
return runWithSystemInstructions(systemInstructions, () => original.call(this, request, ...rest));
|
|
@@ -5592,7 +5616,7 @@ var OpenAIAgentsInstrumentation = class extends InstrumentationBase {
|
|
|
5592
5616
|
}
|
|
5593
5617
|
manuallyInstrument(agentsModule) {
|
|
5594
5618
|
if (!agentsModule) {
|
|
5595
|
-
logger$
|
|
5619
|
+
logger$13.debug("@openai/agents module not provided, skipping");
|
|
5596
5620
|
return;
|
|
5597
5621
|
}
|
|
5598
5622
|
this.registerProcessor(agentsModule);
|
|
@@ -5602,7 +5626,7 @@ var OpenAIAgentsInstrumentation = class extends InstrumentationBase {
|
|
|
5602
5626
|
if (this._processor !== void 0) return;
|
|
5603
5627
|
const addTraceProcessor = module.addTraceProcessor;
|
|
5604
5628
|
if (typeof addTraceProcessor !== "function") {
|
|
5605
|
-
logger$
|
|
5629
|
+
logger$13.debug("addTraceProcessor not found in @openai/agents module");
|
|
5606
5630
|
return;
|
|
5607
5631
|
}
|
|
5608
5632
|
try {
|
|
@@ -5610,7 +5634,7 @@ var OpenAIAgentsInstrumentation = class extends InstrumentationBase {
|
|
|
5610
5634
|
addTraceProcessor(processor);
|
|
5611
5635
|
this._processor = processor;
|
|
5612
5636
|
} catch (e) {
|
|
5613
|
-
logger$
|
|
5637
|
+
logger$13.debug(`Failed to register Laminar Agents processor: ${String(e)}`);
|
|
5614
5638
|
}
|
|
5615
5639
|
}
|
|
5616
5640
|
patchModel(module) {
|
|
@@ -5623,7 +5647,7 @@ var OpenAIAgentsInstrumentation = class extends InstrumentationBase {
|
|
|
5623
5647
|
try {
|
|
5624
5648
|
this._wrap(cls.prototype, method, wrapper);
|
|
5625
5649
|
} catch (e) {
|
|
5626
|
-
logger$
|
|
5650
|
+
logger$13.debug(`Failed to wrap ${method}: ${String(e)}`);
|
|
5627
5651
|
}
|
|
5628
5652
|
}
|
|
5629
5653
|
this._wrappedPrototypes.add(cls.prototype);
|
|
@@ -5728,6 +5752,113 @@ const getLangVersion = () => {
|
|
|
5728
5752
|
if (process?.versions?.node) return `node@${process.versions.node}`;
|
|
5729
5753
|
};
|
|
5730
5754
|
//#endregion
|
|
5755
|
+
//#region src/opentelemetry-lib/tracing/batch-processor.ts
|
|
5756
|
+
const logger$12 = initializeLogger();
|
|
5757
|
+
/**
|
|
5758
|
+
* Non-string attribute values (numbers, booleans) are counted as a flat 8 bytes
|
|
5759
|
+
* instead of being measured — they never dominate a GenAI payload.
|
|
5760
|
+
*/
|
|
5761
|
+
const FIXED_ATTRIBUTE_VALUE_SIZE = 8;
|
|
5762
|
+
const valueSize = (value) => {
|
|
5763
|
+
if (typeof value === "string") return utf8Size(value);
|
|
5764
|
+
if (Array.isArray(value)) return value.reduce((total, item) => total + valueSize(item), 0);
|
|
5765
|
+
return FIXED_ATTRIBUTE_VALUE_SIZE;
|
|
5766
|
+
};
|
|
5767
|
+
const attributesSize = (attributes) => {
|
|
5768
|
+
if (!attributes) return 0;
|
|
5769
|
+
return Object.entries(attributes).reduce((total, [key, value]) => total + utf8Size(key) + valueSize(value), 0);
|
|
5770
|
+
};
|
|
5771
|
+
/**
|
|
5772
|
+
* Byte length of `value` encoded as UTF-8.
|
|
5773
|
+
*
|
|
5774
|
+
* `.length` counts UTF-16 code units, which undercounts CJK by ~3x and
|
|
5775
|
+
* Cyrillic/Arabic/emoji by ~2x — on GenAI payloads that made a batch several
|
|
5776
|
+
* times the configured limit, the exact oversized-export case this limit exists
|
|
5777
|
+
* to prevent.
|
|
5778
|
+
*
|
|
5779
|
+
* Unlike the Python SDK, which samples a strided subset of long strings, this
|
|
5780
|
+
* measures exactly. `Buffer.byteLength` walks the string in native code without
|
|
5781
|
+
* allocating the encoded bytes, so it is both cheaper and more accurate than
|
|
5782
|
+
* sampling here: measured ~27us on a 20k-character CJK string, against ~16us for
|
|
5783
|
+
* a sampled estimate that was also *wrong* for emoji (+11.7%, because indexing a
|
|
5784
|
+
* string splits surrogate pairs and each half encodes as U+FFFD). Python has no
|
|
5785
|
+
* equivalent primitive — `len(s.encode())` allocates the whole encoded copy —
|
|
5786
|
+
* which is the only reason it samples.
|
|
5787
|
+
*/
|
|
5788
|
+
const utf8Size = (value) => Buffer.byteLength(value, "utf8");
|
|
5789
|
+
/**
|
|
5790
|
+
* Approximate the exported size of a span, in bytes.
|
|
5791
|
+
*
|
|
5792
|
+
* Deliberately an underestimate: protobuf framing, ids and timestamps are
|
|
5793
|
+
* ignored, and non-string values are counted flat. Underestimating small spans
|
|
5794
|
+
* is fine — the count and schedule-delay limits fire first for those. What
|
|
5795
|
+
* matters is that a span carrying a large prompt or completion is measured close
|
|
5796
|
+
* to its real weight.
|
|
5797
|
+
*/
|
|
5798
|
+
const approximateSpanSize = (span) => {
|
|
5799
|
+
let total = utf8Size(span.name ?? "");
|
|
5800
|
+
total += attributesSize(span.attributes);
|
|
5801
|
+
for (const event of span.events ?? []) total += utf8Size(event.name ?? "") + attributesSize(event.attributes);
|
|
5802
|
+
for (const link of span.links ?? []) total += attributesSize(link.attributes);
|
|
5803
|
+
if (span.status?.message) total += utf8Size(span.status.message);
|
|
5804
|
+
return total;
|
|
5805
|
+
};
|
|
5806
|
+
/**
|
|
5807
|
+
* `BatchSpanProcessor` with a third, size-based flush trigger.
|
|
5808
|
+
*
|
|
5809
|
+
* Opt-in via `Laminar.initialize({ flushBySize: true })`; the default transport
|
|
5810
|
+
* is the plain upstream `BatchSpanProcessor`.
|
|
5811
|
+
*
|
|
5812
|
+
* Upstream flushes on whichever comes first: `maxExportBatchSize` spans
|
|
5813
|
+
* buffered, or `scheduledDelayMillis` elapsed. Neither bounds the *payload*, so
|
|
5814
|
+
* a handful of large GenAI spans can produce an export big enough for the
|
|
5815
|
+
* backend to reject. This adds `maxExportBatchSizeBytes`: when the span being
|
|
5816
|
+
* ended would push the buffer past the limit, the buffer is flushed first and
|
|
5817
|
+
* the span then starts a fresh batch.
|
|
5818
|
+
*
|
|
5819
|
+
* The Python SDK needs considerably more machinery for this — a dedicated flush
|
|
5820
|
+
* thread, a lock around the running total, a fork handler, and an inline
|
|
5821
|
+
* fallback for backpressure — none of which is needed here, because the two
|
|
5822
|
+
* runtimes behave differently in three ways:
|
|
5823
|
+
*
|
|
5824
|
+
* 1. `forceFlush()` moves the buffered spans out synchronously and only *awaits*
|
|
5825
|
+
* the network, so calling it from `onEnd` never blocks the caller (measured:
|
|
5826
|
+
* 0.34ms worst case against a 300ms export). Python's `force_flush` performs
|
|
5827
|
+
* the export itself on the calling thread, which load testing measured at a
|
|
5828
|
+
* p99 of ~664ms and is why it needs a background thread.
|
|
5829
|
+
* 2. Because the flush is synchronous with respect to the buffer, the batch stays
|
|
5830
|
+
* bounded even when a producer emits in a tight loop, so no inline-fallback
|
|
5831
|
+
* backpressure path is needed. Python's asynchronous handoff can be outrun.
|
|
5832
|
+
* 3. `onEnd` calls `super.onEnd(span)` *after* flushing, so the triggering span
|
|
5833
|
+
* is genuinely excluded from the batch it caused. Python cannot do this — its
|
|
5834
|
+
* flush is asynchronous, so the triggering span rides along, which is why it
|
|
5835
|
+
* needs a separate "span is at least half the limit" rule to bound overshoot.
|
|
5836
|
+
* Here a batch never exceeds the limit by more than the last span under it.
|
|
5837
|
+
*
|
|
5838
|
+
* Single-threaded event loop, so no lock guards the running total, and there is
|
|
5839
|
+
* no `fork` story to handle.
|
|
5840
|
+
*/
|
|
5841
|
+
var SizeLimitedBatchSpanProcessor = class extends BatchSpanProcessor {
|
|
5842
|
+
constructor(exporter, config) {
|
|
5843
|
+
super(exporter, config);
|
|
5844
|
+
this.pendingSizeBytes = 0;
|
|
5845
|
+
this.maxExportBatchSizeBytes = config?.maxExportBatchSizeBytes ?? 33554432;
|
|
5846
|
+
}
|
|
5847
|
+
onEnd(span) {
|
|
5848
|
+
if ((span.spanContext().traceFlags & TraceFlags.SAMPLED) === 0) {
|
|
5849
|
+
super.onEnd(span);
|
|
5850
|
+
return;
|
|
5851
|
+
}
|
|
5852
|
+
const size = approximateSpanSize(span);
|
|
5853
|
+
const shouldFlush = this.pendingSizeBytes > 0 && this.pendingSizeBytes + size > this.maxExportBatchSizeBytes;
|
|
5854
|
+
this.pendingSizeBytes = shouldFlush ? size : this.pendingSizeBytes + size;
|
|
5855
|
+
if (shouldFlush) this.forceFlush().catch((error) => {
|
|
5856
|
+
logger$12.debug(`Size-triggered span flush failed: ${String(error)}`);
|
|
5857
|
+
});
|
|
5858
|
+
super.onEnd(span);
|
|
5859
|
+
}
|
|
5860
|
+
};
|
|
5861
|
+
//#endregion
|
|
5731
5862
|
//#region src/opentelemetry-lib/tracing/exporter.ts
|
|
5732
5863
|
const logger$11 = initializeLogger();
|
|
5733
5864
|
var LaminarSpanExporter = class {
|
|
@@ -5738,7 +5869,7 @@ var LaminarSpanExporter = class {
|
|
|
5738
5869
|
const key = options.apiKey ?? process?.env?.LMNR_PROJECT_API_KEY;
|
|
5739
5870
|
let headers = {};
|
|
5740
5871
|
if (key) {
|
|
5741
|
-
headers = forceHttp ? {
|
|
5872
|
+
headers = forceHttp ? { Authorization: `Bearer ${key}` } : { authorization: `Bearer ${key}` };
|
|
5742
5873
|
if (!url) url = "https://api.lmnr.ai";
|
|
5743
5874
|
} else {
|
|
5744
5875
|
const otelHeaders = getOtelEnvVar("HEADERS");
|
|
@@ -5811,10 +5942,16 @@ var LaminarSpanProcessor = class LaminarSpanProcessor {
|
|
|
5811
5942
|
} else if (options.spanProcessor) this.instance = options.spanProcessor;
|
|
5812
5943
|
else {
|
|
5813
5944
|
const exporter = options.exporter ?? new LaminarSpanExporter(options);
|
|
5814
|
-
|
|
5945
|
+
const batchConfig = {
|
|
5815
5946
|
maxExportBatchSize: options.maxExportBatchSize ?? 512,
|
|
5816
5947
|
exportTimeoutMillis: options.traceExportTimeoutMillis ?? 3e4
|
|
5948
|
+
};
|
|
5949
|
+
if (options.disableBatch) this.instance = new SimpleSpanProcessor(exporter);
|
|
5950
|
+
else if (options.flushBySize) this.instance = new SizeLimitedBatchSpanProcessor(exporter, {
|
|
5951
|
+
...batchConfig,
|
|
5952
|
+
maxExportBatchSizeBytes: options.maxExportBatchSizeBytes
|
|
5817
5953
|
});
|
|
5954
|
+
else this.instance = new BatchSpanProcessor(exporter, batchConfig);
|
|
5818
5955
|
}
|
|
5819
5956
|
}
|
|
5820
5957
|
forceFlush() {
|
|
@@ -6727,6 +6864,8 @@ const startTracing = (options) => {
|
|
|
6727
6864
|
forceHttp: options.forceHttp,
|
|
6728
6865
|
traceExportTimeoutMillis: options.traceExportTimeoutMillis,
|
|
6729
6866
|
maxExportBatchSize: options.maxExportBatchSize,
|
|
6867
|
+
maxExportBatchSizeBytes: options.maxExportBatchSizeBytes,
|
|
6868
|
+
flushBySize: options.flushBySize,
|
|
6730
6869
|
exporter: options.exporter,
|
|
6731
6870
|
disableBatch: options.disableBatch
|
|
6732
6871
|
});
|
|
@@ -7059,7 +7198,7 @@ function withTracingLevel(tracingLevel, fn, ...args) {
|
|
|
7059
7198
|
const currentAssociationProperties = entityContext.getValue(ASSOCIATION_PROPERTIES_KEY) ?? {};
|
|
7060
7199
|
entityContext = entityContext.setValue(ASSOCIATION_PROPERTIES_KEY, {
|
|
7061
7200
|
...currentAssociationProperties,
|
|
7062
|
-
|
|
7201
|
+
tracing_level: tracingLevel
|
|
7063
7202
|
});
|
|
7064
7203
|
const result = LaminarContextManager.runWithIsolatedContext([entityContext], () => fn(...args));
|
|
7065
7204
|
const newAssociationProperties = entityContext.getValue(ASSOCIATION_PROPERTIES_KEY) ?? {};
|