langsmith 0.3.36 → 0.3.38
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/exporter.cjs +80 -62
- package/dist/experimental/otel/exporter.d.ts +27 -1
- package/dist/experimental/otel/exporter.js +80 -62
- package/dist/experimental/otel/setup.cjs +3 -2
- package/dist/experimental/otel/setup.d.ts +21 -5
- package/dist/experimental/otel/setup.js +4 -3
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/singletons/traceable.cjs +1 -2
- package/dist/singletons/traceable.d.ts +4 -4
- package/dist/singletons/traceable.js +1 -2
- package/dist/singletons/types.cjs +1 -0
- package/dist/singletons/types.d.ts +4 -0
- package/dist/singletons/types.js +1 -1
- package/dist/traceable.cjs +22 -14
- package/dist/traceable.d.ts +18 -17
- package/dist/traceable.js +22 -14
- package/dist/wrappers/openai.cjs +85 -64
- package/dist/wrappers/openai.d.ts +7 -8
- package/dist/wrappers/openai.js +86 -65
- package/package.json +9 -8
|
@@ -70,10 +70,10 @@ function parseHeadersString(headersStr) {
|
|
|
70
70
|
*/
|
|
71
71
|
class LangSmithOTLPTraceExporter extends exporter_trace_otlp_proto_1.OTLPTraceExporter {
|
|
72
72
|
constructor(config) {
|
|
73
|
-
const
|
|
73
|
+
const defaultLsEndpoint = (0, env_js_2.getEnvironmentVariable)("OTEL_EXPORTER_OTLP_ENDPOINT") ||
|
|
74
74
|
(0, env_js_2.getLangSmithEnvironmentVariable)("ENDPOINT") ||
|
|
75
75
|
"https://api.smith.langchain.com";
|
|
76
|
-
const defaultBaseUrl =
|
|
76
|
+
const defaultBaseUrl = defaultLsEndpoint.replace(/\/$/, "");
|
|
77
77
|
const defaultUrl = `${defaultBaseUrl}/otel/v1/traces`;
|
|
78
78
|
// Configure headers with API key and project if available
|
|
79
79
|
let defaultHeaderString = (0, env_js_2.getEnvironmentVariable)("OTEL_EXPORTER_OTLP_HEADERS") ?? "";
|
|
@@ -92,82 +92,100 @@ class LangSmithOTLPTraceExporter extends exporter_trace_otlp_proto_1.OTLPTraceEx
|
|
|
92
92
|
headers: parseHeadersString(defaultHeaderString),
|
|
93
93
|
...config,
|
|
94
94
|
});
|
|
95
|
+
Object.defineProperty(this, "transformExportedSpan", {
|
|
96
|
+
enumerable: true,
|
|
97
|
+
configurable: true,
|
|
98
|
+
writable: true,
|
|
99
|
+
value: void 0
|
|
100
|
+
});
|
|
101
|
+
this.transformExportedSpan = config?.transformExportedSpan;
|
|
95
102
|
}
|
|
96
103
|
export(spans, resultCallback) {
|
|
97
104
|
if (!(0, env_js_1.isTracingEnabled)()) {
|
|
98
105
|
return resultCallback({ code: 0 });
|
|
99
106
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if (
|
|
103
|
-
span
|
|
104
|
-
span.attributes["ai.prompt"];
|
|
107
|
+
const runExport = async () => {
|
|
108
|
+
for (let span of spans) {
|
|
109
|
+
if (this.transformExportedSpan) {
|
|
110
|
+
span = await this.transformExportedSpan(span);
|
|
105
111
|
}
|
|
106
|
-
if (span.attributes[
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
messages = JSON.parse(span.attributes["ai.prompt.messages"]);
|
|
112
|
+
if (!span.attributes[constants.GENAI_PROMPT]) {
|
|
113
|
+
if (span.attributes["ai.prompt"]) {
|
|
114
|
+
span.attributes[constants.GENAI_PROMPT] =
|
|
115
|
+
span.attributes["ai.prompt"];
|
|
111
116
|
}
|
|
112
|
-
|
|
113
|
-
|
|
117
|
+
if (span.attributes["ai.prompt.messages"] &&
|
|
118
|
+
typeof span.attributes["ai.prompt.messages"] === "string") {
|
|
119
|
+
let messages;
|
|
120
|
+
try {
|
|
121
|
+
messages = JSON.parse(span.attributes["ai.prompt.messages"]);
|
|
122
|
+
}
|
|
123
|
+
catch (e) {
|
|
124
|
+
console.error("Failed to parse ai.prompt.messages", e);
|
|
125
|
+
}
|
|
126
|
+
if (messages && Array.isArray(messages)) {
|
|
127
|
+
span.attributes[constants.GENAI_PROMPT] = JSON.stringify({
|
|
128
|
+
input: messages,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
114
131
|
}
|
|
115
|
-
if (
|
|
116
|
-
span.attributes[constants.GENAI_PROMPT] =
|
|
117
|
-
|
|
118
|
-
});
|
|
132
|
+
if (span.attributes["ai.toolCall.args"]) {
|
|
133
|
+
span.attributes[constants.GENAI_PROMPT] =
|
|
134
|
+
span.attributes["ai.toolCall.args"];
|
|
119
135
|
}
|
|
120
136
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const metadataKey = key.replace("ai.telemetry.metadata.", "");
|
|
130
|
-
span.attributes[`${constants.LANGSMITH_METADATA}.${metadataKey}`] =
|
|
131
|
-
value;
|
|
132
|
-
delete span.attributes[key];
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
if (!span.attributes[constants.GENAI_COMPLETION]) {
|
|
136
|
-
if (span.attributes["ai.response.text"]) {
|
|
137
|
-
span.attributes[constants.GENAI_COMPLETION] =
|
|
138
|
-
span.attributes["ai.response.text"];
|
|
139
|
-
}
|
|
140
|
-
if (span.attributes["ai.response.choices"]) {
|
|
141
|
-
span.attributes[constants.GENAI_COMPLETION] =
|
|
142
|
-
span.attributes["ai.response.choices"];
|
|
137
|
+
// Iterate over all attributes starting with "ai.telemetry.metadata"
|
|
138
|
+
for (const [key, value] of Object.entries(span.attributes)) {
|
|
139
|
+
if (key.startsWith("ai.telemetry.metadata.")) {
|
|
140
|
+
const metadataKey = key.replace("ai.telemetry.metadata.", "");
|
|
141
|
+
span.attributes[`${constants.LANGSMITH_METADATA}.${metadataKey}`] =
|
|
142
|
+
value;
|
|
143
|
+
delete span.attributes[key];
|
|
144
|
+
}
|
|
143
145
|
}
|
|
144
|
-
if (span.attributes[
|
|
145
|
-
span.attributes[
|
|
146
|
-
span.attributes[
|
|
146
|
+
if (!span.attributes[constants.GENAI_COMPLETION]) {
|
|
147
|
+
if (span.attributes["ai.response.text"]) {
|
|
148
|
+
span.attributes[constants.GENAI_COMPLETION] =
|
|
149
|
+
span.attributes["ai.response.text"];
|
|
150
|
+
}
|
|
151
|
+
if (span.attributes["ai.response.choices"]) {
|
|
152
|
+
span.attributes[constants.GENAI_COMPLETION] =
|
|
153
|
+
span.attributes["ai.response.choices"];
|
|
154
|
+
}
|
|
155
|
+
if (span.attributes["ai.response.object"]) {
|
|
156
|
+
span.attributes[constants.GENAI_COMPLETION] =
|
|
157
|
+
span.attributes["ai.response.object"];
|
|
158
|
+
}
|
|
159
|
+
if (span.attributes["ai.response.toolCalls"]) {
|
|
160
|
+
span.attributes[constants.GENAI_COMPLETION] =
|
|
161
|
+
span.attributes["ai.response.toolCalls"];
|
|
162
|
+
}
|
|
163
|
+
if (span.attributes["ai.toolCall.result"]) {
|
|
164
|
+
span.attributes[constants.GENAI_COMPLETION] =
|
|
165
|
+
span.attributes["ai.toolCall.result"];
|
|
166
|
+
}
|
|
147
167
|
}
|
|
148
|
-
if (span.attributes["ai.
|
|
149
|
-
span.attributes[
|
|
150
|
-
|
|
168
|
+
if (typeof span.attributes["ai.operationId"] === "string" &&
|
|
169
|
+
constants.AI_SDK_LLM_OPERATIONS.includes(span.attributes["ai.operationId"])) {
|
|
170
|
+
span.attributes[constants.LANGSMITH_RUN_TYPE] = "llm";
|
|
151
171
|
}
|
|
152
|
-
if (span.attributes["ai.
|
|
153
|
-
span.attributes[
|
|
154
|
-
|
|
172
|
+
else if (typeof span.attributes["ai.operationId"] === "string" &&
|
|
173
|
+
constants.AI_SDK_TOOL_OPERATIONS.includes(span.attributes["ai.operationId"])) {
|
|
174
|
+
span.attributes[constants.LANGSMITH_RUN_TYPE] = "tool";
|
|
175
|
+
if (span.attributes["ai.toolCall.name"]) {
|
|
176
|
+
span.attributes[constants.LANGSMITH_NAME] =
|
|
177
|
+
span.attributes["ai.toolCall.name"];
|
|
178
|
+
}
|
|
155
179
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}
|
|
161
|
-
else if (typeof span.attributes["ai.operationId"] === "string" &&
|
|
162
|
-
constants.AI_SDK_TOOL_OPERATIONS.includes(span.attributes["ai.operationId"])) {
|
|
163
|
-
span.attributes[constants.LANGSMITH_RUN_TYPE] = "tool";
|
|
164
|
-
if (span.attributes["ai.toolCall.name"]) {
|
|
165
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
166
|
-
span.name = span.attributes["ai.toolCall.name"];
|
|
180
|
+
if (span.attributes[`${constants.LANGSMITH_METADATA}.ls_run_name`]) {
|
|
181
|
+
span.attributes[constants.LANGSMITH_NAME] =
|
|
182
|
+
span.attributes[`${constants.LANGSMITH_METADATA}.ls_run_name`];
|
|
183
|
+
delete span.attributes[`${constants.LANGSMITH_METADATA}.ls_run_name`];
|
|
167
184
|
}
|
|
168
185
|
}
|
|
169
|
-
|
|
170
|
-
|
|
186
|
+
super.export(spans, resultCallback);
|
|
187
|
+
};
|
|
188
|
+
void runExport();
|
|
171
189
|
}
|
|
172
190
|
}
|
|
173
191
|
exports.LangSmithOTLPTraceExporter = LangSmithOTLPTraceExporter;
|
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
|
|
2
2
|
import { ReadableSpan } from "@opentelemetry/sdk-trace-base";
|
|
3
|
+
export type LangSmithOTLPTraceExporterConfig = ConstructorParameters<typeof OTLPTraceExporter>[0] & {
|
|
4
|
+
/**
|
|
5
|
+
* A function that takes an exported span and returns a transformed version of it.
|
|
6
|
+
* May be used to add or remove attributes from the span.
|
|
7
|
+
*
|
|
8
|
+
* For example, to add a custom attribute to the span, you can do:
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { LangSmithOTLPTraceExporter } from "langsmith/experimental/otel/exporter";
|
|
12
|
+
*
|
|
13
|
+
* const exporter = new LangSmithOTLPTraceExporter({
|
|
14
|
+
* transformExportedSpan: (span) => {
|
|
15
|
+
* if (span.name === "foo") {
|
|
16
|
+
* span.attributes["langsmith.metadata.bar"] = "baz";
|
|
17
|
+
* }
|
|
18
|
+
* return span;
|
|
19
|
+
* }
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @param span - The span to transform.
|
|
24
|
+
* @returns A transformed version of the span.
|
|
25
|
+
*/
|
|
26
|
+
transformExportedSpan?: (span: ReadableSpan) => ReadableSpan | Promise<ReadableSpan>;
|
|
27
|
+
};
|
|
3
28
|
/**
|
|
4
29
|
* LangSmith OpenTelemetry trace exporter that extends the standard OTLP trace exporter
|
|
5
30
|
* with LangSmith-specific configuration and span attribute transformations.
|
|
@@ -16,6 +41,7 @@ import { ReadableSpan } from "@opentelemetry/sdk-trace-base";
|
|
|
16
41
|
* Any provided config will override these defaults.
|
|
17
42
|
*/
|
|
18
43
|
export declare class LangSmithOTLPTraceExporter extends OTLPTraceExporter {
|
|
19
|
-
|
|
44
|
+
private transformExportedSpan?;
|
|
45
|
+
constructor(config?: LangSmithOTLPTraceExporterConfig);
|
|
20
46
|
export(spans: ReadableSpan[], resultCallback: Parameters<OTLPTraceExporter["export"]>[1]): void;
|
|
21
47
|
}
|
|
@@ -34,10 +34,10 @@ function parseHeadersString(headersStr) {
|
|
|
34
34
|
*/
|
|
35
35
|
export class LangSmithOTLPTraceExporter extends OTLPTraceExporter {
|
|
36
36
|
constructor(config) {
|
|
37
|
-
const
|
|
37
|
+
const defaultLsEndpoint = getEnvironmentVariable("OTEL_EXPORTER_OTLP_ENDPOINT") ||
|
|
38
38
|
getLangSmithEnvironmentVariable("ENDPOINT") ||
|
|
39
39
|
"https://api.smith.langchain.com";
|
|
40
|
-
const defaultBaseUrl =
|
|
40
|
+
const defaultBaseUrl = defaultLsEndpoint.replace(/\/$/, "");
|
|
41
41
|
const defaultUrl = `${defaultBaseUrl}/otel/v1/traces`;
|
|
42
42
|
// Configure headers with API key and project if available
|
|
43
43
|
let defaultHeaderString = getEnvironmentVariable("OTEL_EXPORTER_OTLP_HEADERS") ?? "";
|
|
@@ -56,81 +56,99 @@ export class LangSmithOTLPTraceExporter extends OTLPTraceExporter {
|
|
|
56
56
|
headers: parseHeadersString(defaultHeaderString),
|
|
57
57
|
...config,
|
|
58
58
|
});
|
|
59
|
+
Object.defineProperty(this, "transformExportedSpan", {
|
|
60
|
+
enumerable: true,
|
|
61
|
+
configurable: true,
|
|
62
|
+
writable: true,
|
|
63
|
+
value: void 0
|
|
64
|
+
});
|
|
65
|
+
this.transformExportedSpan = config?.transformExportedSpan;
|
|
59
66
|
}
|
|
60
67
|
export(spans, resultCallback) {
|
|
61
68
|
if (!isTracingEnabled()) {
|
|
62
69
|
return resultCallback({ code: 0 });
|
|
63
70
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (
|
|
67
|
-
span
|
|
68
|
-
span.attributes["ai.prompt"];
|
|
71
|
+
const runExport = async () => {
|
|
72
|
+
for (let span of spans) {
|
|
73
|
+
if (this.transformExportedSpan) {
|
|
74
|
+
span = await this.transformExportedSpan(span);
|
|
69
75
|
}
|
|
70
|
-
if (span.attributes[
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
messages = JSON.parse(span.attributes["ai.prompt.messages"]);
|
|
76
|
+
if (!span.attributes[constants.GENAI_PROMPT]) {
|
|
77
|
+
if (span.attributes["ai.prompt"]) {
|
|
78
|
+
span.attributes[constants.GENAI_PROMPT] =
|
|
79
|
+
span.attributes["ai.prompt"];
|
|
75
80
|
}
|
|
76
|
-
|
|
77
|
-
|
|
81
|
+
if (span.attributes["ai.prompt.messages"] &&
|
|
82
|
+
typeof span.attributes["ai.prompt.messages"] === "string") {
|
|
83
|
+
let messages;
|
|
84
|
+
try {
|
|
85
|
+
messages = JSON.parse(span.attributes["ai.prompt.messages"]);
|
|
86
|
+
}
|
|
87
|
+
catch (e) {
|
|
88
|
+
console.error("Failed to parse ai.prompt.messages", e);
|
|
89
|
+
}
|
|
90
|
+
if (messages && Array.isArray(messages)) {
|
|
91
|
+
span.attributes[constants.GENAI_PROMPT] = JSON.stringify({
|
|
92
|
+
input: messages,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
78
95
|
}
|
|
79
|
-
if (
|
|
80
|
-
span.attributes[constants.GENAI_PROMPT] =
|
|
81
|
-
|
|
82
|
-
});
|
|
96
|
+
if (span.attributes["ai.toolCall.args"]) {
|
|
97
|
+
span.attributes[constants.GENAI_PROMPT] =
|
|
98
|
+
span.attributes["ai.toolCall.args"];
|
|
83
99
|
}
|
|
84
100
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const metadataKey = key.replace("ai.telemetry.metadata.", "");
|
|
94
|
-
span.attributes[`${constants.LANGSMITH_METADATA}.${metadataKey}`] =
|
|
95
|
-
value;
|
|
96
|
-
delete span.attributes[key];
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
if (!span.attributes[constants.GENAI_COMPLETION]) {
|
|
100
|
-
if (span.attributes["ai.response.text"]) {
|
|
101
|
-
span.attributes[constants.GENAI_COMPLETION] =
|
|
102
|
-
span.attributes["ai.response.text"];
|
|
103
|
-
}
|
|
104
|
-
if (span.attributes["ai.response.choices"]) {
|
|
105
|
-
span.attributes[constants.GENAI_COMPLETION] =
|
|
106
|
-
span.attributes["ai.response.choices"];
|
|
101
|
+
// Iterate over all attributes starting with "ai.telemetry.metadata"
|
|
102
|
+
for (const [key, value] of Object.entries(span.attributes)) {
|
|
103
|
+
if (key.startsWith("ai.telemetry.metadata.")) {
|
|
104
|
+
const metadataKey = key.replace("ai.telemetry.metadata.", "");
|
|
105
|
+
span.attributes[`${constants.LANGSMITH_METADATA}.${metadataKey}`] =
|
|
106
|
+
value;
|
|
107
|
+
delete span.attributes[key];
|
|
108
|
+
}
|
|
107
109
|
}
|
|
108
|
-
if (span.attributes[
|
|
109
|
-
span.attributes[
|
|
110
|
-
span.attributes[
|
|
110
|
+
if (!span.attributes[constants.GENAI_COMPLETION]) {
|
|
111
|
+
if (span.attributes["ai.response.text"]) {
|
|
112
|
+
span.attributes[constants.GENAI_COMPLETION] =
|
|
113
|
+
span.attributes["ai.response.text"];
|
|
114
|
+
}
|
|
115
|
+
if (span.attributes["ai.response.choices"]) {
|
|
116
|
+
span.attributes[constants.GENAI_COMPLETION] =
|
|
117
|
+
span.attributes["ai.response.choices"];
|
|
118
|
+
}
|
|
119
|
+
if (span.attributes["ai.response.object"]) {
|
|
120
|
+
span.attributes[constants.GENAI_COMPLETION] =
|
|
121
|
+
span.attributes["ai.response.object"];
|
|
122
|
+
}
|
|
123
|
+
if (span.attributes["ai.response.toolCalls"]) {
|
|
124
|
+
span.attributes[constants.GENAI_COMPLETION] =
|
|
125
|
+
span.attributes["ai.response.toolCalls"];
|
|
126
|
+
}
|
|
127
|
+
if (span.attributes["ai.toolCall.result"]) {
|
|
128
|
+
span.attributes[constants.GENAI_COMPLETION] =
|
|
129
|
+
span.attributes["ai.toolCall.result"];
|
|
130
|
+
}
|
|
111
131
|
}
|
|
112
|
-
if (span.attributes["ai.
|
|
113
|
-
span.attributes[
|
|
114
|
-
|
|
132
|
+
if (typeof span.attributes["ai.operationId"] === "string" &&
|
|
133
|
+
constants.AI_SDK_LLM_OPERATIONS.includes(span.attributes["ai.operationId"])) {
|
|
134
|
+
span.attributes[constants.LANGSMITH_RUN_TYPE] = "llm";
|
|
115
135
|
}
|
|
116
|
-
if (span.attributes["ai.
|
|
117
|
-
span.attributes[
|
|
118
|
-
|
|
136
|
+
else if (typeof span.attributes["ai.operationId"] === "string" &&
|
|
137
|
+
constants.AI_SDK_TOOL_OPERATIONS.includes(span.attributes["ai.operationId"])) {
|
|
138
|
+
span.attributes[constants.LANGSMITH_RUN_TYPE] = "tool";
|
|
139
|
+
if (span.attributes["ai.toolCall.name"]) {
|
|
140
|
+
span.attributes[constants.LANGSMITH_NAME] =
|
|
141
|
+
span.attributes["ai.toolCall.name"];
|
|
142
|
+
}
|
|
119
143
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
else if (typeof span.attributes["ai.operationId"] === "string" &&
|
|
126
|
-
constants.AI_SDK_TOOL_OPERATIONS.includes(span.attributes["ai.operationId"])) {
|
|
127
|
-
span.attributes[constants.LANGSMITH_RUN_TYPE] = "tool";
|
|
128
|
-
if (span.attributes["ai.toolCall.name"]) {
|
|
129
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
130
|
-
span.name = span.attributes["ai.toolCall.name"];
|
|
144
|
+
if (span.attributes[`${constants.LANGSMITH_METADATA}.ls_run_name`]) {
|
|
145
|
+
span.attributes[constants.LANGSMITH_NAME] =
|
|
146
|
+
span.attributes[`${constants.LANGSMITH_METADATA}.ls_run_name`];
|
|
147
|
+
delete span.attributes[`${constants.LANGSMITH_METADATA}.ls_run_name`];
|
|
131
148
|
}
|
|
132
149
|
}
|
|
133
|
-
|
|
134
|
-
|
|
150
|
+
super.export(spans, resultCallback);
|
|
151
|
+
};
|
|
152
|
+
void runExport();
|
|
135
153
|
}
|
|
136
154
|
}
|
|
@@ -40,7 +40,8 @@ const otel_js_1 = require("../../singletons/otel.cjs");
|
|
|
40
40
|
* initializeOTEL({ globalTracerProvider: customProvider });
|
|
41
41
|
* ```
|
|
42
42
|
*/
|
|
43
|
-
const initializeOTEL = (
|
|
43
|
+
const initializeOTEL = (config = {}) => {
|
|
44
|
+
const { globalTracerProvider, globalContextManager, exporterConfig } = config;
|
|
44
45
|
const otel = {
|
|
45
46
|
trace: api_1.trace,
|
|
46
47
|
context: api_1.context,
|
|
@@ -51,7 +52,7 @@ const initializeOTEL = ({ globalTracerProvider, globalContextManager, } = {}) =>
|
|
|
51
52
|
contextManager.enable();
|
|
52
53
|
api_1.context.setGlobalContextManager(contextManager);
|
|
53
54
|
}
|
|
54
|
-
const DEFAULT_LANGSMITH_SPAN_EXPORTER = new exporter_js_1.LangSmithOTLPTraceExporter(
|
|
55
|
+
const DEFAULT_LANGSMITH_SPAN_EXPORTER = new exporter_js_1.LangSmithOTLPTraceExporter(exporterConfig);
|
|
55
56
|
const DEFAULT_LANGSMITH_SPAN_PROCESSOR = new sdk_trace_base_1.BatchSpanProcessor(DEFAULT_LANGSMITH_SPAN_EXPORTER);
|
|
56
57
|
if (!globalTracerProvider) {
|
|
57
58
|
const DEFAULT_LANGSMITH_TRACER_PROVIDER = new sdk_trace_base_1.BasicTracerProvider({
|
|
@@ -1,6 +1,25 @@
|
|
|
1
1
|
import { type TracerProvider, type ContextManager } from "@opentelemetry/api";
|
|
2
2
|
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
3
|
-
import { LangSmithOTLPTraceExporter } from "./exporter.js";
|
|
3
|
+
import { LangSmithOTLPTraceExporter, LangSmithOTLPTraceExporterConfig } from "./exporter.js";
|
|
4
|
+
/**
|
|
5
|
+
* Configuration options for initializing OpenTelemetry with LangSmith.
|
|
6
|
+
*/
|
|
7
|
+
export type InitializeOTELConfig = {
|
|
8
|
+
/**
|
|
9
|
+
* Optional custom OTEL TracerProvider to use instead of
|
|
10
|
+
* creating and globally setting a new one.
|
|
11
|
+
*/
|
|
12
|
+
globalTracerProvider?: TracerProvider;
|
|
13
|
+
/**
|
|
14
|
+
* Optional custom OTEL ContextManager to use instead of
|
|
15
|
+
* creating and globally setting a new one with AsyncHooksContextManager.
|
|
16
|
+
*/
|
|
17
|
+
globalContextManager?: ContextManager;
|
|
18
|
+
/**
|
|
19
|
+
* Optional configuration passed to the default LangSmith OTLP trace exporter.
|
|
20
|
+
*/
|
|
21
|
+
exporterConfig?: LangSmithOTLPTraceExporterConfig;
|
|
22
|
+
};
|
|
4
23
|
/**
|
|
5
24
|
* Initializes OpenTelemetry with LangSmith-specific configuration for tracing.
|
|
6
25
|
*
|
|
@@ -32,10 +51,7 @@ import { LangSmithOTLPTraceExporter } from "./exporter.js";
|
|
|
32
51
|
* initializeOTEL({ globalTracerProvider: customProvider });
|
|
33
52
|
* ```
|
|
34
53
|
*/
|
|
35
|
-
export declare const initializeOTEL: (
|
|
36
|
-
globalTracerProvider?: TracerProvider;
|
|
37
|
-
globalContextManager?: ContextManager;
|
|
38
|
-
}) => {
|
|
54
|
+
export declare const initializeOTEL: (config?: InitializeOTELConfig) => {
|
|
39
55
|
DEFAULT_LANGSMITH_TRACER_PROVIDER: TracerProvider;
|
|
40
56
|
DEFAULT_LANGSMITH_SPAN_PROCESSOR: BatchSpanProcessor;
|
|
41
57
|
DEFAULT_LANGSMITH_SPAN_EXPORTER: LangSmithOTLPTraceExporter;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { AsyncHooksContextManager } from "@opentelemetry/context-async-hooks";
|
|
5
5
|
import { trace as otel_trace, context as otel_context, } from "@opentelemetry/api";
|
|
6
6
|
import { BatchSpanProcessor, BasicTracerProvider, } from "@opentelemetry/sdk-trace-base";
|
|
7
|
-
import { LangSmithOTLPTraceExporter } from "./exporter.js";
|
|
7
|
+
import { LangSmithOTLPTraceExporter, } from "./exporter.js";
|
|
8
8
|
import { setDefaultOTLPTracerComponents, setOTELInstances, } from "../../singletons/otel.js";
|
|
9
9
|
/**
|
|
10
10
|
* Initializes OpenTelemetry with LangSmith-specific configuration for tracing.
|
|
@@ -37,7 +37,8 @@ import { setDefaultOTLPTracerComponents, setOTELInstances, } from "../../singlet
|
|
|
37
37
|
* initializeOTEL({ globalTracerProvider: customProvider });
|
|
38
38
|
* ```
|
|
39
39
|
*/
|
|
40
|
-
export const initializeOTEL = (
|
|
40
|
+
export const initializeOTEL = (config = {}) => {
|
|
41
|
+
const { globalTracerProvider, globalContextManager, exporterConfig } = config;
|
|
41
42
|
const otel = {
|
|
42
43
|
trace: otel_trace,
|
|
43
44
|
context: otel_context,
|
|
@@ -48,7 +49,7 @@ export const initializeOTEL = ({ globalTracerProvider, globalContextManager, } =
|
|
|
48
49
|
contextManager.enable();
|
|
49
50
|
otel_context.setGlobalContextManager(contextManager);
|
|
50
51
|
}
|
|
51
|
-
const DEFAULT_LANGSMITH_SPAN_EXPORTER = new LangSmithOTLPTraceExporter(
|
|
52
|
+
const DEFAULT_LANGSMITH_SPAN_EXPORTER = new LangSmithOTLPTraceExporter(exporterConfig);
|
|
52
53
|
const DEFAULT_LANGSMITH_SPAN_PROCESSOR = new BatchSpanProcessor(DEFAULT_LANGSMITH_SPAN_EXPORTER);
|
|
53
54
|
if (!globalTracerProvider) {
|
|
54
55
|
const DEFAULT_LANGSMITH_TRACER_PROVIDER = new BasicTracerProvider({
|
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.
|
|
13
|
+
exports.__version__ = "0.3.38";
|
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.
|
|
6
|
+
export declare const __version__ = "0.3.38";
|
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.
|
|
6
|
+
export const __version__ = "0.3.38";
|
|
@@ -4,7 +4,6 @@ exports.ROOT = exports.AsyncLocalStorageProviderSingleton = void 0;
|
|
|
4
4
|
exports.getCurrentRunTree = getCurrentRunTree;
|
|
5
5
|
exports.withRunTree = withRunTree;
|
|
6
6
|
exports.isTraceableFunction = isTraceableFunction;
|
|
7
|
-
const run_trees_js_1 = require("../run_trees.cjs");
|
|
8
7
|
class MockAsyncLocalStorage {
|
|
9
8
|
getStore() {
|
|
10
9
|
return undefined;
|
|
@@ -31,7 +30,7 @@ class AsyncLocalStorageProvider {
|
|
|
31
30
|
exports.AsyncLocalStorageProviderSingleton = new AsyncLocalStorageProvider();
|
|
32
31
|
function getCurrentRunTree(permitAbsentRunTree = false) {
|
|
33
32
|
const runTree = exports.AsyncLocalStorageProviderSingleton.getInstance().getStore();
|
|
34
|
-
if (!permitAbsentRunTree &&
|
|
33
|
+
if (!permitAbsentRunTree && runTree === undefined) {
|
|
35
34
|
throw new Error("Could not get the current run tree.\n\nPlease make sure you are calling this method within a traceable function and that tracing is enabled.");
|
|
36
35
|
}
|
|
37
36
|
return runTree;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { RunTree } from "../run_trees.js";
|
|
2
|
-
import { TraceableFunction } from "./types.js";
|
|
1
|
+
import type { RunTree } from "../run_trees.js";
|
|
2
|
+
import type { ContextPlaceholder, TraceableFunction } from "./types.js";
|
|
3
3
|
interface AsyncLocalStorageInterface {
|
|
4
|
-
getStore: () => RunTree | undefined;
|
|
5
|
-
run: (context: RunTree | undefined, fn: () => void) => void;
|
|
4
|
+
getStore: () => RunTree | ContextPlaceholder | undefined;
|
|
5
|
+
run: (context: RunTree | ContextPlaceholder | undefined, fn: () => void) => void;
|
|
6
6
|
}
|
|
7
7
|
declare class AsyncLocalStorageProvider {
|
|
8
8
|
getInstance(): AsyncLocalStorageInterface;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { isRunTree } from "../run_trees.js";
|
|
2
1
|
class MockAsyncLocalStorage {
|
|
3
2
|
getStore() {
|
|
4
3
|
return undefined;
|
|
@@ -25,7 +24,7 @@ class AsyncLocalStorageProvider {
|
|
|
25
24
|
export const AsyncLocalStorageProviderSingleton = new AsyncLocalStorageProvider();
|
|
26
25
|
export function getCurrentRunTree(permitAbsentRunTree = false) {
|
|
27
26
|
const runTree = AsyncLocalStorageProviderSingleton.getInstance().getStore();
|
|
28
|
-
if (!permitAbsentRunTree &&
|
|
27
|
+
if (!permitAbsentRunTree && runTree === undefined) {
|
|
29
28
|
throw new Error("Could not get the current run tree.\n\nPlease make sure you are calling this method within a traceable function and that tracing is enabled.");
|
|
30
29
|
}
|
|
31
30
|
return runTree;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { RunTree, RunnableConfigLike } from "../run_trees.js";
|
|
2
2
|
import { ROOT } from "./traceable.js";
|
|
3
|
+
import { _LC_CONTEXT_VARIABLES_KEY } from "./constants.js";
|
|
3
4
|
type SmartPromise<T> = T extends AsyncGenerator ? T : T extends Promise<unknown> ? T : Promise<T>;
|
|
4
5
|
type WrapArgReturnPair<Pair> = Pair extends [
|
|
5
6
|
infer Args extends any[],
|
|
@@ -37,4 +38,7 @@ export type TraceableFunction<Func extends (...args: any[]) => any> = (Func exte
|
|
|
37
38
|
[K in keyof Func]: Func[K];
|
|
38
39
|
};
|
|
39
40
|
export type RunTreeLike = RunTree;
|
|
41
|
+
export type ContextPlaceholder = {
|
|
42
|
+
[_LC_CONTEXT_VARIABLES_KEY]?: Record<string, unknown>;
|
|
43
|
+
};
|
|
40
44
|
export {};
|
package/dist/singletons/types.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
import { _LC_CONTEXT_VARIABLES_KEY } from "./constants.js";
|
package/dist/traceable.cjs
CHANGED
|
@@ -143,7 +143,7 @@ const handleRunAttachments = (rawInputs, extractAttachments) => {
|
|
|
143
143
|
};
|
|
144
144
|
const getTracingRunTree = (runTree, inputs, getInvocationParams, processInputs, extractAttachments) => {
|
|
145
145
|
if (!(0, env_js_1.isTracingEnabled)(runTree.tracingEnabled)) {
|
|
146
|
-
return
|
|
146
|
+
return {};
|
|
147
147
|
}
|
|
148
148
|
const [attached, args] = handleRunAttachments(inputs, extractAttachments);
|
|
149
149
|
runTree.attachments = attached;
|
|
@@ -364,7 +364,7 @@ function traceable(wrappedFunc, config) {
|
|
|
364
364
|
for (let i = 0; i < processedArgs.length; i++) {
|
|
365
365
|
processedArgs[i] = convertSerializableArg(processedArgs[i]);
|
|
366
366
|
}
|
|
367
|
-
const [
|
|
367
|
+
const [currentContext, rawInputs] = (() => {
|
|
368
368
|
const [firstArg, ...restArgs] = processedArgs;
|
|
369
369
|
// used for handoff between LangChain.JS and traceable functions
|
|
370
370
|
if ((0, run_trees_js_1.isRunnableConfigLike)(firstArg)) {
|
|
@@ -391,24 +391,32 @@ function traceable(wrappedFunc, config) {
|
|
|
391
391
|
// Node.JS uses AsyncLocalStorage (ALS) and AsyncResource
|
|
392
392
|
// to allow storing context
|
|
393
393
|
const prevRunFromStore = asyncLocalStorage.getStore();
|
|
394
|
-
|
|
395
|
-
return [
|
|
396
|
-
getTracingRunTree(prevRunFromStore.createChild(ensuredConfig), processedArgs, config?.getInvocationParams, processInputsFn, extractAttachmentsFn),
|
|
397
|
-
processedArgs,
|
|
398
|
-
];
|
|
399
|
-
}
|
|
400
|
-
const currentRunTree = getTracingRunTree(new run_trees_js_1.RunTree(ensuredConfig), processedArgs, config?.getInvocationParams, processInputsFn, extractAttachmentsFn);
|
|
394
|
+
let lc_contextVars;
|
|
401
395
|
// If a context var is set by LangChain outside of a traceable,
|
|
402
396
|
// it will be an object with a single property and we should copy
|
|
403
397
|
// context vars over into the new run tree.
|
|
404
398
|
if (prevRunFromStore !== undefined &&
|
|
405
399
|
constants_js_1._LC_CONTEXT_VARIABLES_KEY in prevRunFromStore) {
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
400
|
+
lc_contextVars = prevRunFromStore[constants_js_1._LC_CONTEXT_VARIABLES_KEY];
|
|
401
|
+
}
|
|
402
|
+
if ((0, run_trees_js_1.isRunTree)(prevRunFromStore)) {
|
|
403
|
+
const currentRunTree = getTracingRunTree(prevRunFromStore.createChild(ensuredConfig), processedArgs, config?.getInvocationParams, processInputsFn, extractAttachmentsFn);
|
|
404
|
+
if (lc_contextVars) {
|
|
405
|
+
(currentRunTree ?? {})[constants_js_1._LC_CONTEXT_VARIABLES_KEY] =
|
|
406
|
+
lc_contextVars;
|
|
407
|
+
}
|
|
408
|
+
return [currentRunTree, processedArgs];
|
|
409
|
+
}
|
|
410
|
+
const currentRunTree = getTracingRunTree(new run_trees_js_1.RunTree(ensuredConfig), processedArgs, config?.getInvocationParams, processInputsFn, extractAttachmentsFn);
|
|
411
|
+
if (lc_contextVars) {
|
|
412
|
+
(currentRunTree ?? {})[constants_js_1._LC_CONTEXT_VARIABLES_KEY] =
|
|
413
|
+
lc_contextVars;
|
|
409
414
|
}
|
|
410
415
|
return [currentRunTree, processedArgs];
|
|
411
416
|
})();
|
|
417
|
+
const currentRunTree = (0, run_trees_js_1.isRunTree)(currentContext)
|
|
418
|
+
? currentContext
|
|
419
|
+
: undefined;
|
|
412
420
|
const otelContextManager = maybeCreateOtelContext(currentRunTree, config?.tracer);
|
|
413
421
|
const otel_context = (0, otel_js_1.getOTELContext)();
|
|
414
422
|
const runWithContext = () => {
|
|
@@ -648,10 +656,10 @@ function traceable(wrappedFunc, config) {
|
|
|
648
656
|
};
|
|
649
657
|
// Wrap with OTEL context if available, similar to Python's implementation
|
|
650
658
|
if (otelContextManager) {
|
|
651
|
-
return asyncLocalStorage.run(
|
|
659
|
+
return asyncLocalStorage.run(currentContext, () => otelContextManager(runWithContext));
|
|
652
660
|
}
|
|
653
661
|
else {
|
|
654
|
-
return asyncLocalStorage.run(
|
|
662
|
+
return asyncLocalStorage.run(currentContext, runWithContext);
|
|
655
663
|
}
|
|
656
664
|
};
|
|
657
665
|
Object.defineProperty(traceableFunc, "langsmith:traceable", {
|
package/dist/traceable.d.ts
CHANGED
|
@@ -1,22 +1,8 @@
|
|
|
1
1
|
import { RunTreeConfig } from "./run_trees.js";
|
|
2
2
|
import { Attachments, InvocationParamsSchema, KVMap } from "./schemas.js";
|
|
3
|
-
import { TraceableFunction } from "./singletons/types.js";
|
|
3
|
+
import type { TraceableFunction } from "./singletons/types.js";
|
|
4
4
|
import { OTELTracer } from "./experimental/otel/types.js";
|
|
5
|
-
|
|
6
|
-
* Higher-order function that takes function as input and returns a
|
|
7
|
-
* "TraceableFunction" - a wrapped version of the input that
|
|
8
|
-
* automatically handles tracing. If the returned traceable function calls any
|
|
9
|
-
* traceable functions, those are automatically traced as well.
|
|
10
|
-
*
|
|
11
|
-
* The returned TraceableFunction can accept a run tree or run tree config as
|
|
12
|
-
* its first argument. If omitted, it will default to the caller's run tree,
|
|
13
|
-
* or will be treated as a root run.
|
|
14
|
-
*
|
|
15
|
-
* @param wrappedFunc Targeted function to be traced
|
|
16
|
-
* @param config Additional metadata such as name, tags or providing
|
|
17
|
-
* a custom LangSmith client instance
|
|
18
|
-
*/
|
|
19
|
-
export declare function traceable<Func extends (...args: any[]) => any>(wrappedFunc: Func, config?: Partial<Omit<RunTreeConfig, "inputs" | "outputs">> & {
|
|
5
|
+
export type TraceableConfig<Func extends (...args: any[]) => any> = Partial<Omit<RunTreeConfig, "inputs" | "outputs">> & {
|
|
20
6
|
aggregator?: (args: any[]) => any;
|
|
21
7
|
argsConfigPath?: [number] | [number, string];
|
|
22
8
|
tracer?: OTELTracer;
|
|
@@ -54,6 +40,21 @@ export declare function traceable<Func extends (...args: any[]) => any>(wrappedF
|
|
|
54
40
|
* @returns Transformed key-value map
|
|
55
41
|
*/
|
|
56
42
|
processOutputs?: (outputs: Readonly<KVMap>) => KVMap;
|
|
57
|
-
}
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Higher-order function that takes function as input and returns a
|
|
46
|
+
* "TraceableFunction" - a wrapped version of the input that
|
|
47
|
+
* automatically handles tracing. If the returned traceable function calls any
|
|
48
|
+
* traceable functions, those are automatically traced as well.
|
|
49
|
+
*
|
|
50
|
+
* The returned TraceableFunction can accept a run tree or run tree config as
|
|
51
|
+
* its first argument. If omitted, it will default to the caller's run tree,
|
|
52
|
+
* or will be treated as a root run.
|
|
53
|
+
*
|
|
54
|
+
* @param wrappedFunc Targeted function to be traced
|
|
55
|
+
* @param config Additional metadata such as name, tags or providing
|
|
56
|
+
* a custom LangSmith client instance
|
|
57
|
+
*/
|
|
58
|
+
export declare function traceable<Func extends (...args: any[]) => any>(wrappedFunc: Func, config?: TraceableConfig<Func>): TraceableFunction<Func>;
|
|
58
59
|
export { getCurrentRunTree, isTraceableFunction, withRunTree, ROOT, } from "./singletons/traceable.js";
|
|
59
60
|
export type { RunTreeLike, TraceableFunction } from "./singletons/types.js";
|
package/dist/traceable.js
CHANGED
|
@@ -139,7 +139,7 @@ const handleRunAttachments = (rawInputs, extractAttachments) => {
|
|
|
139
139
|
};
|
|
140
140
|
const getTracingRunTree = (runTree, inputs, getInvocationParams, processInputs, extractAttachments) => {
|
|
141
141
|
if (!isTracingEnabled(runTree.tracingEnabled)) {
|
|
142
|
-
return
|
|
142
|
+
return {};
|
|
143
143
|
}
|
|
144
144
|
const [attached, args] = handleRunAttachments(inputs, extractAttachments);
|
|
145
145
|
runTree.attachments = attached;
|
|
@@ -360,7 +360,7 @@ export function traceable(wrappedFunc, config) {
|
|
|
360
360
|
for (let i = 0; i < processedArgs.length; i++) {
|
|
361
361
|
processedArgs[i] = convertSerializableArg(processedArgs[i]);
|
|
362
362
|
}
|
|
363
|
-
const [
|
|
363
|
+
const [currentContext, rawInputs] = (() => {
|
|
364
364
|
const [firstArg, ...restArgs] = processedArgs;
|
|
365
365
|
// used for handoff between LangChain.JS and traceable functions
|
|
366
366
|
if (isRunnableConfigLike(firstArg)) {
|
|
@@ -387,24 +387,32 @@ export function traceable(wrappedFunc, config) {
|
|
|
387
387
|
// Node.JS uses AsyncLocalStorage (ALS) and AsyncResource
|
|
388
388
|
// to allow storing context
|
|
389
389
|
const prevRunFromStore = asyncLocalStorage.getStore();
|
|
390
|
-
|
|
391
|
-
return [
|
|
392
|
-
getTracingRunTree(prevRunFromStore.createChild(ensuredConfig), processedArgs, config?.getInvocationParams, processInputsFn, extractAttachmentsFn),
|
|
393
|
-
processedArgs,
|
|
394
|
-
];
|
|
395
|
-
}
|
|
396
|
-
const currentRunTree = getTracingRunTree(new RunTree(ensuredConfig), processedArgs, config?.getInvocationParams, processInputsFn, extractAttachmentsFn);
|
|
390
|
+
let lc_contextVars;
|
|
397
391
|
// If a context var is set by LangChain outside of a traceable,
|
|
398
392
|
// it will be an object with a single property and we should copy
|
|
399
393
|
// context vars over into the new run tree.
|
|
400
394
|
if (prevRunFromStore !== undefined &&
|
|
401
395
|
_LC_CONTEXT_VARIABLES_KEY in prevRunFromStore) {
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
396
|
+
lc_contextVars = prevRunFromStore[_LC_CONTEXT_VARIABLES_KEY];
|
|
397
|
+
}
|
|
398
|
+
if (isRunTree(prevRunFromStore)) {
|
|
399
|
+
const currentRunTree = getTracingRunTree(prevRunFromStore.createChild(ensuredConfig), processedArgs, config?.getInvocationParams, processInputsFn, extractAttachmentsFn);
|
|
400
|
+
if (lc_contextVars) {
|
|
401
|
+
(currentRunTree ?? {})[_LC_CONTEXT_VARIABLES_KEY] =
|
|
402
|
+
lc_contextVars;
|
|
403
|
+
}
|
|
404
|
+
return [currentRunTree, processedArgs];
|
|
405
|
+
}
|
|
406
|
+
const currentRunTree = getTracingRunTree(new RunTree(ensuredConfig), processedArgs, config?.getInvocationParams, processInputsFn, extractAttachmentsFn);
|
|
407
|
+
if (lc_contextVars) {
|
|
408
|
+
(currentRunTree ?? {})[_LC_CONTEXT_VARIABLES_KEY] =
|
|
409
|
+
lc_contextVars;
|
|
405
410
|
}
|
|
406
411
|
return [currentRunTree, processedArgs];
|
|
407
412
|
})();
|
|
413
|
+
const currentRunTree = isRunTree(currentContext)
|
|
414
|
+
? currentContext
|
|
415
|
+
: undefined;
|
|
408
416
|
const otelContextManager = maybeCreateOtelContext(currentRunTree, config?.tracer);
|
|
409
417
|
const otel_context = getOTELContext();
|
|
410
418
|
const runWithContext = () => {
|
|
@@ -644,10 +652,10 @@ export function traceable(wrappedFunc, config) {
|
|
|
644
652
|
};
|
|
645
653
|
// Wrap with OTEL context if available, similar to Python's implementation
|
|
646
654
|
if (otelContextManager) {
|
|
647
|
-
return asyncLocalStorage.run(
|
|
655
|
+
return asyncLocalStorage.run(currentContext, () => otelContextManager(runWithContext));
|
|
648
656
|
}
|
|
649
657
|
else {
|
|
650
|
-
return asyncLocalStorage.run(
|
|
658
|
+
return asyncLocalStorage.run(currentContext, runWithContext);
|
|
651
659
|
}
|
|
652
660
|
};
|
|
653
661
|
Object.defineProperty(traceableFunc, "langsmith:traceable", {
|
package/dist/wrappers/openai.cjs
CHANGED
|
@@ -93,6 +93,19 @@ const chatAggregator = (chunks) => {
|
|
|
93
93
|
aggregatedOutput.choices = Object.values(choicesByIndex).map((choices) => _combineChatCompletionChoices(choices));
|
|
94
94
|
return aggregatedOutput;
|
|
95
95
|
};
|
|
96
|
+
const responsesAggregator = (events) => {
|
|
97
|
+
if (!events || events.length === 0) {
|
|
98
|
+
return {};
|
|
99
|
+
}
|
|
100
|
+
// Find the response.completed event which contains the final response
|
|
101
|
+
for (const event of events) {
|
|
102
|
+
if (event.type === "response.completed" && event.response) {
|
|
103
|
+
return event.response;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// If no completed event found, return the last event
|
|
107
|
+
return events[events.length - 1] || {};
|
|
108
|
+
};
|
|
96
109
|
const textAggregator = (allChunks
|
|
97
110
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
98
111
|
) => {
|
|
@@ -187,74 +200,50 @@ const wrapOpenAI = (openai, options) => {
|
|
|
187
200
|
// Some internal OpenAI methods call each other, so we need to preserve original
|
|
188
201
|
// OpenAI methods.
|
|
189
202
|
const tracedOpenAIClient = { ...openai };
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
},
|
|
222
|
-
...options,
|
|
223
|
-
}),
|
|
224
|
-
},
|
|
225
|
-
},
|
|
226
|
-
};
|
|
203
|
+
const chatCompletionParseMetadata = {
|
|
204
|
+
name: "ChatOpenAI",
|
|
205
|
+
run_type: "llm",
|
|
206
|
+
aggregator: chatAggregator,
|
|
207
|
+
argsConfigPath: [1, "langsmithExtra"],
|
|
208
|
+
getInvocationParams: (payload) => {
|
|
209
|
+
if (typeof payload !== "object" || payload == null)
|
|
210
|
+
return undefined;
|
|
211
|
+
// we can safely do so, as the types are not exported in TSC
|
|
212
|
+
const params = payload;
|
|
213
|
+
const ls_stop = (typeof params.stop === "string" ? [params.stop] : params.stop) ??
|
|
214
|
+
undefined;
|
|
215
|
+
return {
|
|
216
|
+
ls_provider: "openai",
|
|
217
|
+
ls_model_type: "chat",
|
|
218
|
+
ls_model_name: params.model,
|
|
219
|
+
ls_max_tokens: params.max_completion_tokens ?? params.max_tokens ?? undefined,
|
|
220
|
+
ls_temperature: params.temperature ?? undefined,
|
|
221
|
+
ls_stop,
|
|
222
|
+
};
|
|
223
|
+
},
|
|
224
|
+
processOutputs: processChatCompletion,
|
|
225
|
+
...options,
|
|
226
|
+
};
|
|
227
|
+
if (openai.beta) {
|
|
228
|
+
tracedOpenAIClient.beta = openai.beta;
|
|
229
|
+
if (openai.beta.chat &&
|
|
230
|
+
openai.beta.chat.completions &&
|
|
231
|
+
typeof openai.beta.chat.completions.parse === "function") {
|
|
232
|
+
tracedOpenAIClient.beta.chat.completions.parse = (0, traceable_js_1.traceable)(openai.beta.chat.completions.parse.bind(openai.beta.chat.completions), chatCompletionParseMetadata);
|
|
233
|
+
}
|
|
227
234
|
}
|
|
228
235
|
tracedOpenAIClient.chat = {
|
|
229
236
|
...openai.chat,
|
|
230
|
-
completions:
|
|
231
|
-
...openai.chat.completions,
|
|
232
|
-
create: (0, traceable_js_1.traceable)(openai.chat.completions.create.bind(openai.chat.completions), {
|
|
233
|
-
name: "ChatOpenAI",
|
|
234
|
-
run_type: "llm",
|
|
235
|
-
aggregator: chatAggregator,
|
|
236
|
-
argsConfigPath: [1, "langsmithExtra"],
|
|
237
|
-
getInvocationParams: (payload) => {
|
|
238
|
-
if (typeof payload !== "object" || payload == null)
|
|
239
|
-
return undefined;
|
|
240
|
-
// we can safely do so, as the types are not exported in TSC
|
|
241
|
-
const params = payload;
|
|
242
|
-
const ls_stop = (typeof params.stop === "string" ? [params.stop] : params.stop) ??
|
|
243
|
-
undefined;
|
|
244
|
-
return {
|
|
245
|
-
ls_provider: "openai",
|
|
246
|
-
ls_model_type: "chat",
|
|
247
|
-
ls_model_name: params.model,
|
|
248
|
-
ls_max_tokens: params.max_tokens ?? undefined,
|
|
249
|
-
ls_temperature: params.temperature ?? undefined,
|
|
250
|
-
ls_stop,
|
|
251
|
-
};
|
|
252
|
-
},
|
|
253
|
-
processOutputs: processChatCompletion,
|
|
254
|
-
...options,
|
|
255
|
-
}),
|
|
256
|
-
},
|
|
237
|
+
completions: Object.create(Object.getPrototypeOf(openai.chat.completions)),
|
|
257
238
|
};
|
|
239
|
+
// Copy all own properties and then wrap specific methods
|
|
240
|
+
Object.assign(tracedOpenAIClient.chat.completions, openai.chat.completions);
|
|
241
|
+
// Wrap chat.completions.create
|
|
242
|
+
tracedOpenAIClient.chat.completions.create = (0, traceable_js_1.traceable)(openai.chat.completions.create.bind(openai.chat.completions), chatCompletionParseMetadata);
|
|
243
|
+
// Wrap chat.completions.parse if it exists
|
|
244
|
+
if (typeof openai.chat.completions.parse === "function") {
|
|
245
|
+
tracedOpenAIClient.chat.completions.parse = (0, traceable_js_1.traceable)(openai.chat.completions.parse.bind(openai.chat.completions), chatCompletionParseMetadata);
|
|
246
|
+
}
|
|
258
247
|
tracedOpenAIClient.completions = {
|
|
259
248
|
...openai.completions,
|
|
260
249
|
create: (0, traceable_js_1.traceable)(openai.completions.create.bind(openai.completions), {
|
|
@@ -281,6 +270,38 @@ const wrapOpenAI = (openai, options) => {
|
|
|
281
270
|
...options,
|
|
282
271
|
}),
|
|
283
272
|
};
|
|
273
|
+
// Add responses API support if it exists
|
|
274
|
+
if (openai.responses) {
|
|
275
|
+
// Create a new object with the same prototype to preserve all methods
|
|
276
|
+
tracedOpenAIClient.responses = Object.create(Object.getPrototypeOf(openai.responses));
|
|
277
|
+
// Copy all own properties
|
|
278
|
+
if (tracedOpenAIClient.responses) {
|
|
279
|
+
Object.assign(tracedOpenAIClient.responses, openai.responses);
|
|
280
|
+
}
|
|
281
|
+
// Wrap responses.create method
|
|
282
|
+
if (tracedOpenAIClient.responses &&
|
|
283
|
+
typeof tracedOpenAIClient.responses.create === "function") {
|
|
284
|
+
tracedOpenAIClient.responses.create = (0, traceable_js_1.traceable)(openai.responses.create.bind(openai.responses), {
|
|
285
|
+
name: "ChatOpenAI",
|
|
286
|
+
run_type: "llm",
|
|
287
|
+
aggregator: responsesAggregator,
|
|
288
|
+
argsConfigPath: [1, "langsmithExtra"],
|
|
289
|
+
getInvocationParams: (payload) => {
|
|
290
|
+
if (typeof payload !== "object" || payload == null)
|
|
291
|
+
return undefined;
|
|
292
|
+
// Handle responses API parameters
|
|
293
|
+
const params = payload;
|
|
294
|
+
return {
|
|
295
|
+
ls_provider: "openai",
|
|
296
|
+
ls_model_type: "llm",
|
|
297
|
+
ls_model_name: params.model || "unknown",
|
|
298
|
+
};
|
|
299
|
+
},
|
|
300
|
+
processOutputs: processChatCompletion,
|
|
301
|
+
...options,
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
}
|
|
284
305
|
return tracedOpenAIClient;
|
|
285
306
|
};
|
|
286
307
|
exports.wrapOpenAI = wrapOpenAI;
|
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
import { OpenAI } from "openai";
|
|
2
|
-
import type { APIPromise } from "openai
|
|
2
|
+
import type { APIPromise } from "openai";
|
|
3
3
|
import type { RunTreeConfig } from "../index.js";
|
|
4
4
|
type OpenAIType = {
|
|
5
|
-
beta?:
|
|
6
|
-
chat?: {
|
|
7
|
-
completions?: {
|
|
8
|
-
parse?: (...args: any[]) => any;
|
|
9
|
-
};
|
|
10
|
-
};
|
|
11
|
-
};
|
|
5
|
+
beta?: any;
|
|
12
6
|
chat: {
|
|
13
7
|
completions: {
|
|
14
8
|
create: (...args: any[]) => any;
|
|
9
|
+
parse: (...args: any[]) => any;
|
|
15
10
|
};
|
|
16
11
|
};
|
|
17
12
|
completions: {
|
|
18
13
|
create: (...args: any[]) => any;
|
|
19
14
|
};
|
|
15
|
+
responses?: {
|
|
16
|
+
create: (...args: any[]) => any;
|
|
17
|
+
retrieve: (...args: any[]) => any;
|
|
18
|
+
};
|
|
20
19
|
};
|
|
21
20
|
type ExtraRunTreeConfig = Pick<Partial<RunTreeConfig>, "name" | "metadata" | "tags">;
|
|
22
21
|
type PatchedOpenAIClient<T extends OpenAIType> = T & {
|
package/dist/wrappers/openai.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isTraceableFunction, traceable } from "../traceable.js";
|
|
1
|
+
import { isTraceableFunction, traceable, } from "../traceable.js";
|
|
2
2
|
function _combineChatCompletionChoices(choices
|
|
3
3
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4
4
|
) {
|
|
@@ -90,6 +90,19 @@ const chatAggregator = (chunks) => {
|
|
|
90
90
|
aggregatedOutput.choices = Object.values(choicesByIndex).map((choices) => _combineChatCompletionChoices(choices));
|
|
91
91
|
return aggregatedOutput;
|
|
92
92
|
};
|
|
93
|
+
const responsesAggregator = (events) => {
|
|
94
|
+
if (!events || events.length === 0) {
|
|
95
|
+
return {};
|
|
96
|
+
}
|
|
97
|
+
// Find the response.completed event which contains the final response
|
|
98
|
+
for (const event of events) {
|
|
99
|
+
if (event.type === "response.completed" && event.response) {
|
|
100
|
+
return event.response;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// If no completed event found, return the last event
|
|
104
|
+
return events[events.length - 1] || {};
|
|
105
|
+
};
|
|
93
106
|
const textAggregator = (allChunks
|
|
94
107
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
95
108
|
) => {
|
|
@@ -184,74 +197,50 @@ export const wrapOpenAI = (openai, options) => {
|
|
|
184
197
|
// Some internal OpenAI methods call each other, so we need to preserve original
|
|
185
198
|
// OpenAI methods.
|
|
186
199
|
const tracedOpenAIClient = { ...openai };
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
},
|
|
219
|
-
...options,
|
|
220
|
-
}),
|
|
221
|
-
},
|
|
222
|
-
},
|
|
223
|
-
};
|
|
200
|
+
const chatCompletionParseMetadata = {
|
|
201
|
+
name: "ChatOpenAI",
|
|
202
|
+
run_type: "llm",
|
|
203
|
+
aggregator: chatAggregator,
|
|
204
|
+
argsConfigPath: [1, "langsmithExtra"],
|
|
205
|
+
getInvocationParams: (payload) => {
|
|
206
|
+
if (typeof payload !== "object" || payload == null)
|
|
207
|
+
return undefined;
|
|
208
|
+
// we can safely do so, as the types are not exported in TSC
|
|
209
|
+
const params = payload;
|
|
210
|
+
const ls_stop = (typeof params.stop === "string" ? [params.stop] : params.stop) ??
|
|
211
|
+
undefined;
|
|
212
|
+
return {
|
|
213
|
+
ls_provider: "openai",
|
|
214
|
+
ls_model_type: "chat",
|
|
215
|
+
ls_model_name: params.model,
|
|
216
|
+
ls_max_tokens: params.max_completion_tokens ?? params.max_tokens ?? undefined,
|
|
217
|
+
ls_temperature: params.temperature ?? undefined,
|
|
218
|
+
ls_stop,
|
|
219
|
+
};
|
|
220
|
+
},
|
|
221
|
+
processOutputs: processChatCompletion,
|
|
222
|
+
...options,
|
|
223
|
+
};
|
|
224
|
+
if (openai.beta) {
|
|
225
|
+
tracedOpenAIClient.beta = openai.beta;
|
|
226
|
+
if (openai.beta.chat &&
|
|
227
|
+
openai.beta.chat.completions &&
|
|
228
|
+
typeof openai.beta.chat.completions.parse === "function") {
|
|
229
|
+
tracedOpenAIClient.beta.chat.completions.parse = traceable(openai.beta.chat.completions.parse.bind(openai.beta.chat.completions), chatCompletionParseMetadata);
|
|
230
|
+
}
|
|
224
231
|
}
|
|
225
232
|
tracedOpenAIClient.chat = {
|
|
226
233
|
...openai.chat,
|
|
227
|
-
completions:
|
|
228
|
-
...openai.chat.completions,
|
|
229
|
-
create: traceable(openai.chat.completions.create.bind(openai.chat.completions), {
|
|
230
|
-
name: "ChatOpenAI",
|
|
231
|
-
run_type: "llm",
|
|
232
|
-
aggregator: chatAggregator,
|
|
233
|
-
argsConfigPath: [1, "langsmithExtra"],
|
|
234
|
-
getInvocationParams: (payload) => {
|
|
235
|
-
if (typeof payload !== "object" || payload == null)
|
|
236
|
-
return undefined;
|
|
237
|
-
// we can safely do so, as the types are not exported in TSC
|
|
238
|
-
const params = payload;
|
|
239
|
-
const ls_stop = (typeof params.stop === "string" ? [params.stop] : params.stop) ??
|
|
240
|
-
undefined;
|
|
241
|
-
return {
|
|
242
|
-
ls_provider: "openai",
|
|
243
|
-
ls_model_type: "chat",
|
|
244
|
-
ls_model_name: params.model,
|
|
245
|
-
ls_max_tokens: params.max_tokens ?? undefined,
|
|
246
|
-
ls_temperature: params.temperature ?? undefined,
|
|
247
|
-
ls_stop,
|
|
248
|
-
};
|
|
249
|
-
},
|
|
250
|
-
processOutputs: processChatCompletion,
|
|
251
|
-
...options,
|
|
252
|
-
}),
|
|
253
|
-
},
|
|
234
|
+
completions: Object.create(Object.getPrototypeOf(openai.chat.completions)),
|
|
254
235
|
};
|
|
236
|
+
// Copy all own properties and then wrap specific methods
|
|
237
|
+
Object.assign(tracedOpenAIClient.chat.completions, openai.chat.completions);
|
|
238
|
+
// Wrap chat.completions.create
|
|
239
|
+
tracedOpenAIClient.chat.completions.create = traceable(openai.chat.completions.create.bind(openai.chat.completions), chatCompletionParseMetadata);
|
|
240
|
+
// Wrap chat.completions.parse if it exists
|
|
241
|
+
if (typeof openai.chat.completions.parse === "function") {
|
|
242
|
+
tracedOpenAIClient.chat.completions.parse = traceable(openai.chat.completions.parse.bind(openai.chat.completions), chatCompletionParseMetadata);
|
|
243
|
+
}
|
|
255
244
|
tracedOpenAIClient.completions = {
|
|
256
245
|
...openai.completions,
|
|
257
246
|
create: traceable(openai.completions.create.bind(openai.completions), {
|
|
@@ -278,5 +267,37 @@ export const wrapOpenAI = (openai, options) => {
|
|
|
278
267
|
...options,
|
|
279
268
|
}),
|
|
280
269
|
};
|
|
270
|
+
// Add responses API support if it exists
|
|
271
|
+
if (openai.responses) {
|
|
272
|
+
// Create a new object with the same prototype to preserve all methods
|
|
273
|
+
tracedOpenAIClient.responses = Object.create(Object.getPrototypeOf(openai.responses));
|
|
274
|
+
// Copy all own properties
|
|
275
|
+
if (tracedOpenAIClient.responses) {
|
|
276
|
+
Object.assign(tracedOpenAIClient.responses, openai.responses);
|
|
277
|
+
}
|
|
278
|
+
// Wrap responses.create method
|
|
279
|
+
if (tracedOpenAIClient.responses &&
|
|
280
|
+
typeof tracedOpenAIClient.responses.create === "function") {
|
|
281
|
+
tracedOpenAIClient.responses.create = traceable(openai.responses.create.bind(openai.responses), {
|
|
282
|
+
name: "ChatOpenAI",
|
|
283
|
+
run_type: "llm",
|
|
284
|
+
aggregator: responsesAggregator,
|
|
285
|
+
argsConfigPath: [1, "langsmithExtra"],
|
|
286
|
+
getInvocationParams: (payload) => {
|
|
287
|
+
if (typeof payload !== "object" || payload == null)
|
|
288
|
+
return undefined;
|
|
289
|
+
// Handle responses API parameters
|
|
290
|
+
const params = payload;
|
|
291
|
+
return {
|
|
292
|
+
ls_provider: "openai",
|
|
293
|
+
ls_model_type: "llm",
|
|
294
|
+
ls_model_name: params.model || "unknown",
|
|
295
|
+
};
|
|
296
|
+
},
|
|
297
|
+
processOutputs: processChatCompletion,
|
|
298
|
+
...options,
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
}
|
|
281
302
|
return tracedOpenAIClient;
|
|
282
303
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "langsmith",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.38",
|
|
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": [
|
|
@@ -145,9 +145,9 @@
|
|
|
145
145
|
"@faker-js/faker": "^8.4.1",
|
|
146
146
|
"@jest/globals": "^29.5.0",
|
|
147
147
|
"@jest/reporters": "^29.7.0",
|
|
148
|
-
"@langchain/core": "^0.3.
|
|
149
|
-
"@langchain/langgraph": "^0.
|
|
150
|
-
"@langchain/openai": "^0.
|
|
148
|
+
"@langchain/core": "^0.3.61",
|
|
149
|
+
"@langchain/langgraph": "^0.3.6",
|
|
150
|
+
"@langchain/openai": "^0.5.16",
|
|
151
151
|
"@opentelemetry/api": "^1.9.0",
|
|
152
152
|
"@opentelemetry/auto-instrumentations-node": "^0.58.0",
|
|
153
153
|
"@opentelemetry/sdk-node": "^0.200.0",
|
|
@@ -155,6 +155,7 @@
|
|
|
155
155
|
"@opentelemetry/sdk-trace-node": "^2.0.0",
|
|
156
156
|
"@tsconfig/recommended": "^1.0.2",
|
|
157
157
|
"@types/jest": "^29.5.1",
|
|
158
|
+
"@types/node-fetch": "^2.6.12",
|
|
158
159
|
"@typescript-eslint/eslint-plugin": "^5.59.8",
|
|
159
160
|
"@typescript-eslint/parser": "^5.59.8",
|
|
160
161
|
"ai": "^4.3.10",
|
|
@@ -167,9 +168,9 @@
|
|
|
167
168
|
"eslint-plugin-no-instanceof": "^1.0.1",
|
|
168
169
|
"eslint-plugin-prettier": "^4.2.1",
|
|
169
170
|
"jest": "^29.5.0",
|
|
170
|
-
"langchain": "^0.3.
|
|
171
|
+
"langchain": "^0.3.29",
|
|
171
172
|
"node-fetch": "^2.7.0",
|
|
172
|
-
"openai": "^
|
|
173
|
+
"openai": "^5.8.2",
|
|
173
174
|
"prettier": "^2.8.8",
|
|
174
175
|
"ts-jest": "^29.1.0",
|
|
175
176
|
"ts-node": "^10.9.1",
|
|
@@ -180,10 +181,10 @@
|
|
|
180
181
|
"zod": "^3.23.8"
|
|
181
182
|
},
|
|
182
183
|
"peerDependencies": {
|
|
183
|
-
"openai": "*",
|
|
184
184
|
"@opentelemetry/api": "*",
|
|
185
185
|
"@opentelemetry/exporter-trace-otlp-proto": "*",
|
|
186
|
-
"@opentelemetry/sdk-trace-base": "*"
|
|
186
|
+
"@opentelemetry/sdk-trace-base": "*",
|
|
187
|
+
"openai": "*"
|
|
187
188
|
},
|
|
188
189
|
"peerDependenciesMeta": {
|
|
189
190
|
"openai": {
|