langsmith 0.3.49-rc.0 → 0.3.49-rc.2
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/experimental/otel/constants.cjs +2 -1
- package/dist/experimental/otel/constants.d.ts +1 -0
- package/dist/experimental/otel/constants.js +1 -0
- package/dist/experimental/otel/exporter.cjs +0 -15
- package/dist/experimental/otel/exporter.d.ts +0 -11
- package/dist/experimental/otel/exporter.js +0 -15
- package/dist/experimental/otel/processor.cjs +109 -0
- package/dist/experimental/otel/processor.d.ts +17 -0
- package/dist/experimental/otel/processor.js +104 -0
- package/dist/experimental/otel/setup.cjs +2 -1
- package/dist/experimental/otel/setup.d.ts +2 -2
- package/dist/experimental/otel/setup.js +3 -2
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/experimental/otel/processor.cjs +1 -0
- package/experimental/otel/processor.d.cts +1 -0
- package/experimental/otel/processor.d.ts +1 -0
- package/experimental/otel/processor.js +1 -0
- package/package.json +14 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AI_SDK_TOOL_OPERATIONS = exports.AI_SDK_LLM_OPERATIONS = exports.GEN_AI_CHOICE = exports.GEN_AI_ASSISTANT_MESSAGE = exports.GEN_AI_USER_MESSAGE = exports.GEN_AI_SYSTEM_MESSAGE = exports.LANGSMITH_TRACEABLE = exports.LANGSMITH_REFERENCE_EXAMPLE_ID = exports.LANGSMITH_USAGE_METADATA = exports.LANGSMITH_PARENT_RUN_ID = exports.LANGSMITH_DOTTED_ORDER = exports.LANGSMITH_TRACE_ID = exports.LANGSMITH_RUN_ID = exports.LANGSMITH_REQUEST_HEADERS = exports.LANGSMITH_REQUEST_STREAMING = exports.LANGSMITH_RUNTIME = exports.LANGSMITH_TAGS = exports.LANGSMITH_METADATA = exports.LANGSMITH_NAME = exports.LANGSMITH_RUN_TYPE = exports.LANGSMITH_SESSION_NAME = exports.LANGSMITH_SESSION_ID = exports.GEN_AI_USAGE_OUTPUT_TOKEN_DETAILS = exports.GEN_AI_USAGE_INPUT_TOKEN_DETAILS = exports.GEN_AI_RESPONSE_SYSTEM_FINGERPRINT = exports.GEN_AI_RESPONSE_SERVICE_TIER = exports.GEN_AI_RESPONSE_ID = exports.GEN_AI_SERIALIZED_DOC = exports.GEN_AI_SERIALIZED_SIGNATURE = exports.GEN_AI_SERIALIZED_NAME = exports.GEN_AI_REQUEST_EXTRA_BODY = exports.GEN_AI_REQUEST_EXTRA_QUERY = exports.GENAI_COMPLETION = exports.GENAI_PROMPT = exports.GEN_AI_RESPONSE_FINISH_REASONS = exports.GEN_AI_REQUEST_PRESENCE_PENALTY = exports.GEN_AI_REQUEST_FREQUENCY_PENALTY = exports.GEN_AI_REQUEST_TOP_P = exports.GEN_AI_REQUEST_TEMPERATURE = exports.GEN_AI_REQUEST_MAX_TOKENS = exports.GEN_AI_USAGE_TOTAL_TOKENS = exports.GEN_AI_USAGE_OUTPUT_TOKENS = exports.GEN_AI_USAGE_INPUT_TOKENS = exports.GEN_AI_RESPONSE_MODEL = exports.GEN_AI_REQUEST_MODEL = exports.GEN_AI_SYSTEM = exports.GEN_AI_OPERATION_NAME = void 0;
|
|
3
|
+
exports.AI_SDK_TOOL_OPERATIONS = exports.AI_SDK_LLM_OPERATIONS = exports.GEN_AI_CHOICE = exports.GEN_AI_ASSISTANT_MESSAGE = exports.GEN_AI_USER_MESSAGE = exports.GEN_AI_SYSTEM_MESSAGE = exports.LANGSMITH_IS_ROOT = exports.LANGSMITH_TRACEABLE = exports.LANGSMITH_REFERENCE_EXAMPLE_ID = exports.LANGSMITH_USAGE_METADATA = exports.LANGSMITH_PARENT_RUN_ID = exports.LANGSMITH_DOTTED_ORDER = exports.LANGSMITH_TRACE_ID = exports.LANGSMITH_RUN_ID = exports.LANGSMITH_REQUEST_HEADERS = exports.LANGSMITH_REQUEST_STREAMING = exports.LANGSMITH_RUNTIME = exports.LANGSMITH_TAGS = exports.LANGSMITH_METADATA = exports.LANGSMITH_NAME = exports.LANGSMITH_RUN_TYPE = exports.LANGSMITH_SESSION_NAME = exports.LANGSMITH_SESSION_ID = exports.GEN_AI_USAGE_OUTPUT_TOKEN_DETAILS = exports.GEN_AI_USAGE_INPUT_TOKEN_DETAILS = exports.GEN_AI_RESPONSE_SYSTEM_FINGERPRINT = exports.GEN_AI_RESPONSE_SERVICE_TIER = exports.GEN_AI_RESPONSE_ID = exports.GEN_AI_SERIALIZED_DOC = exports.GEN_AI_SERIALIZED_SIGNATURE = exports.GEN_AI_SERIALIZED_NAME = exports.GEN_AI_REQUEST_EXTRA_BODY = exports.GEN_AI_REQUEST_EXTRA_QUERY = exports.GENAI_COMPLETION = exports.GENAI_PROMPT = exports.GEN_AI_RESPONSE_FINISH_REASONS = exports.GEN_AI_REQUEST_PRESENCE_PENALTY = exports.GEN_AI_REQUEST_FREQUENCY_PENALTY = exports.GEN_AI_REQUEST_TOP_P = exports.GEN_AI_REQUEST_TEMPERATURE = exports.GEN_AI_REQUEST_MAX_TOKENS = exports.GEN_AI_USAGE_TOTAL_TOKENS = exports.GEN_AI_USAGE_OUTPUT_TOKENS = exports.GEN_AI_USAGE_INPUT_TOKENS = exports.GEN_AI_RESPONSE_MODEL = exports.GEN_AI_REQUEST_MODEL = exports.GEN_AI_SYSTEM = exports.GEN_AI_OPERATION_NAME = void 0;
|
|
4
4
|
// OpenTelemetry GenAI semantic convention attribute names
|
|
5
5
|
exports.GEN_AI_OPERATION_NAME = "gen_ai.operation.name";
|
|
6
6
|
exports.GEN_AI_SYSTEM = "gen_ai.system";
|
|
@@ -44,6 +44,7 @@ exports.LANGSMITH_PARENT_RUN_ID = "langsmith.span.parent_id";
|
|
|
44
44
|
exports.LANGSMITH_USAGE_METADATA = "langsmith.usage_metadata";
|
|
45
45
|
exports.LANGSMITH_REFERENCE_EXAMPLE_ID = "langsmith.reference_example_id";
|
|
46
46
|
exports.LANGSMITH_TRACEABLE = "langsmith.traceable";
|
|
47
|
+
exports.LANGSMITH_IS_ROOT = "langsmith.is_root";
|
|
47
48
|
// GenAI event names
|
|
48
49
|
exports.GEN_AI_SYSTEM_MESSAGE = "gen_ai.system.message";
|
|
49
50
|
exports.GEN_AI_USER_MESSAGE = "gen_ai.user.message";
|
|
@@ -39,6 +39,7 @@ export declare const LANGSMITH_PARENT_RUN_ID = "langsmith.span.parent_id";
|
|
|
39
39
|
export declare const LANGSMITH_USAGE_METADATA = "langsmith.usage_metadata";
|
|
40
40
|
export declare const LANGSMITH_REFERENCE_EXAMPLE_ID = "langsmith.reference_example_id";
|
|
41
41
|
export declare const LANGSMITH_TRACEABLE = "langsmith.traceable";
|
|
42
|
+
export declare const LANGSMITH_IS_ROOT = "langsmith.is_root";
|
|
42
43
|
export declare const GEN_AI_SYSTEM_MESSAGE = "gen_ai.system.message";
|
|
43
44
|
export declare const GEN_AI_USER_MESSAGE = "gen_ai.user.message";
|
|
44
45
|
export declare const GEN_AI_ASSISTANT_MESSAGE = "gen_ai.assistant.message";
|
|
@@ -41,6 +41,7 @@ export const LANGSMITH_PARENT_RUN_ID = "langsmith.span.parent_id";
|
|
|
41
41
|
export const LANGSMITH_USAGE_METADATA = "langsmith.usage_metadata";
|
|
42
42
|
export const LANGSMITH_REFERENCE_EXAMPLE_ID = "langsmith.reference_example_id";
|
|
43
43
|
export const LANGSMITH_TRACEABLE = "langsmith.traceable";
|
|
44
|
+
export const LANGSMITH_IS_ROOT = "langsmith.is_root";
|
|
44
45
|
// GenAI event names
|
|
45
46
|
export const GEN_AI_SYSTEM_MESSAGE = "gen_ai.system.message";
|
|
46
47
|
export const GEN_AI_USER_MESSAGE = "gen_ai.user.message";
|
|
@@ -39,7 +39,6 @@ const constants = __importStar(require("./constants.cjs"));
|
|
|
39
39
|
const env_js_1 = require("../../env.cjs");
|
|
40
40
|
const env_js_2 = require("../../utils/env.cjs");
|
|
41
41
|
const vercel_js_1 = require("../../utils/vercel.cjs");
|
|
42
|
-
const constants_js_1 = require("./constants.cjs");
|
|
43
42
|
/**
|
|
44
43
|
* Convert headers string in format "name=value,name2=value2" to object
|
|
45
44
|
*/
|
|
@@ -55,10 +54,6 @@ function parseHeadersString(headersStr) {
|
|
|
55
54
|
});
|
|
56
55
|
return headers;
|
|
57
56
|
}
|
|
58
|
-
function isTraceableSpan(span) {
|
|
59
|
-
return (span.attributes[constants_js_1.LANGSMITH_TRACEABLE] === "true" ||
|
|
60
|
-
typeof span.attributes["ai.operationId"] === "string");
|
|
61
|
-
}
|
|
62
57
|
/**
|
|
63
58
|
* LangSmith OpenTelemetry trace exporter that extends the standard OTLP trace exporter
|
|
64
59
|
* with LangSmith-specific configuration and span attribute transformations.
|
|
@@ -110,16 +105,9 @@ class LangSmithOTLPTraceExporter extends exporter_trace_otlp_proto_1.OTLPTraceEx
|
|
|
110
105
|
writable: true,
|
|
111
106
|
value: void 0
|
|
112
107
|
});
|
|
113
|
-
Object.defineProperty(this, "exportAllSpans", {
|
|
114
|
-
enumerable: true,
|
|
115
|
-
configurable: true,
|
|
116
|
-
writable: true,
|
|
117
|
-
value: void 0
|
|
118
|
-
});
|
|
119
108
|
this.transformExportedSpan = config?.transformExportedSpan;
|
|
120
109
|
this.projectName =
|
|
121
110
|
config?.projectName ?? (0, env_js_2.getLangSmithEnvironmentVariable)("PROJECT");
|
|
122
|
-
this.exportAllSpans = config?.exportAllSpans;
|
|
123
111
|
}
|
|
124
112
|
export(spans, resultCallback) {
|
|
125
113
|
if (!(0, env_js_1.isTracingEnabled)()) {
|
|
@@ -127,9 +115,6 @@ class LangSmithOTLPTraceExporter extends exporter_trace_otlp_proto_1.OTLPTraceEx
|
|
|
127
115
|
}
|
|
128
116
|
const runExport = async () => {
|
|
129
117
|
for (let span of spans) {
|
|
130
|
-
if (!this.exportAllSpans && !isTraceableSpan(span)) {
|
|
131
|
-
continue;
|
|
132
|
-
}
|
|
133
118
|
if (this.transformExportedSpan) {
|
|
134
119
|
span = await this.transformExportedSpan(span);
|
|
135
120
|
}
|
|
@@ -36,16 +36,6 @@ export type LangSmithOTLPTraceExporterConfig = ConstructorParameters<typeof OTLP
|
|
|
36
36
|
* Default headers to add to exporter requests.
|
|
37
37
|
*/
|
|
38
38
|
headers?: Record<string, string>;
|
|
39
|
-
/**
|
|
40
|
-
* Trace all spans.
|
|
41
|
-
*
|
|
42
|
-
* If true, all spans will be traced, regardless of whether they are
|
|
43
|
-
* normally interesting to LangSmith.
|
|
44
|
-
* If false, only selected spans will be traced.
|
|
45
|
-
*
|
|
46
|
-
* @default false
|
|
47
|
-
*/
|
|
48
|
-
exportAllSpans?: boolean;
|
|
49
39
|
};
|
|
50
40
|
/**
|
|
51
41
|
* LangSmith OpenTelemetry trace exporter that extends the standard OTLP trace exporter
|
|
@@ -65,7 +55,6 @@ export type LangSmithOTLPTraceExporterConfig = ConstructorParameters<typeof OTLP
|
|
|
65
55
|
export declare class LangSmithOTLPTraceExporter extends OTLPTraceExporter {
|
|
66
56
|
private transformExportedSpan?;
|
|
67
57
|
private projectName?;
|
|
68
|
-
private exportAllSpans?;
|
|
69
58
|
constructor(config?: LangSmithOTLPTraceExporterConfig);
|
|
70
59
|
export(spans: ReadableSpan[], resultCallback: Parameters<OTLPTraceExporter["export"]>[1]): void;
|
|
71
60
|
}
|
|
@@ -3,7 +3,6 @@ import * as constants from "./constants.js";
|
|
|
3
3
|
import { isTracingEnabled } from "../../env.js";
|
|
4
4
|
import { getEnvironmentVariable, getLangSmithEnvironmentVariable, } from "../../utils/env.js";
|
|
5
5
|
import { extractUsageMetadata } from "../../utils/vercel.js";
|
|
6
|
-
import { LANGSMITH_TRACEABLE } from "./constants.js";
|
|
7
6
|
/**
|
|
8
7
|
* Convert headers string in format "name=value,name2=value2" to object
|
|
9
8
|
*/
|
|
@@ -19,10 +18,6 @@ function parseHeadersString(headersStr) {
|
|
|
19
18
|
});
|
|
20
19
|
return headers;
|
|
21
20
|
}
|
|
22
|
-
function isTraceableSpan(span) {
|
|
23
|
-
return (span.attributes[LANGSMITH_TRACEABLE] === "true" ||
|
|
24
|
-
typeof span.attributes["ai.operationId"] === "string");
|
|
25
|
-
}
|
|
26
21
|
/**
|
|
27
22
|
* LangSmith OpenTelemetry trace exporter that extends the standard OTLP trace exporter
|
|
28
23
|
* with LangSmith-specific configuration and span attribute transformations.
|
|
@@ -74,16 +69,9 @@ export class LangSmithOTLPTraceExporter extends OTLPTraceExporter {
|
|
|
74
69
|
writable: true,
|
|
75
70
|
value: void 0
|
|
76
71
|
});
|
|
77
|
-
Object.defineProperty(this, "exportAllSpans", {
|
|
78
|
-
enumerable: true,
|
|
79
|
-
configurable: true,
|
|
80
|
-
writable: true,
|
|
81
|
-
value: void 0
|
|
82
|
-
});
|
|
83
72
|
this.transformExportedSpan = config?.transformExportedSpan;
|
|
84
73
|
this.projectName =
|
|
85
74
|
config?.projectName ?? getLangSmithEnvironmentVariable("PROJECT");
|
|
86
|
-
this.exportAllSpans = config?.exportAllSpans;
|
|
87
75
|
}
|
|
88
76
|
export(spans, resultCallback) {
|
|
89
77
|
if (!isTracingEnabled()) {
|
|
@@ -91,9 +79,6 @@ export class LangSmithOTLPTraceExporter extends OTLPTraceExporter {
|
|
|
91
79
|
}
|
|
92
80
|
const runExport = async () => {
|
|
93
81
|
for (let span of spans) {
|
|
94
|
-
if (!this.exportAllSpans && !isTraceableSpan(span)) {
|
|
95
|
-
continue;
|
|
96
|
-
}
|
|
97
82
|
if (this.transformExportedSpan) {
|
|
98
83
|
span = await this.transformExportedSpan(span);
|
|
99
84
|
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LangSmithOTLPSpanProcessor = void 0;
|
|
4
|
+
exports.isTraceableSpan = isTraceableSpan;
|
|
5
|
+
const sdk_trace_base_1 = require("@opentelemetry/sdk-trace-base");
|
|
6
|
+
const constants_js_1 = require("./constants.cjs");
|
|
7
|
+
const utils_js_1 = require("./utils.cjs");
|
|
8
|
+
function isTraceableSpan(span) {
|
|
9
|
+
return (span.attributes[constants_js_1.LANGSMITH_TRACEABLE] === "true" ||
|
|
10
|
+
typeof span.attributes["ai.operationId"] === "string");
|
|
11
|
+
}
|
|
12
|
+
function getParentSpanId(span) {
|
|
13
|
+
// Backcompat shim to support OTEL 1.x and 2.x
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
15
|
+
return (span.parentSpanId ?? span.parentSpanContext?.spanId ?? undefined);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Span processor that filters out spans that are not LangSmith-related and
|
|
19
|
+
* usually should not be traced.
|
|
20
|
+
*/
|
|
21
|
+
class LangSmithOTLPSpanProcessor extends sdk_trace_base_1.BatchSpanProcessor {
|
|
22
|
+
constructor(...args) {
|
|
23
|
+
super(...args);
|
|
24
|
+
Object.defineProperty(this, "traceMap", {
|
|
25
|
+
enumerable: true,
|
|
26
|
+
configurable: true,
|
|
27
|
+
writable: true,
|
|
28
|
+
value: {}
|
|
29
|
+
});
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
31
|
+
Object.defineProperty(this, "cleanupInterval", {
|
|
32
|
+
enumerable: true,
|
|
33
|
+
configurable: true,
|
|
34
|
+
writable: true,
|
|
35
|
+
value: void 0
|
|
36
|
+
});
|
|
37
|
+
Object.defineProperty(this, "TRACE_TTL_MS", {
|
|
38
|
+
enumerable: true,
|
|
39
|
+
configurable: true,
|
|
40
|
+
writable: true,
|
|
41
|
+
value: 10 * 60 * 1000
|
|
42
|
+
}); // 10 minutes
|
|
43
|
+
// We must use a cleanup interval because LangSmith can start child spans
|
|
44
|
+
// after arbitrary OTEL parent spans have ended since it uses batching.
|
|
45
|
+
this.cleanupInterval = setInterval(() => this.cleanupStaleTraces(), 60000);
|
|
46
|
+
}
|
|
47
|
+
cleanupStaleTraces() {
|
|
48
|
+
const now = Date.now();
|
|
49
|
+
for (const [traceId, traceInfo] of Object.entries(this.traceMap)) {
|
|
50
|
+
if (now - traceInfo.lastAccessed > this.TRACE_TTL_MS) {
|
|
51
|
+
delete this.traceMap[traceId];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
shutdown() {
|
|
56
|
+
if (this.cleanupInterval) {
|
|
57
|
+
clearInterval(this.cleanupInterval);
|
|
58
|
+
}
|
|
59
|
+
return super.shutdown();
|
|
60
|
+
}
|
|
61
|
+
onStart(span, parentContext) {
|
|
62
|
+
if (!this.traceMap[span.spanContext().traceId]) {
|
|
63
|
+
this.traceMap[span.spanContext().traceId] = {
|
|
64
|
+
spanInfo: {},
|
|
65
|
+
lastAccessed: Date.now(),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
this.traceMap[span.spanContext().traceId].lastAccessed = Date.now();
|
|
69
|
+
const isTraceable = isTraceableSpan(span);
|
|
70
|
+
const parentSpanId = getParentSpanId(span);
|
|
71
|
+
this.traceMap[span.spanContext().traceId].spanInfo[span.spanContext().spanId] = {
|
|
72
|
+
isTraceable,
|
|
73
|
+
parentSpanId,
|
|
74
|
+
};
|
|
75
|
+
let currentCandidateParentSpanId = parentSpanId;
|
|
76
|
+
let traceableParentId;
|
|
77
|
+
while (currentCandidateParentSpanId) {
|
|
78
|
+
const currentSpanInfo = this.traceMap[span.spanContext().traceId].spanInfo[currentCandidateParentSpanId];
|
|
79
|
+
if (currentSpanInfo?.isTraceable) {
|
|
80
|
+
traceableParentId = currentCandidateParentSpanId;
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
currentCandidateParentSpanId = currentSpanInfo?.parentSpanId;
|
|
84
|
+
}
|
|
85
|
+
if (!traceableParentId) {
|
|
86
|
+
span.attributes[constants_js_1.LANGSMITH_IS_ROOT] = true;
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
span.attributes[constants_js_1.LANGSMITH_PARENT_RUN_ID] =
|
|
90
|
+
(0, utils_js_1.getUuidFromOtelSpanId)(traceableParentId);
|
|
91
|
+
}
|
|
92
|
+
if (isTraceable) {
|
|
93
|
+
super.onStart(span, parentContext);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
onEnd(span) {
|
|
97
|
+
const traceInfo = this.traceMap[span.spanContext().traceId];
|
|
98
|
+
if (!traceInfo)
|
|
99
|
+
return;
|
|
100
|
+
traceInfo.lastAccessed = Date.now();
|
|
101
|
+
const spanInfo = traceInfo.spanInfo[span.spanContext().spanId];
|
|
102
|
+
if (!spanInfo)
|
|
103
|
+
return;
|
|
104
|
+
if (spanInfo.isTraceable) {
|
|
105
|
+
super.onEnd(span);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
exports.LangSmithOTLPSpanProcessor = LangSmithOTLPSpanProcessor;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ReadableSpan, Span, BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
2
|
+
import { Context } from "@opentelemetry/api";
|
|
3
|
+
export declare function isTraceableSpan(span: ReadableSpan): boolean;
|
|
4
|
+
/**
|
|
5
|
+
* Span processor that filters out spans that are not LangSmith-related and
|
|
6
|
+
* usually should not be traced.
|
|
7
|
+
*/
|
|
8
|
+
export declare class LangSmithOTLPSpanProcessor extends BatchSpanProcessor {
|
|
9
|
+
private traceMap;
|
|
10
|
+
private cleanupInterval;
|
|
11
|
+
private TRACE_TTL_MS;
|
|
12
|
+
constructor(...args: ConstructorParameters<typeof BatchSpanProcessor>);
|
|
13
|
+
private cleanupStaleTraces;
|
|
14
|
+
shutdown(): Promise<void>;
|
|
15
|
+
onStart(span: Span, parentContext: Context): void;
|
|
16
|
+
onEnd(span: ReadableSpan): void;
|
|
17
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { BatchSpanProcessor, } from "@opentelemetry/sdk-trace-base";
|
|
2
|
+
import { LANGSMITH_IS_ROOT, LANGSMITH_PARENT_RUN_ID, LANGSMITH_TRACEABLE, } from "./constants.js";
|
|
3
|
+
import { getUuidFromOtelSpanId } from "./utils.js";
|
|
4
|
+
export function isTraceableSpan(span) {
|
|
5
|
+
return (span.attributes[LANGSMITH_TRACEABLE] === "true" ||
|
|
6
|
+
typeof span.attributes["ai.operationId"] === "string");
|
|
7
|
+
}
|
|
8
|
+
function getParentSpanId(span) {
|
|
9
|
+
// Backcompat shim to support OTEL 1.x and 2.x
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
11
|
+
return (span.parentSpanId ?? span.parentSpanContext?.spanId ?? undefined);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Span processor that filters out spans that are not LangSmith-related and
|
|
15
|
+
* usually should not be traced.
|
|
16
|
+
*/
|
|
17
|
+
export class LangSmithOTLPSpanProcessor extends BatchSpanProcessor {
|
|
18
|
+
constructor(...args) {
|
|
19
|
+
super(...args);
|
|
20
|
+
Object.defineProperty(this, "traceMap", {
|
|
21
|
+
enumerable: true,
|
|
22
|
+
configurable: true,
|
|
23
|
+
writable: true,
|
|
24
|
+
value: {}
|
|
25
|
+
});
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
27
|
+
Object.defineProperty(this, "cleanupInterval", {
|
|
28
|
+
enumerable: true,
|
|
29
|
+
configurable: true,
|
|
30
|
+
writable: true,
|
|
31
|
+
value: void 0
|
|
32
|
+
});
|
|
33
|
+
Object.defineProperty(this, "TRACE_TTL_MS", {
|
|
34
|
+
enumerable: true,
|
|
35
|
+
configurable: true,
|
|
36
|
+
writable: true,
|
|
37
|
+
value: 10 * 60 * 1000
|
|
38
|
+
}); // 10 minutes
|
|
39
|
+
// We must use a cleanup interval because LangSmith can start child spans
|
|
40
|
+
// after arbitrary OTEL parent spans have ended since it uses batching.
|
|
41
|
+
this.cleanupInterval = setInterval(() => this.cleanupStaleTraces(), 60000);
|
|
42
|
+
}
|
|
43
|
+
cleanupStaleTraces() {
|
|
44
|
+
const now = Date.now();
|
|
45
|
+
for (const [traceId, traceInfo] of Object.entries(this.traceMap)) {
|
|
46
|
+
if (now - traceInfo.lastAccessed > this.TRACE_TTL_MS) {
|
|
47
|
+
delete this.traceMap[traceId];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
shutdown() {
|
|
52
|
+
if (this.cleanupInterval) {
|
|
53
|
+
clearInterval(this.cleanupInterval);
|
|
54
|
+
}
|
|
55
|
+
return super.shutdown();
|
|
56
|
+
}
|
|
57
|
+
onStart(span, parentContext) {
|
|
58
|
+
if (!this.traceMap[span.spanContext().traceId]) {
|
|
59
|
+
this.traceMap[span.spanContext().traceId] = {
|
|
60
|
+
spanInfo: {},
|
|
61
|
+
lastAccessed: Date.now(),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
this.traceMap[span.spanContext().traceId].lastAccessed = Date.now();
|
|
65
|
+
const isTraceable = isTraceableSpan(span);
|
|
66
|
+
const parentSpanId = getParentSpanId(span);
|
|
67
|
+
this.traceMap[span.spanContext().traceId].spanInfo[span.spanContext().spanId] = {
|
|
68
|
+
isTraceable,
|
|
69
|
+
parentSpanId,
|
|
70
|
+
};
|
|
71
|
+
let currentCandidateParentSpanId = parentSpanId;
|
|
72
|
+
let traceableParentId;
|
|
73
|
+
while (currentCandidateParentSpanId) {
|
|
74
|
+
const currentSpanInfo = this.traceMap[span.spanContext().traceId].spanInfo[currentCandidateParentSpanId];
|
|
75
|
+
if (currentSpanInfo?.isTraceable) {
|
|
76
|
+
traceableParentId = currentCandidateParentSpanId;
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
currentCandidateParentSpanId = currentSpanInfo?.parentSpanId;
|
|
80
|
+
}
|
|
81
|
+
if (!traceableParentId) {
|
|
82
|
+
span.attributes[LANGSMITH_IS_ROOT] = true;
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
span.attributes[LANGSMITH_PARENT_RUN_ID] =
|
|
86
|
+
getUuidFromOtelSpanId(traceableParentId);
|
|
87
|
+
}
|
|
88
|
+
if (isTraceable) {
|
|
89
|
+
super.onStart(span, parentContext);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
onEnd(span) {
|
|
93
|
+
const traceInfo = this.traceMap[span.spanContext().traceId];
|
|
94
|
+
if (!traceInfo)
|
|
95
|
+
return;
|
|
96
|
+
traceInfo.lastAccessed = Date.now();
|
|
97
|
+
const spanInfo = traceInfo.spanInfo[span.spanContext().spanId];
|
|
98
|
+
if (!spanInfo)
|
|
99
|
+
return;
|
|
100
|
+
if (spanInfo.isTraceable) {
|
|
101
|
+
super.onEnd(span);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -8,6 +8,7 @@ const context_async_hooks_1 = require("@opentelemetry/context-async-hooks");
|
|
|
8
8
|
const api_1 = require("@opentelemetry/api");
|
|
9
9
|
const sdk_trace_base_1 = require("@opentelemetry/sdk-trace-base");
|
|
10
10
|
const exporter_js_1 = require("./exporter.cjs");
|
|
11
|
+
const processor_js_1 = require("./processor.cjs");
|
|
11
12
|
const otel_js_1 = require("../../singletons/otel.cjs");
|
|
12
13
|
/**
|
|
13
14
|
* Initializes OpenTelemetry with LangSmith-specific configuration for tracing.
|
|
@@ -62,7 +63,7 @@ const initializeOTEL = (config = {}) => {
|
|
|
62
63
|
}
|
|
63
64
|
}
|
|
64
65
|
const DEFAULT_LANGSMITH_SPAN_EXPORTER = new exporter_js_1.LangSmithOTLPTraceExporter(exporterConfig);
|
|
65
|
-
const DEFAULT_LANGSMITH_SPAN_PROCESSOR = new
|
|
66
|
+
const DEFAULT_LANGSMITH_SPAN_PROCESSOR = new processor_js_1.LangSmithOTLPSpanProcessor(DEFAULT_LANGSMITH_SPAN_EXPORTER);
|
|
66
67
|
if (!globalTracerProvider) {
|
|
67
68
|
const DEFAULT_LANGSMITH_TRACER_PROVIDER = new sdk_trace_base_1.BasicTracerProvider({
|
|
68
69
|
spanProcessors: [DEFAULT_LANGSMITH_SPAN_PROCESSOR],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type TracerProvider, type ContextManager } from "@opentelemetry/api";
|
|
2
|
-
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
3
2
|
import { LangSmithOTLPTraceExporter, LangSmithOTLPTraceExporterConfig } from "./exporter.js";
|
|
3
|
+
import { LangSmithOTLPSpanProcessor } from "./processor.js";
|
|
4
4
|
/**
|
|
5
5
|
* Configuration options for initializing OpenTelemetry with LangSmith.
|
|
6
6
|
*/
|
|
@@ -59,6 +59,6 @@ export type InitializeOTELConfig = {
|
|
|
59
59
|
*/
|
|
60
60
|
export declare const initializeOTEL: (config?: InitializeOTELConfig) => {
|
|
61
61
|
DEFAULT_LANGSMITH_TRACER_PROVIDER: TracerProvider;
|
|
62
|
-
DEFAULT_LANGSMITH_SPAN_PROCESSOR:
|
|
62
|
+
DEFAULT_LANGSMITH_SPAN_PROCESSOR: LangSmithOTLPSpanProcessor;
|
|
63
63
|
DEFAULT_LANGSMITH_SPAN_EXPORTER: LangSmithOTLPTraceExporter;
|
|
64
64
|
};
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
4
4
|
import { AsyncHooksContextManager } from "@opentelemetry/context-async-hooks";
|
|
5
5
|
import { trace as otel_trace, context as otel_context, } from "@opentelemetry/api";
|
|
6
|
-
import {
|
|
6
|
+
import { BasicTracerProvider } from "@opentelemetry/sdk-trace-base";
|
|
7
7
|
import { LangSmithOTLPTraceExporter, } from "./exporter.js";
|
|
8
|
+
import { LangSmithOTLPSpanProcessor } from "./processor.js";
|
|
8
9
|
import { setDefaultOTLPTracerComponents, setOTELInstances, } from "../../singletons/otel.js";
|
|
9
10
|
/**
|
|
10
11
|
* Initializes OpenTelemetry with LangSmith-specific configuration for tracing.
|
|
@@ -59,7 +60,7 @@ export const initializeOTEL = (config = {}) => {
|
|
|
59
60
|
}
|
|
60
61
|
}
|
|
61
62
|
const DEFAULT_LANGSMITH_SPAN_EXPORTER = new LangSmithOTLPTraceExporter(exporterConfig);
|
|
62
|
-
const DEFAULT_LANGSMITH_SPAN_PROCESSOR = new
|
|
63
|
+
const DEFAULT_LANGSMITH_SPAN_PROCESSOR = new LangSmithOTLPSpanProcessor(DEFAULT_LANGSMITH_SPAN_EXPORTER);
|
|
63
64
|
if (!globalTracerProvider) {
|
|
64
65
|
const DEFAULT_LANGSMITH_TRACER_PROVIDER = new BasicTracerProvider({
|
|
65
66
|
spanProcessors: [DEFAULT_LANGSMITH_SPAN_PROCESSOR],
|
package/dist/index.cjs
CHANGED
|
@@ -10,4 +10,4 @@ Object.defineProperty(exports, "overrideFetchImplementation", { enumerable: true
|
|
|
10
10
|
var project_js_1 = require("./utils/project.cjs");
|
|
11
11
|
Object.defineProperty(exports, "getDefaultProjectName", { enumerable: true, get: function () { return project_js_1.getDefaultProjectName; } });
|
|
12
12
|
// Update using yarn bump-version
|
|
13
|
-
exports.__version__ = "0.3.49-rc.
|
|
13
|
+
exports.__version__ = "0.3.49-rc.2";
|
package/dist/index.d.ts
CHANGED
|
@@ -3,4 +3,4 @@ export type { Dataset, Example, TracerSession, Run, Feedback, RetrieverOutput, }
|
|
|
3
3
|
export { RunTree, type RunTreeConfig } from "./run_trees.js";
|
|
4
4
|
export { overrideFetchImplementation } from "./singletons/fetch.js";
|
|
5
5
|
export { getDefaultProjectName } from "./utils/project.js";
|
|
6
|
-
export declare const __version__ = "0.3.49-rc.
|
|
6
|
+
export declare const __version__ = "0.3.49-rc.2";
|
package/dist/index.js
CHANGED
|
@@ -3,4 +3,4 @@ export { RunTree } from "./run_trees.js";
|
|
|
3
3
|
export { overrideFetchImplementation } from "./singletons/fetch.js";
|
|
4
4
|
export { getDefaultProjectName } from "./utils/project.js";
|
|
5
5
|
// Update using yarn bump-version
|
|
6
|
-
export const __version__ = "0.3.49-rc.
|
|
6
|
+
export const __version__ = "0.3.49-rc.2";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('../../dist/experimental/otel/processor.cjs');
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../../dist/experimental/otel/processor.js'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../../dist/experimental/otel/processor.js'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../../dist/experimental/otel/processor.js'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "langsmith",
|
|
3
|
-
"version": "0.3.49-rc.
|
|
3
|
+
"version": "0.3.49-rc.2",
|
|
4
4
|
"description": "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform.",
|
|
5
5
|
"packageManager": "yarn@1.22.19",
|
|
6
6
|
"files": [
|
|
@@ -85,6 +85,10 @@
|
|
|
85
85
|
"experimental/otel/exporter.js",
|
|
86
86
|
"experimental/otel/exporter.d.ts",
|
|
87
87
|
"experimental/otel/exporter.d.cts",
|
|
88
|
+
"experimental/otel/processor.cjs",
|
|
89
|
+
"experimental/otel/processor.js",
|
|
90
|
+
"experimental/otel/processor.d.ts",
|
|
91
|
+
"experimental/otel/processor.d.cts",
|
|
88
92
|
"index.cjs",
|
|
89
93
|
"index.js",
|
|
90
94
|
"index.d.ts",
|
|
@@ -397,6 +401,15 @@
|
|
|
397
401
|
"import": "./experimental/otel/exporter.js",
|
|
398
402
|
"require": "./experimental/otel/exporter.cjs"
|
|
399
403
|
},
|
|
404
|
+
"./experimental/otel/processor": {
|
|
405
|
+
"types": {
|
|
406
|
+
"import": "./experimental/otel/processor.d.ts",
|
|
407
|
+
"require": "./experimental/otel/processor.d.cts",
|
|
408
|
+
"default": "./experimental/otel/processor.d.ts"
|
|
409
|
+
},
|
|
410
|
+
"import": "./experimental/otel/processor.js",
|
|
411
|
+
"require": "./experimental/otel/processor.cjs"
|
|
412
|
+
},
|
|
400
413
|
"./package.json": "./package.json"
|
|
401
414
|
}
|
|
402
415
|
}
|