langsmith 0.8.12 → 0.10.0
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/_openapi_client/client.cjs +1 -1
- package/dist/_openapi_client/client.d.ts +2 -2
- package/dist/_openapi_client/client.js +1 -1
- package/dist/_openapi_client/internal/detect-platform.cjs +1 -1
- package/dist/_openapi_client/internal/detect-platform.js +1 -1
- package/dist/_openapi_client/resources/annotation-queues/annotation-queues.d.ts +1 -0
- package/dist/_openapi_client/resources/index.d.ts +1 -1
- package/dist/_openapi_client/resources/issues.cjs +1 -1
- package/dist/_openapi_client/resources/issues.d.ts +18 -1
- package/dist/_openapi_client/resources/issues.js +1 -1
- package/dist/_openapi_client/resources/online-evaluators.d.ts +2 -2
- package/dist/_openapi_client/resources/sandboxes/boxes.cjs +20 -3
- package/dist/_openapi_client/resources/sandboxes/boxes.d.ts +40 -9
- package/dist/_openapi_client/resources/sandboxes/boxes.js +20 -3
- package/dist/_openapi_client/resources/sandboxes/sandboxes.d.ts +46 -3
- package/dist/_openapi_client/resources/sandboxes/snapshots.cjs +6 -2
- package/dist/_openapi_client/resources/sandboxes/snapshots.d.ts +16 -6
- package/dist/_openapi_client/resources/sandboxes/snapshots.js +6 -2
- package/dist/client.cjs +56 -83
- package/dist/client.d.ts +16 -14
- package/dist/client.js +56 -83
- package/dist/env.cjs +14 -1
- package/dist/env.d.ts +9 -0
- package/dist/env.js +12 -0
- package/dist/evaluation/evaluate_comparative.cjs +2 -0
- package/dist/evaluation/evaluate_comparative.js +2 -0
- package/dist/experimental/vercel/telemetry.cjs +19 -6
- package/dist/experimental/vercel/telemetry.js +17 -4
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/run_trees.cjs +19 -2
- package/dist/run_trees.d.ts +12 -0
- package/dist/run_trees.js +19 -2
- package/dist/schemas.d.ts +2 -0
- package/dist/singletons/constants.cjs +7 -1
- package/dist/singletons/constants.d.ts +3 -0
- package/dist/singletons/constants.js +5 -0
- package/dist/traceable.cjs +46 -15
- package/dist/traceable.js +48 -17
- package/dist/utils/_uuid.cjs +11 -19
- package/dist/utils/_uuid.d.ts +7 -7
- package/dist/utils/_uuid.js +11 -19
- package/dist/utils/env.cjs +18 -7
- package/dist/utils/env.js +18 -7
- package/dist/utils/redaction.cjs +38 -0
- package/dist/utils/redaction.d.ts +10 -0
- package/dist/utils/redaction.js +33 -0
- package/dist/utils/sampling.cjs +34 -0
- package/dist/utils/sampling.d.ts +10 -0
- package/dist/utils/sampling.js +30 -0
- package/dist/wrappers/anthropic.cjs +21 -24
- package/dist/wrappers/anthropic.js +21 -24
- package/dist/wrappers/openai_agents.cjs +2 -1
- package/dist/wrappers/openai_agents.js +2 -1
- package/package.json +1 -1
package/dist/run_trees.js
CHANGED
|
@@ -134,6 +134,10 @@ class Baggage {
|
|
|
134
134
|
return items.join(",");
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
|
+
function getExcludeInputsOnPatch() {
|
|
138
|
+
const value = getLangSmithEnvironmentVariable("EXCLUDE_INPUTS_ON_PATCH");
|
|
139
|
+
return value === undefined || value.toLowerCase() === "true" || value === "1";
|
|
140
|
+
}
|
|
137
141
|
export class RunTree {
|
|
138
142
|
constructor(originalConfig) {
|
|
139
143
|
Object.defineProperty(this, "id", {
|
|
@@ -731,7 +735,20 @@ export class RunTree {
|
|
|
731
735
|
console.error(`Error in postRun for run ${this.id}:`, error);
|
|
732
736
|
}
|
|
733
737
|
}
|
|
738
|
+
/**
|
|
739
|
+
* Patch the run tree to the API.
|
|
740
|
+
*
|
|
741
|
+
* @param options.excludeInputs - Whether to exclude inputs from the patch
|
|
742
|
+
* request. Defaults to the value of `LANGSMITH_EXCLUDE_INPUTS_ON_PATCH`
|
|
743
|
+
* (or its `LANGCHAIN_` equivalent), which itself defaults to `true`.
|
|
744
|
+
* An explicit value overrides the environment variable for this call.
|
|
745
|
+
*
|
|
746
|
+
* Unless the environment variable is disabled, inputs added after the
|
|
747
|
+
* initial `postRun()` are not persisted unless this option is explicitly set
|
|
748
|
+
* to `false`.
|
|
749
|
+
*/
|
|
734
750
|
async patchRun(options) {
|
|
751
|
+
const excludeInputs = options?.excludeInputs ?? getExcludeInputsOnPatch();
|
|
735
752
|
if (this.replicas && this.replicas.length > 0) {
|
|
736
753
|
for (const { projectName, primary, apiKey, apiUrl, workspaceId, updates, reroot, client: replicaClient, } of this.replicas) {
|
|
737
754
|
const runData = this._remapForProject({
|
|
@@ -767,7 +784,7 @@ export class RunTree {
|
|
|
767
784
|
// Important that inputs is not a key in the run update
|
|
768
785
|
// if excluded because it will overwrite the run create if the
|
|
769
786
|
// two operations are merged during batching
|
|
770
|
-
if (!
|
|
787
|
+
if (!excludeInputs) {
|
|
771
788
|
updatePayload.inputs = runData.inputs;
|
|
772
789
|
}
|
|
773
790
|
const targetClient = replicaClient ?? this.client;
|
|
@@ -800,7 +817,7 @@ export class RunTree {
|
|
|
800
817
|
// Important that inputs is not a key in the run update
|
|
801
818
|
// if excluded because it will overwrite the run create if the
|
|
802
819
|
// two operations are merged during batching
|
|
803
|
-
if (!
|
|
820
|
+
if (!excludeInputs) {
|
|
804
821
|
runUpdate.inputs = this.inputs;
|
|
805
822
|
}
|
|
806
823
|
await this.client.updateRun(this.id, runUpdate);
|
package/dist/schemas.d.ts
CHANGED
|
@@ -288,6 +288,8 @@ export interface FeedbackBase {
|
|
|
288
288
|
created_at: string;
|
|
289
289
|
modified_at: string;
|
|
290
290
|
run_id: string;
|
|
291
|
+
/** The trace the run belongs to. Equals run_id for a root run. */
|
|
292
|
+
trace_id?: string;
|
|
291
293
|
key: string;
|
|
292
294
|
score: ScoreType;
|
|
293
295
|
value: ValueType;
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports._REPLICA_TRACE_ROOTS_KEY = exports._LC_CHILD_RUN_END_PROMISES_KEY = exports._LC_CONTEXT_VARIABLES_KEY = void 0;
|
|
3
|
+
exports._processingFailed = exports._PROCESSING_FAILED_PREFIX = exports._PROCESSING_FAILED_KEY = exports._REPLICA_TRACE_ROOTS_KEY = exports._LC_CHILD_RUN_END_PROMISES_KEY = exports._LC_CONTEXT_VARIABLES_KEY = void 0;
|
|
4
4
|
exports._LC_CONTEXT_VARIABLES_KEY = Symbol.for("lc:context_variables");
|
|
5
5
|
exports._LC_CHILD_RUN_END_PROMISES_KEY = Symbol.for("lc:child_run_end_promises");
|
|
6
6
|
exports._REPLICA_TRACE_ROOTS_KEY = Symbol.for("langsmith:replica_trace_roots");
|
|
7
|
+
// Marker for a payload whose redactor threw.
|
|
8
|
+
// Takes the hook name, never the error: reading e.constructor runs user code.
|
|
9
|
+
exports._PROCESSING_FAILED_KEY = "ls_error";
|
|
10
|
+
exports._PROCESSING_FAILED_PREFIX = "Processing failed; ";
|
|
11
|
+
const _processingFailed = (payload, hook) => `${exports._PROCESSING_FAILED_PREFIX}${payload} dropped (${hook})`;
|
|
12
|
+
exports._processingFailed = _processingFailed;
|
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
export declare const _LC_CONTEXT_VARIABLES_KEY: unique symbol;
|
|
2
2
|
export declare const _LC_CHILD_RUN_END_PROMISES_KEY: unique symbol;
|
|
3
3
|
export declare const _REPLICA_TRACE_ROOTS_KEY: unique symbol;
|
|
4
|
+
export declare const _PROCESSING_FAILED_KEY = "ls_error";
|
|
5
|
+
export declare const _PROCESSING_FAILED_PREFIX = "Processing failed; ";
|
|
6
|
+
export declare const _processingFailed: (payload: "inputs" | "outputs", hook: string) => string;
|
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
export const _LC_CONTEXT_VARIABLES_KEY = Symbol.for("lc:context_variables");
|
|
2
2
|
export const _LC_CHILD_RUN_END_PROMISES_KEY = Symbol.for("lc:child_run_end_promises");
|
|
3
3
|
export const _REPLICA_TRACE_ROOTS_KEY = Symbol.for("langsmith:replica_trace_roots");
|
|
4
|
+
// Marker for a payload whose redactor threw.
|
|
5
|
+
// Takes the hook name, never the error: reading e.constructor runs user code.
|
|
6
|
+
export const _PROCESSING_FAILED_KEY = "ls_error";
|
|
7
|
+
export const _PROCESSING_FAILED_PREFIX = "Processing failed; ";
|
|
8
|
+
export const _processingFailed = (payload, hook) => `${_PROCESSING_FAILED_PREFIX}${payload} dropped (${hook})`;
|
package/dist/traceable.cjs
CHANGED
|
@@ -91,12 +91,26 @@ const runInputsToMap = (rawInputs) => {
|
|
|
91
91
|
const handleRunInputs = (
|
|
92
92
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
93
93
|
inputs, processInputs) => {
|
|
94
|
+
// Fail closed: trace the run, but with a marker instead of raw inputs.
|
|
95
|
+
const dropped = (e) => {
|
|
96
|
+
if ((0, env_js_1.allowUnprocessedPayloads)()) {
|
|
97
|
+
console.warn("Error occurred during processInputs. Tracing unprocessed inputs " +
|
|
98
|
+
"because LANGSMITH_ALLOW_UNPROCESSED_PAYLOADS is set:", e);
|
|
99
|
+
return inputs;
|
|
100
|
+
}
|
|
101
|
+
console.warn("Error occurred during processInputs. Dropping inputs:", e);
|
|
102
|
+
return {
|
|
103
|
+
[constants_js_1._PROCESSING_FAILED_KEY]: (0, constants_js_1._processingFailed)("inputs", "processInputs"),
|
|
104
|
+
};
|
|
105
|
+
};
|
|
94
106
|
try {
|
|
95
|
-
|
|
107
|
+
const processed = processInputs(inputs);
|
|
108
|
+
return (0, asserts_js_1.isThenable)(processed)
|
|
109
|
+
? Promise.resolve(processed).catch(dropped)
|
|
110
|
+
: processed;
|
|
96
111
|
}
|
|
97
112
|
catch (e) {
|
|
98
|
-
|
|
99
|
-
return inputs;
|
|
113
|
+
return dropped(e);
|
|
100
114
|
}
|
|
101
115
|
};
|
|
102
116
|
const _extractUsage = (runData) => {
|
|
@@ -156,12 +170,36 @@ async function handleRunOutputs(params) {
|
|
|
156
170
|
else {
|
|
157
171
|
outputs = { outputs: rawOutputs };
|
|
158
172
|
}
|
|
173
|
+
// `outputs` is overwritten by the redactor call below; keep the raw value.
|
|
174
|
+
const unprocessed = outputs;
|
|
159
175
|
const childRunEndPromises = !skipChildPromiseDelay &&
|
|
160
176
|
(0, run_trees_js_1.isRunTree)(runTree) &&
|
|
161
177
|
constants_js_1._LC_CHILD_RUN_END_PROMISES_KEY in runTree &&
|
|
162
178
|
Array.isArray(runTree[constants_js_1._LC_CHILD_RUN_END_PROMISES_KEY])
|
|
163
179
|
? Promise.all(runTree[constants_js_1._LC_CHILD_RUN_END_PROMISES_KEY] ?? [])
|
|
164
180
|
: Promise.resolve();
|
|
181
|
+
// Fail closed, as for inputs: a marker rather than the raw value.
|
|
182
|
+
const droppedOutputs = (e) => {
|
|
183
|
+
if ((0, env_js_1.allowUnprocessedPayloads)()) {
|
|
184
|
+
console.error("Error occurred during processOutputs. Tracing unprocessed outputs " +
|
|
185
|
+
"because LANGSMITH_ALLOW_UNPROCESSED_PAYLOADS is set:", e);
|
|
186
|
+
return unprocessed;
|
|
187
|
+
}
|
|
188
|
+
console.error("Error occurred during processOutputs. Dropping outputs:", e);
|
|
189
|
+
return {
|
|
190
|
+
[constants_js_1._PROCESSING_FAILED_KEY]: (0, constants_js_1._processingFailed)("outputs", "processOutputs"),
|
|
191
|
+
};
|
|
192
|
+
};
|
|
193
|
+
const endOutputsDropped = async (e) => {
|
|
194
|
+
const finalOutputs = droppedOutputs(e);
|
|
195
|
+
try {
|
|
196
|
+
await childRunEndPromises;
|
|
197
|
+
await runTree?.end(finalOutputs);
|
|
198
|
+
}
|
|
199
|
+
catch (endErr) {
|
|
200
|
+
console.error("Error occurred during runTree?.end.", endErr);
|
|
201
|
+
}
|
|
202
|
+
};
|
|
165
203
|
try {
|
|
166
204
|
outputs = processOutputsFn(outputs);
|
|
167
205
|
// TODO: Investigate making this behavior for all returned promises
|
|
@@ -173,16 +211,7 @@ async function handleRunOutputs(params) {
|
|
|
173
211
|
await childRunEndPromises;
|
|
174
212
|
await runTree?.end(processedOutputs);
|
|
175
213
|
})
|
|
176
|
-
.catch(
|
|
177
|
-
console.error("Error occurred during processOutputs. Sending unprocessed outputs:", e);
|
|
178
|
-
try {
|
|
179
|
-
await childRunEndPromises;
|
|
180
|
-
await runTree?.end(outputs);
|
|
181
|
-
}
|
|
182
|
-
catch (e) {
|
|
183
|
-
console.error("Error occurred during runTree?.end.", e);
|
|
184
|
-
}
|
|
185
|
-
})
|
|
214
|
+
.catch(endOutputsDropped)
|
|
186
215
|
.finally(async () => {
|
|
187
216
|
try {
|
|
188
217
|
await handleEnd({
|
|
@@ -200,7 +229,7 @@ async function handleRunOutputs(params) {
|
|
|
200
229
|
}
|
|
201
230
|
}
|
|
202
231
|
catch (e) {
|
|
203
|
-
|
|
232
|
+
outputs = droppedOutputs(e);
|
|
204
233
|
}
|
|
205
234
|
_populateUsageMetadataAndOutputs(outputs, runTree);
|
|
206
235
|
void childRunEndPromises
|
|
@@ -238,7 +267,9 @@ const getTracingRunTree = (runTree, inputs, getInvocationParams, processInputs,
|
|
|
238
267
|
const [attached, args] = handleRunAttachments(inputs, extractAttachments);
|
|
239
268
|
runTree.attachments = attached;
|
|
240
269
|
const processedInputs = handleRunInputs(args, processInputs);
|
|
241
|
-
|
|
270
|
+
// Off the value, not the fn shape: non-async redactors return promises too.
|
|
271
|
+
// Client.prepareRunCreateOrUpdateInputs also flattens it; don't rely on that.
|
|
272
|
+
if ((0, asserts_js_1.isThenable)(processedInputs)) {
|
|
242
273
|
runTree._awaitInputsOnPost = true;
|
|
243
274
|
}
|
|
244
275
|
runTree.inputs = processedInputs;
|
package/dist/traceable.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
2
|
import { RunTree, isRunTree, isRunnableConfigLike, } from "./run_trees.js";
|
|
3
|
-
import { isEnvTracingEnabled } from "./env.js";
|
|
3
|
+
import { allowUnprocessedPayloads, isEnvTracingEnabled } from "./env.js";
|
|
4
4
|
import { ROOT, AsyncLocalStorageProviderSingleton, getCurrentRunTree, } from "./singletons/traceable.js";
|
|
5
|
-
import { _LC_CHILD_RUN_END_PROMISES_KEY, _LC_CONTEXT_VARIABLES_KEY, } from "./singletons/constants.js";
|
|
5
|
+
import { _LC_CHILD_RUN_END_PROMISES_KEY, _LC_CONTEXT_VARIABLES_KEY, _PROCESSING_FAILED_KEY, _processingFailed, } from "./singletons/constants.js";
|
|
6
6
|
import { isKVMap, isReadableStream, isAsyncIterable, isIteratorLike, isThenable, isGenerator, isPromiseMethod, } from "./utils/asserts.js";
|
|
7
7
|
import { __version__ } from "./index.js";
|
|
8
8
|
import { getOTELTrace, getOTELContext } from "./singletons/otel.js";
|
|
@@ -87,12 +87,26 @@ const runInputsToMap = (rawInputs) => {
|
|
|
87
87
|
const handleRunInputs = (
|
|
88
88
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
89
89
|
inputs, processInputs) => {
|
|
90
|
+
// Fail closed: trace the run, but with a marker instead of raw inputs.
|
|
91
|
+
const dropped = (e) => {
|
|
92
|
+
if (allowUnprocessedPayloads()) {
|
|
93
|
+
console.warn("Error occurred during processInputs. Tracing unprocessed inputs " +
|
|
94
|
+
"because LANGSMITH_ALLOW_UNPROCESSED_PAYLOADS is set:", e);
|
|
95
|
+
return inputs;
|
|
96
|
+
}
|
|
97
|
+
console.warn("Error occurred during processInputs. Dropping inputs:", e);
|
|
98
|
+
return {
|
|
99
|
+
[_PROCESSING_FAILED_KEY]: _processingFailed("inputs", "processInputs"),
|
|
100
|
+
};
|
|
101
|
+
};
|
|
90
102
|
try {
|
|
91
|
-
|
|
103
|
+
const processed = processInputs(inputs);
|
|
104
|
+
return isThenable(processed)
|
|
105
|
+
? Promise.resolve(processed).catch(dropped)
|
|
106
|
+
: processed;
|
|
92
107
|
}
|
|
93
108
|
catch (e) {
|
|
94
|
-
|
|
95
|
-
return inputs;
|
|
109
|
+
return dropped(e);
|
|
96
110
|
}
|
|
97
111
|
};
|
|
98
112
|
const _extractUsage = (runData) => {
|
|
@@ -152,12 +166,36 @@ async function handleRunOutputs(params) {
|
|
|
152
166
|
else {
|
|
153
167
|
outputs = { outputs: rawOutputs };
|
|
154
168
|
}
|
|
169
|
+
// `outputs` is overwritten by the redactor call below; keep the raw value.
|
|
170
|
+
const unprocessed = outputs;
|
|
155
171
|
const childRunEndPromises = !skipChildPromiseDelay &&
|
|
156
172
|
isRunTree(runTree) &&
|
|
157
173
|
_LC_CHILD_RUN_END_PROMISES_KEY in runTree &&
|
|
158
174
|
Array.isArray(runTree[_LC_CHILD_RUN_END_PROMISES_KEY])
|
|
159
175
|
? Promise.all(runTree[_LC_CHILD_RUN_END_PROMISES_KEY] ?? [])
|
|
160
176
|
: Promise.resolve();
|
|
177
|
+
// Fail closed, as for inputs: a marker rather than the raw value.
|
|
178
|
+
const droppedOutputs = (e) => {
|
|
179
|
+
if (allowUnprocessedPayloads()) {
|
|
180
|
+
console.error("Error occurred during processOutputs. Tracing unprocessed outputs " +
|
|
181
|
+
"because LANGSMITH_ALLOW_UNPROCESSED_PAYLOADS is set:", e);
|
|
182
|
+
return unprocessed;
|
|
183
|
+
}
|
|
184
|
+
console.error("Error occurred during processOutputs. Dropping outputs:", e);
|
|
185
|
+
return {
|
|
186
|
+
[_PROCESSING_FAILED_KEY]: _processingFailed("outputs", "processOutputs"),
|
|
187
|
+
};
|
|
188
|
+
};
|
|
189
|
+
const endOutputsDropped = async (e) => {
|
|
190
|
+
const finalOutputs = droppedOutputs(e);
|
|
191
|
+
try {
|
|
192
|
+
await childRunEndPromises;
|
|
193
|
+
await runTree?.end(finalOutputs);
|
|
194
|
+
}
|
|
195
|
+
catch (endErr) {
|
|
196
|
+
console.error("Error occurred during runTree?.end.", endErr);
|
|
197
|
+
}
|
|
198
|
+
};
|
|
161
199
|
try {
|
|
162
200
|
outputs = processOutputsFn(outputs);
|
|
163
201
|
// TODO: Investigate making this behavior for all returned promises
|
|
@@ -169,16 +207,7 @@ async function handleRunOutputs(params) {
|
|
|
169
207
|
await childRunEndPromises;
|
|
170
208
|
await runTree?.end(processedOutputs);
|
|
171
209
|
})
|
|
172
|
-
.catch(
|
|
173
|
-
console.error("Error occurred during processOutputs. Sending unprocessed outputs:", e);
|
|
174
|
-
try {
|
|
175
|
-
await childRunEndPromises;
|
|
176
|
-
await runTree?.end(outputs);
|
|
177
|
-
}
|
|
178
|
-
catch (e) {
|
|
179
|
-
console.error("Error occurred during runTree?.end.", e);
|
|
180
|
-
}
|
|
181
|
-
})
|
|
210
|
+
.catch(endOutputsDropped)
|
|
182
211
|
.finally(async () => {
|
|
183
212
|
try {
|
|
184
213
|
await handleEnd({
|
|
@@ -196,7 +225,7 @@ async function handleRunOutputs(params) {
|
|
|
196
225
|
}
|
|
197
226
|
}
|
|
198
227
|
catch (e) {
|
|
199
|
-
|
|
228
|
+
outputs = droppedOutputs(e);
|
|
200
229
|
}
|
|
201
230
|
_populateUsageMetadataAndOutputs(outputs, runTree);
|
|
202
231
|
void childRunEndPromises
|
|
@@ -234,7 +263,9 @@ const getTracingRunTree = (runTree, inputs, getInvocationParams, processInputs,
|
|
|
234
263
|
const [attached, args] = handleRunAttachments(inputs, extractAttachments);
|
|
235
264
|
runTree.attachments = attached;
|
|
236
265
|
const processedInputs = handleRunInputs(args, processInputs);
|
|
237
|
-
|
|
266
|
+
// Off the value, not the fn shape: non-async redactors return promises too.
|
|
267
|
+
// Client.prepareRunCreateOrUpdateInputs also flattens it; don't rely on that.
|
|
268
|
+
if (isThenable(processedInputs)) {
|
|
238
269
|
runTree._awaitInputsOnPost = true;
|
|
239
270
|
}
|
|
240
271
|
runTree.inputs = processedInputs;
|
package/dist/utils/_uuid.cjs
CHANGED
|
@@ -113,14 +113,14 @@ function _fastHash128(str) {
|
|
|
113
113
|
/**
|
|
114
114
|
* Generate a deterministic UUID v7 derived from an original UUID and a key.
|
|
115
115
|
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
* - Is valid UUID v7 format
|
|
116
|
+
* A pure function of `originalId` and `key`: it reads no ambient state, so the
|
|
117
|
+
* same pair always yields the same UUID, in any process, at any time. This is
|
|
118
|
+
* used for creating replica ids that are reproducible across distributed
|
|
119
|
+
* systems, including across SDK implementations.
|
|
121
120
|
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
121
|
+
* A UUID v7 original keeps its timestamp, so derived ids stay ordered by run
|
|
122
|
+
* start time. Any other original has none to keep, and the field is
|
|
123
|
+
* hash-derived: stable, but not chronological.
|
|
124
124
|
*
|
|
125
125
|
* @param originalId - The source UUID string (ideally UUID v7 to preserve timestamp)
|
|
126
126
|
* @param key - A string key used for deterministic derivation (e.g., project name)
|
|
@@ -147,8 +147,6 @@ function nonCryptographicUuid7Deterministic(originalId, key) {
|
|
|
147
147
|
// [8] 2 bits: variant (10) + 6 bits rand_b
|
|
148
148
|
// [9-15] 56 bits: rand_b (continued)
|
|
149
149
|
const b = new Uint8Array(16);
|
|
150
|
-
// Check if original is UUID v7 - if so, preserve its timestamp
|
|
151
|
-
// If not, use current time to ensure the derived UUID has a valid timestamp
|
|
152
150
|
const version = getUuidVersion(originalId);
|
|
153
151
|
if (version === 7) {
|
|
154
152
|
// Preserve timestamp from original UUID7 (bytes 0-5)
|
|
@@ -156,16 +154,10 @@ function nonCryptographicUuid7Deterministic(originalId, key) {
|
|
|
156
154
|
b.set(originalBytes.slice(0, 6), 0);
|
|
157
155
|
}
|
|
158
156
|
else {
|
|
159
|
-
//
|
|
160
|
-
//
|
|
161
|
-
//
|
|
162
|
-
|
|
163
|
-
b[0] = (msecs / 0x10000000000) & 0xff;
|
|
164
|
-
b[1] = (msecs / 0x100000000) & 0xff;
|
|
165
|
-
b[2] = (msecs / 0x1000000) & 0xff;
|
|
166
|
-
b[3] = (msecs / 0x10000) & 0xff;
|
|
167
|
-
b[4] = (msecs / 0x100) & 0xff;
|
|
168
|
-
b[5] = msecs & 0xff;
|
|
157
|
+
// No timestamp to preserve. Use the six hash bytes left unused below: a
|
|
158
|
+
// clock read here would make this impure, and the same id is derived
|
|
159
|
+
// from several runs, all of which must agree.
|
|
160
|
+
b.set(h.slice(10, 16), 0);
|
|
169
161
|
}
|
|
170
162
|
// Set version 7 (0111) in high nibble + 4 bits from hash
|
|
171
163
|
b[6] = 0x70 | (h[0] & 0x0f);
|
package/dist/utils/_uuid.d.ts
CHANGED
|
@@ -22,14 +22,14 @@ export declare function warnIfNotUuidV7(uuidStr: string, _idType: string): void;
|
|
|
22
22
|
/**
|
|
23
23
|
* Generate a deterministic UUID v7 derived from an original UUID and a key.
|
|
24
24
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* - Is valid UUID v7 format
|
|
25
|
+
* A pure function of `originalId` and `key`: it reads no ambient state, so the
|
|
26
|
+
* same pair always yields the same UUID, in any process, at any time. This is
|
|
27
|
+
* used for creating replica ids that are reproducible across distributed
|
|
28
|
+
* systems, including across SDK implementations.
|
|
30
29
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
30
|
+
* A UUID v7 original keeps its timestamp, so derived ids stay ordered by run
|
|
31
|
+
* start time. Any other original has none to keep, and the field is
|
|
32
|
+
* hash-derived: stable, but not chronological.
|
|
33
33
|
*
|
|
34
34
|
* @param originalId - The source UUID string (ideally UUID v7 to preserve timestamp)
|
|
35
35
|
* @param key - A string key used for deterministic derivation (e.g., project name)
|
package/dist/utils/_uuid.js
CHANGED
|
@@ -106,14 +106,14 @@ function _fastHash128(str) {
|
|
|
106
106
|
/**
|
|
107
107
|
* Generate a deterministic UUID v7 derived from an original UUID and a key.
|
|
108
108
|
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
* - Is valid UUID v7 format
|
|
109
|
+
* A pure function of `originalId` and `key`: it reads no ambient state, so the
|
|
110
|
+
* same pair always yields the same UUID, in any process, at any time. This is
|
|
111
|
+
* used for creating replica ids that are reproducible across distributed
|
|
112
|
+
* systems, including across SDK implementations.
|
|
114
113
|
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
114
|
+
* A UUID v7 original keeps its timestamp, so derived ids stay ordered by run
|
|
115
|
+
* start time. Any other original has none to keep, and the field is
|
|
116
|
+
* hash-derived: stable, but not chronological.
|
|
117
117
|
*
|
|
118
118
|
* @param originalId - The source UUID string (ideally UUID v7 to preserve timestamp)
|
|
119
119
|
* @param key - A string key used for deterministic derivation (e.g., project name)
|
|
@@ -140,8 +140,6 @@ export function nonCryptographicUuid7Deterministic(originalId, key) {
|
|
|
140
140
|
// [8] 2 bits: variant (10) + 6 bits rand_b
|
|
141
141
|
// [9-15] 56 bits: rand_b (continued)
|
|
142
142
|
const b = new Uint8Array(16);
|
|
143
|
-
// Check if original is UUID v7 - if so, preserve its timestamp
|
|
144
|
-
// If not, use current time to ensure the derived UUID has a valid timestamp
|
|
145
143
|
const version = getUuidVersion(originalId);
|
|
146
144
|
if (version === 7) {
|
|
147
145
|
// Preserve timestamp from original UUID7 (bytes 0-5)
|
|
@@ -149,16 +147,10 @@ export function nonCryptographicUuid7Deterministic(originalId, key) {
|
|
|
149
147
|
b.set(originalBytes.slice(0, 6), 0);
|
|
150
148
|
}
|
|
151
149
|
else {
|
|
152
|
-
//
|
|
153
|
-
//
|
|
154
|
-
//
|
|
155
|
-
|
|
156
|
-
b[0] = (msecs / 0x10000000000) & 0xff;
|
|
157
|
-
b[1] = (msecs / 0x100000000) & 0xff;
|
|
158
|
-
b[2] = (msecs / 0x1000000) & 0xff;
|
|
159
|
-
b[3] = (msecs / 0x10000) & 0xff;
|
|
160
|
-
b[4] = (msecs / 0x100) & 0xff;
|
|
161
|
-
b[5] = msecs & 0xff;
|
|
150
|
+
// No timestamp to preserve. Use the six hash bytes left unused below: a
|
|
151
|
+
// clock read here would make this impure, and the same id is derived
|
|
152
|
+
// from several runs, all of which must agree.
|
|
153
|
+
b.set(h.slice(10, 16), 0);
|
|
162
154
|
}
|
|
163
155
|
// Set version 7 (0111) in high nibble + 4 bits from hash
|
|
164
156
|
b[6] = 0x70 | (h[0] & 0x0f);
|
package/dist/utils/env.cjs
CHANGED
|
@@ -76,6 +76,22 @@ function getRuntimeEnvironment() {
|
|
|
76
76
|
}
|
|
77
77
|
return runtimeEnvironment;
|
|
78
78
|
}
|
|
79
|
+
// Substrings marking a variable as secret-bearing or personal; matched
|
|
80
|
+
// case-insensitively against the variable name.
|
|
81
|
+
const EXCLUDED_SUBSTRINGS = [
|
|
82
|
+
"key",
|
|
83
|
+
"secret",
|
|
84
|
+
"token",
|
|
85
|
+
"password",
|
|
86
|
+
"passwd",
|
|
87
|
+
"pwd",
|
|
88
|
+
"credential",
|
|
89
|
+
"email",
|
|
90
|
+
];
|
|
91
|
+
function isSensitiveEnvVarName(key) {
|
|
92
|
+
const lowered = key.toLowerCase();
|
|
93
|
+
return EXCLUDED_SUBSTRINGS.some((sub) => lowered.includes(sub));
|
|
94
|
+
}
|
|
79
95
|
/**
|
|
80
96
|
* Retrieves the LangSmith-specific metadata from the current runtime environment.
|
|
81
97
|
*
|
|
@@ -101,9 +117,7 @@ function getLangSmithEnvVarsMetadata() {
|
|
|
101
117
|
for (const [key, value] of Object.entries(allEnvVars)) {
|
|
102
118
|
if (typeof value === "string" &&
|
|
103
119
|
!excluded.includes(key) &&
|
|
104
|
-
!
|
|
105
|
-
!key.toLowerCase().includes("secret") &&
|
|
106
|
-
!key.toLowerCase().includes("token")) {
|
|
120
|
+
!isSensitiveEnvVarName(key)) {
|
|
107
121
|
if (key === "LANGCHAIN_REVISION_ID") {
|
|
108
122
|
envVars["revision_id"] = value;
|
|
109
123
|
}
|
|
@@ -131,10 +145,7 @@ function getLangSmithEnvironmentVariables() {
|
|
|
131
145
|
for (const [key, value] of Object.entries(process.env)) {
|
|
132
146
|
if ((key.startsWith("LANGCHAIN_") || key.startsWith("LANGSMITH_")) &&
|
|
133
147
|
value != null) {
|
|
134
|
-
if ((key
|
|
135
|
-
key.toLowerCase().includes("secret") ||
|
|
136
|
-
key.toLowerCase().includes("token")) &&
|
|
137
|
-
typeof value === "string") {
|
|
148
|
+
if (isSensitiveEnvVarName(key) && typeof value === "string") {
|
|
138
149
|
envVars[key] =
|
|
139
150
|
value.slice(0, 2) +
|
|
140
151
|
"*".repeat(value.length - 4) +
|
package/dist/utils/env.js
CHANGED
|
@@ -58,6 +58,22 @@ export function getRuntimeEnvironment() {
|
|
|
58
58
|
}
|
|
59
59
|
return runtimeEnvironment;
|
|
60
60
|
}
|
|
61
|
+
// Substrings marking a variable as secret-bearing or personal; matched
|
|
62
|
+
// case-insensitively against the variable name.
|
|
63
|
+
const EXCLUDED_SUBSTRINGS = [
|
|
64
|
+
"key",
|
|
65
|
+
"secret",
|
|
66
|
+
"token",
|
|
67
|
+
"password",
|
|
68
|
+
"passwd",
|
|
69
|
+
"pwd",
|
|
70
|
+
"credential",
|
|
71
|
+
"email",
|
|
72
|
+
];
|
|
73
|
+
function isSensitiveEnvVarName(key) {
|
|
74
|
+
const lowered = key.toLowerCase();
|
|
75
|
+
return EXCLUDED_SUBSTRINGS.some((sub) => lowered.includes(sub));
|
|
76
|
+
}
|
|
61
77
|
/**
|
|
62
78
|
* Retrieves the LangSmith-specific metadata from the current runtime environment.
|
|
63
79
|
*
|
|
@@ -83,9 +99,7 @@ export function getLangSmithEnvVarsMetadata() {
|
|
|
83
99
|
for (const [key, value] of Object.entries(allEnvVars)) {
|
|
84
100
|
if (typeof value === "string" &&
|
|
85
101
|
!excluded.includes(key) &&
|
|
86
|
-
!
|
|
87
|
-
!key.toLowerCase().includes("secret") &&
|
|
88
|
-
!key.toLowerCase().includes("token")) {
|
|
102
|
+
!isSensitiveEnvVarName(key)) {
|
|
89
103
|
if (key === "LANGCHAIN_REVISION_ID") {
|
|
90
104
|
envVars["revision_id"] = value;
|
|
91
105
|
}
|
|
@@ -113,10 +127,7 @@ export function getLangSmithEnvironmentVariables() {
|
|
|
113
127
|
for (const [key, value] of Object.entries(process.env)) {
|
|
114
128
|
if ((key.startsWith("LANGCHAIN_") || key.startsWith("LANGSMITH_")) &&
|
|
115
129
|
value != null) {
|
|
116
|
-
if ((key
|
|
117
|
-
key.toLowerCase().includes("secret") ||
|
|
118
|
-
key.toLowerCase().includes("token")) &&
|
|
119
|
-
typeof value === "string") {
|
|
130
|
+
if (isSensitiveEnvVarName(key) && typeof value === "string") {
|
|
120
131
|
envVars[key] =
|
|
121
132
|
value.slice(0, 2) +
|
|
122
133
|
"*".repeat(value.length - 4) +
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mask = mask;
|
|
4
|
+
exports.redactOutside = redactOutside;
|
|
5
|
+
exports.overParams = overParams;
|
|
6
|
+
const index_js_1 = require("../anonymizer/index.cjs");
|
|
7
|
+
function isPlainObject(value) {
|
|
8
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9
|
+
}
|
|
10
|
+
/** Mask a value, keeping object key names so a trace shows what was set. */
|
|
11
|
+
function mask(value) {
|
|
12
|
+
if (isPlainObject(value)) {
|
|
13
|
+
return Object.fromEntries(Object.keys(value).map((key) => [key, index_js_1.SECRET_PLACEHOLDER]));
|
|
14
|
+
}
|
|
15
|
+
return index_js_1.SECRET_PLACEHOLDER;
|
|
16
|
+
}
|
|
17
|
+
/** Copy `entry`, masking values outside `safeKeys`, so unknown keys fail closed. */
|
|
18
|
+
function redactOutside(entry, safeKeys) {
|
|
19
|
+
if (!isPlainObject(entry))
|
|
20
|
+
return entry;
|
|
21
|
+
return Object.fromEntries(Object.entries(entry).map(([key, value]) => [
|
|
22
|
+
key,
|
|
23
|
+
safeKeys.has(key) ? value : index_js_1.SECRET_PLACEHOLDER,
|
|
24
|
+
]));
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Apply `redact` to every object argument, since `traceable` shapes a
|
|
28
|
+
* multi-argument call as `{ args: [params, requestOptions] }`.
|
|
29
|
+
*/
|
|
30
|
+
function overParams(inputs, redact) {
|
|
31
|
+
if (Array.isArray(inputs?.args)) {
|
|
32
|
+
return {
|
|
33
|
+
...inputs,
|
|
34
|
+
args: inputs.args.map((arg) => isPlainObject(arg) ? redact(arg) : arg),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
return redact(inputs);
|
|
38
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { KVMap } from "../schemas.js";
|
|
2
|
+
/** Mask a value, keeping object key names so a trace shows what was set. */
|
|
3
|
+
export declare function mask(value: unknown): unknown;
|
|
4
|
+
/** Copy `entry`, masking values outside `safeKeys`, so unknown keys fail closed. */
|
|
5
|
+
export declare function redactOutside(entry: unknown, safeKeys: ReadonlySet<string>): unknown;
|
|
6
|
+
/**
|
|
7
|
+
* Apply `redact` to every object argument, since `traceable` shapes a
|
|
8
|
+
* multi-argument call as `{ args: [params, requestOptions] }`.
|
|
9
|
+
*/
|
|
10
|
+
export declare function overParams(inputs: KVMap, redact: (params: KVMap) => KVMap): KVMap;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { SECRET_PLACEHOLDER } from "../anonymizer/index.js";
|
|
2
|
+
function isPlainObject(value) {
|
|
3
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4
|
+
}
|
|
5
|
+
/** Mask a value, keeping object key names so a trace shows what was set. */
|
|
6
|
+
export function mask(value) {
|
|
7
|
+
if (isPlainObject(value)) {
|
|
8
|
+
return Object.fromEntries(Object.keys(value).map((key) => [key, SECRET_PLACEHOLDER]));
|
|
9
|
+
}
|
|
10
|
+
return SECRET_PLACEHOLDER;
|
|
11
|
+
}
|
|
12
|
+
/** Copy `entry`, masking values outside `safeKeys`, so unknown keys fail closed. */
|
|
13
|
+
export function redactOutside(entry, safeKeys) {
|
|
14
|
+
if (!isPlainObject(entry))
|
|
15
|
+
return entry;
|
|
16
|
+
return Object.fromEntries(Object.entries(entry).map(([key, value]) => [
|
|
17
|
+
key,
|
|
18
|
+
safeKeys.has(key) ? value : SECRET_PLACEHOLDER,
|
|
19
|
+
]));
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Apply `redact` to every object argument, since `traceable` shapes a
|
|
23
|
+
* multi-argument call as `{ args: [params, requestOptions] }`.
|
|
24
|
+
*/
|
|
25
|
+
export function overParams(inputs, redact) {
|
|
26
|
+
if (Array.isArray(inputs?.args)) {
|
|
27
|
+
return {
|
|
28
|
+
...inputs,
|
|
29
|
+
args: inputs.args.map((arg) => isPlainObject(arg) ? redact(arg) : arg),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return redact(inputs);
|
|
33
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Deterministic trace sampling, kept consistent across LangSmith SDKs.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.isSampledById = void 0;
|
|
7
|
+
const xxhash_js_1 = require("./xxhash/xxhash.cjs");
|
|
8
|
+
const SAMPLING_HASH_MODULUS = 1000000n;
|
|
9
|
+
// Reused across calls: constructing a TextEncoder per hash costs more than the
|
|
10
|
+
// hash itself.
|
|
11
|
+
const encoder = new TextEncoder();
|
|
12
|
+
/**
|
|
13
|
+
* Decide whether `identifier` is sampled in at `samplingRate`.
|
|
14
|
+
*
|
|
15
|
+
* The decision is a pure function of the identifier, so every process and
|
|
16
|
+
* every SDK agrees on it, and a run's create and patch never disagree.
|
|
17
|
+
*/
|
|
18
|
+
const isSampledById = (identifier, samplingRate) => {
|
|
19
|
+
if (samplingRate === undefined || samplingRate >= 1) {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
if (samplingRate <= 0) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
if (identifier == null) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
// XXH3-128 over the UTF-8 bytes, matching the Python SDK's `xxhash.xxh3_128`.
|
|
29
|
+
// The identifier is sampled in when its fraction of the modulus falls below
|
|
30
|
+
// the rate, which is also expressed in [0, 1).
|
|
31
|
+
const bucket = (0, xxhash_js_1.XXH3_128)(encoder.encode(identifier.toLowerCase())) % SAMPLING_HASH_MODULUS;
|
|
32
|
+
return Number(bucket) / Number(SAMPLING_HASH_MODULUS) < samplingRate;
|
|
33
|
+
};
|
|
34
|
+
exports.isSampledById = isSampledById;
|