langsmith 0.3.1 → 0.3.3
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/README.md +16 -15
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/jest/reporter.cjs +12 -1
- package/dist/jest/reporter.js +12 -1
- package/dist/utils/jestlike/index.cjs +24 -24
- package/dist/utils/jestlike/index.d.ts +1 -0
- package/dist/utils/jestlike/index.js +22 -23
- package/dist/utils/jestlike/reporter.cjs +12 -5
- package/dist/utils/jestlike/reporter.js +12 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|

|
|
4
4
|
[](https://www.npmjs.com/package/langsmith)
|
|
5
5
|
|
|
6
|
-
|
|
7
6
|
This package contains the TypeScript client for interacting with the [LangSmith platform](https://smith.langchain.com/).
|
|
8
7
|
|
|
9
8
|
To install:
|
|
@@ -51,11 +50,11 @@ yarn add langchain
|
|
|
51
50
|
Tracing can be activated by setting the following environment variables or by manually specifying the LangChainTracer.
|
|
52
51
|
|
|
53
52
|
```typescript
|
|
54
|
-
process.env
|
|
55
|
-
process.env
|
|
56
|
-
// process.env
|
|
57
|
-
process.env
|
|
58
|
-
// process.env
|
|
53
|
+
process.env.LANGSMITH_TRACING_V2 = "true";
|
|
54
|
+
process.env.LANGSMITH_ENDPOINT = "https://api.smith.langchain.com";
|
|
55
|
+
// process.env.LANGSMITH_ENDPOINT = "https://eu.api.smith.langchain.com"; // If signed up in the EU region
|
|
56
|
+
process.env.LANGSMITH_API_KEY = "<YOUR-LANGSMITH-API-KEY>";
|
|
57
|
+
// process.env.LANGSMITH_PROJECT = "My Project Name"; // Optional: "default" is used if not set
|
|
59
58
|
```
|
|
60
59
|
|
|
61
60
|
> **Tip:** Projects are groups of traces. All runs are logged to a project. If not specified, the project is set to `default`.
|
|
@@ -83,9 +82,10 @@ or by directly specifying the connection information in the RunTree.
|
|
|
83
82
|
1. **Copy the environment variables from the Settings Page and add them to your application.**
|
|
84
83
|
|
|
85
84
|
```shell
|
|
86
|
-
export
|
|
87
|
-
|
|
88
|
-
# export
|
|
85
|
+
export LANGSMITH_TRACING_V2="true";
|
|
86
|
+
export LANGSMITH_API_KEY=<YOUR-LANGSMITH-API-KEY>
|
|
87
|
+
# export LANGSMITH_PROJECT="My Project Name" # Optional: "default" is used if not set
|
|
88
|
+
# export LANGSMITH_ENDPOINT=https://api.smith.langchain.com # or your own server
|
|
89
89
|
```
|
|
90
90
|
|
|
91
91
|
## Integrations
|
|
@@ -102,7 +102,8 @@ is using the `wrapOpenAI` wrapper function available in LangSmith 0.1.3 and up.
|
|
|
102
102
|
In order to use, you first need to set your LangSmith API key:
|
|
103
103
|
|
|
104
104
|
```shell
|
|
105
|
-
export
|
|
105
|
+
export LANGSMITH_TRACING_V2="true";
|
|
106
|
+
export LANGSMITH_API_KEY=<your-api-key>
|
|
106
107
|
```
|
|
107
108
|
|
|
108
109
|
Next, you will need to install the LangSmith SDK and the OpenAI SDK:
|
|
@@ -345,9 +346,9 @@ const parentRunConfig: RunTreeConfig = {
|
|
|
345
346
|
text: "Summarize this morning's meetings.",
|
|
346
347
|
},
|
|
347
348
|
serialized: {}, // Serialized representation of this chain
|
|
348
|
-
// project_name: "Defaults to the
|
|
349
|
-
// apiUrl: "Defaults to the
|
|
350
|
-
// apiKey: "Defaults to the
|
|
349
|
+
// project_name: "Defaults to the LANGSMITH_PROJECT env var"
|
|
350
|
+
// apiUrl: "Defaults to the LANGSMITH_ENDPOINT env var"
|
|
351
|
+
// apiKey: "Defaults to the LANGSMITH_API_KEY env var"
|
|
351
352
|
};
|
|
352
353
|
|
|
353
354
|
const parentRun = new RunTree(parentRunConfig);
|
|
@@ -440,8 +441,8 @@ the web interface, as explained in the [LangSmith docs](https://docs.smith.langc
|
|
|
440
441
|
```typescript
|
|
441
442
|
import { Client } from "langsmith/client";
|
|
442
443
|
const client = new Client({
|
|
443
|
-
// apiUrl: "https://api.langchain.com", // Defaults to the
|
|
444
|
-
// apiKey: "my_api_key", // Defaults to the
|
|
444
|
+
// apiUrl: "https://api.langchain.com", // Defaults to the LANGSMITH_ENDPOINT env var
|
|
445
|
+
// apiKey: "my_api_key", // Defaults to the LANGSMITH_API_KEY env var
|
|
445
446
|
/* callerOptions: {
|
|
446
447
|
maxConcurrency?: Infinity; // Maximum number of concurrent requests to make
|
|
447
448
|
maxRetries?: 6; // Maximum number of retries to make
|
package/dist/index.cjs
CHANGED
|
@@ -8,4 +8,4 @@ Object.defineProperty(exports, "RunTree", { enumerable: true, get: function () {
|
|
|
8
8
|
var fetch_js_1 = require("./singletons/fetch.cjs");
|
|
9
9
|
Object.defineProperty(exports, "overrideFetchImplementation", { enumerable: true, get: function () { return fetch_js_1.overrideFetchImplementation; } });
|
|
10
10
|
// Update using yarn bump-version
|
|
11
|
-
exports.__version__ = "0.3.
|
|
11
|
+
exports.__version__ = "0.3.3";
|
package/dist/index.d.ts
CHANGED
|
@@ -2,4 +2,4 @@ export { Client, type ClientConfig, type LangSmithTracingClientInterface, } from
|
|
|
2
2
|
export type { Dataset, Example, TracerSession, Run, Feedback, RetrieverOutput, } from "./schemas.js";
|
|
3
3
|
export { RunTree, type RunTreeConfig } from "./run_trees.js";
|
|
4
4
|
export { overrideFetchImplementation } from "./singletons/fetch.js";
|
|
5
|
-
export declare const __version__ = "0.3.
|
|
5
|
+
export declare const __version__ = "0.3.3";
|
package/dist/index.js
CHANGED
package/dist/jest/reporter.cjs
CHANGED
|
@@ -5,8 +5,19 @@ const reporters_1 = require("@jest/reporters");
|
|
|
5
5
|
const reporter_js_1 = require("../utils/jestlike/reporter.cjs");
|
|
6
6
|
class LangSmithEvalReporter extends reporters_1.DefaultReporter {
|
|
7
7
|
async onTestResult(test, testResult, aggregatedResults) {
|
|
8
|
+
const groupedTestResults = testResult.testResults.reduce((groups, testResult) => {
|
|
9
|
+
const ancestorTitle = testResult.ancestorTitles.join(" > ");
|
|
10
|
+
if (groups[ancestorTitle] === undefined) {
|
|
11
|
+
groups[ancestorTitle] = [];
|
|
12
|
+
}
|
|
13
|
+
groups[ancestorTitle].push(testResult);
|
|
14
|
+
return groups;
|
|
15
|
+
}, {});
|
|
8
16
|
try {
|
|
9
|
-
|
|
17
|
+
for (const testGroupName of Object.keys(groupedTestResults)) {
|
|
18
|
+
const resultGroup = groupedTestResults[testGroupName];
|
|
19
|
+
await (0, reporter_js_1.printReporterTable)(resultGroup, testResult.failureMessage);
|
|
20
|
+
}
|
|
10
21
|
}
|
|
11
22
|
catch (e) {
|
|
12
23
|
console.log("Failed to display LangSmith eval results:", e.message);
|
package/dist/jest/reporter.js
CHANGED
|
@@ -3,8 +3,19 @@ import { DefaultReporter } from "@jest/reporters";
|
|
|
3
3
|
import { printReporterTable } from "../utils/jestlike/reporter.js";
|
|
4
4
|
class LangSmithEvalReporter extends DefaultReporter {
|
|
5
5
|
async onTestResult(test, testResult, aggregatedResults) {
|
|
6
|
+
const groupedTestResults = testResult.testResults.reduce((groups, testResult) => {
|
|
7
|
+
const ancestorTitle = testResult.ancestorTitles.join(" > ");
|
|
8
|
+
if (groups[ancestorTitle] === undefined) {
|
|
9
|
+
groups[ancestorTitle] = [];
|
|
10
|
+
}
|
|
11
|
+
groups[ancestorTitle].push(testResult);
|
|
12
|
+
return groups;
|
|
13
|
+
}, {});
|
|
6
14
|
try {
|
|
7
|
-
|
|
15
|
+
for (const testGroupName of Object.keys(groupedTestResults)) {
|
|
16
|
+
const resultGroup = groupedTestResults[testGroupName];
|
|
17
|
+
await printReporterTable(resultGroup, testResult.failureMessage);
|
|
18
|
+
}
|
|
8
19
|
}
|
|
9
20
|
catch (e) {
|
|
10
21
|
console.log("Failed to display LangSmith eval results:", e.message);
|
|
@@ -28,7 +28,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
28
28
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
29
29
|
};
|
|
30
30
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
|
-
exports.generateWrapperFromJestlikeMethods = exports.logOutputs = exports.logFeedback = exports.TEST_ID_DELIMITER = exports.STRIP_ANSI_REGEX = void 0;
|
|
31
|
+
exports.generateWrapperFromJestlikeMethods = exports.objectHash = exports.logOutputs = exports.logFeedback = exports.TEST_ID_DELIMITER = exports.STRIP_ANSI_REGEX = void 0;
|
|
32
32
|
const crypto_1 = __importDefault(require("crypto"));
|
|
33
33
|
const uuid_1 = require("uuid");
|
|
34
34
|
const os = __importStar(require("node:os"));
|
|
@@ -86,31 +86,31 @@ function logOutputs(output) {
|
|
|
86
86
|
context.setLoggedOutput(output);
|
|
87
87
|
}
|
|
88
88
|
exports.logOutputs = logOutputs;
|
|
89
|
+
function objectHash(obj, depth = 0) {
|
|
90
|
+
// Prevent infinite recursion
|
|
91
|
+
if (depth > 50) {
|
|
92
|
+
throw new Error("Object is too deep to check equality for serialization. Please use a simpler example.");
|
|
93
|
+
}
|
|
94
|
+
if (Array.isArray(obj)) {
|
|
95
|
+
const arrayHash = obj.map((item) => objectHash(item, depth + 1)).join(",");
|
|
96
|
+
return crypto_1.default.createHash("sha256").update(arrayHash).digest("hex");
|
|
97
|
+
}
|
|
98
|
+
if (obj && typeof obj === "object") {
|
|
99
|
+
const sortedHash = Object.keys(obj)
|
|
100
|
+
.sort()
|
|
101
|
+
.map((key) => `${key}:${objectHash(obj[key], depth + 1)}`)
|
|
102
|
+
.join(",");
|
|
103
|
+
return crypto_1.default.createHash("sha256").update(sortedHash).digest("hex");
|
|
104
|
+
}
|
|
105
|
+
return (crypto_1.default
|
|
106
|
+
.createHash("sha256")
|
|
107
|
+
// Treat null and undefined as equal for serialization purposes
|
|
108
|
+
.update(JSON.stringify(obj ?? null))
|
|
109
|
+
.digest("hex"));
|
|
110
|
+
}
|
|
111
|
+
exports.objectHash = objectHash;
|
|
89
112
|
function generateWrapperFromJestlikeMethods(methods, testRunnerName) {
|
|
90
113
|
const { expect, test, describe, beforeAll, afterAll } = methods;
|
|
91
|
-
const objectHash = (obj, depth = 0) => {
|
|
92
|
-
// Prevent infinite recursion
|
|
93
|
-
if (depth > 50) {
|
|
94
|
-
throw new Error("Object is too deep to check equality for serialization. Please use a simpler example.");
|
|
95
|
-
}
|
|
96
|
-
if (Array.isArray(obj)) {
|
|
97
|
-
const arrayHash = obj
|
|
98
|
-
.map((item) => objectHash(item, depth + 1))
|
|
99
|
-
.join(",");
|
|
100
|
-
return crypto_1.default.createHash("sha256").update(arrayHash).digest("hex");
|
|
101
|
-
}
|
|
102
|
-
if (obj && typeof obj === "object") {
|
|
103
|
-
const sortedHash = Object.keys(obj)
|
|
104
|
-
.sort()
|
|
105
|
-
.map((key) => `${key}:${objectHash(obj[key], depth + 1)}`)
|
|
106
|
-
.join(",");
|
|
107
|
-
return crypto_1.default.createHash("sha256").update(sortedHash).digest("hex");
|
|
108
|
-
}
|
|
109
|
-
return crypto_1.default
|
|
110
|
-
.createHash("sha256")
|
|
111
|
-
.update(JSON.stringify(obj))
|
|
112
|
-
.digest("hex");
|
|
113
|
-
};
|
|
114
114
|
async function _createProject(client, datasetId, projectConfig) {
|
|
115
115
|
// Create the project, updating the experimentName until we find a unique one.
|
|
116
116
|
let project;
|
|
@@ -9,6 +9,7 @@ export declare function logFeedback(feedback: SimpleEvaluationResult, config?: {
|
|
|
9
9
|
sourceRunId?: string;
|
|
10
10
|
}): void;
|
|
11
11
|
export declare function logOutputs(output: Record<string, unknown>): void;
|
|
12
|
+
export declare function objectHash(obj: KVMap, depth?: number): string;
|
|
12
13
|
export declare function generateWrapperFromJestlikeMethods(methods: Record<string, any>, testRunnerName: string): {
|
|
13
14
|
test: (<I extends Record<string, any> = Record<string, any>, O extends Record<string, any> = Record<string, any>>(name: string, lsParams: LangSmithJestlikeWrapperParams<I, O>, testFn: (data: {
|
|
14
15
|
inputs: I;
|
|
@@ -55,31 +55,30 @@ export function logOutputs(output) {
|
|
|
55
55
|
}
|
|
56
56
|
context.setLoggedOutput(output);
|
|
57
57
|
}
|
|
58
|
+
export function objectHash(obj, depth = 0) {
|
|
59
|
+
// Prevent infinite recursion
|
|
60
|
+
if (depth > 50) {
|
|
61
|
+
throw new Error("Object is too deep to check equality for serialization. Please use a simpler example.");
|
|
62
|
+
}
|
|
63
|
+
if (Array.isArray(obj)) {
|
|
64
|
+
const arrayHash = obj.map((item) => objectHash(item, depth + 1)).join(",");
|
|
65
|
+
return crypto.createHash("sha256").update(arrayHash).digest("hex");
|
|
66
|
+
}
|
|
67
|
+
if (obj && typeof obj === "object") {
|
|
68
|
+
const sortedHash = Object.keys(obj)
|
|
69
|
+
.sort()
|
|
70
|
+
.map((key) => `${key}:${objectHash(obj[key], depth + 1)}`)
|
|
71
|
+
.join(",");
|
|
72
|
+
return crypto.createHash("sha256").update(sortedHash).digest("hex");
|
|
73
|
+
}
|
|
74
|
+
return (crypto
|
|
75
|
+
.createHash("sha256")
|
|
76
|
+
// Treat null and undefined as equal for serialization purposes
|
|
77
|
+
.update(JSON.stringify(obj ?? null))
|
|
78
|
+
.digest("hex"));
|
|
79
|
+
}
|
|
58
80
|
export function generateWrapperFromJestlikeMethods(methods, testRunnerName) {
|
|
59
81
|
const { expect, test, describe, beforeAll, afterAll } = methods;
|
|
60
|
-
const objectHash = (obj, depth = 0) => {
|
|
61
|
-
// Prevent infinite recursion
|
|
62
|
-
if (depth > 50) {
|
|
63
|
-
throw new Error("Object is too deep to check equality for serialization. Please use a simpler example.");
|
|
64
|
-
}
|
|
65
|
-
if (Array.isArray(obj)) {
|
|
66
|
-
const arrayHash = obj
|
|
67
|
-
.map((item) => objectHash(item, depth + 1))
|
|
68
|
-
.join(",");
|
|
69
|
-
return crypto.createHash("sha256").update(arrayHash).digest("hex");
|
|
70
|
-
}
|
|
71
|
-
if (obj && typeof obj === "object") {
|
|
72
|
-
const sortedHash = Object.keys(obj)
|
|
73
|
-
.sort()
|
|
74
|
-
.map((key) => `${key}:${objectHash(obj[key], depth + 1)}`)
|
|
75
|
-
.join(",");
|
|
76
|
-
return crypto.createHash("sha256").update(sortedHash).digest("hex");
|
|
77
|
-
}
|
|
78
|
-
return crypto
|
|
79
|
-
.createHash("sha256")
|
|
80
|
-
.update(JSON.stringify(obj))
|
|
81
|
-
.digest("hex");
|
|
82
|
-
};
|
|
83
82
|
async function _createProject(client, datasetId, projectConfig) {
|
|
84
83
|
// Create the project, updating the experimentName until we find a unique one.
|
|
85
84
|
let project;
|
|
@@ -34,6 +34,7 @@ const path = __importStar(require("node:path"));
|
|
|
34
34
|
const fs = __importStar(require("node:fs/promises"));
|
|
35
35
|
const index_js_1 = require("./index.cjs");
|
|
36
36
|
const FEEDBACK_COLLAPSE_THRESHOLD = 48;
|
|
37
|
+
const MAX_TEST_PARAMS_LENGTH = 18;
|
|
37
38
|
const RESERVED_KEYS = [
|
|
38
39
|
"Name",
|
|
39
40
|
"Result",
|
|
@@ -86,7 +87,9 @@ function formatValue(value) {
|
|
|
86
87
|
.map(([k, v]) => {
|
|
87
88
|
const rawValue = typeof v === "string" ? v : JSON.stringify(v);
|
|
88
89
|
const rawEntry = `${k}: ${rawValue}`;
|
|
89
|
-
const entry = rawEntry.length >
|
|
90
|
+
const entry = rawEntry.length > MAX_TEST_PARAMS_LENGTH
|
|
91
|
+
? rawEntry.slice(0, MAX_TEST_PARAMS_LENGTH - 3) + "..."
|
|
92
|
+
: rawEntry;
|
|
90
93
|
return entry;
|
|
91
94
|
})
|
|
92
95
|
.join("\n");
|
|
@@ -230,9 +233,13 @@ async function printReporterTable(results, failureMessage) {
|
|
|
230
233
|
}
|
|
231
234
|
const defaultColumns = [
|
|
232
235
|
{ name: "Test", alignment: "left", maxLen: 36 },
|
|
233
|
-
{ name: "Inputs", alignment: "left", minLen:
|
|
234
|
-
{
|
|
235
|
-
|
|
236
|
+
{ name: "Inputs", alignment: "left", minLen: MAX_TEST_PARAMS_LENGTH },
|
|
237
|
+
{
|
|
238
|
+
name: "Reference Outputs",
|
|
239
|
+
alignment: "left",
|
|
240
|
+
minLen: MAX_TEST_PARAMS_LENGTH,
|
|
241
|
+
},
|
|
242
|
+
{ name: "Outputs", alignment: "left", minLen: MAX_TEST_PARAMS_LENGTH },
|
|
236
243
|
{ name: "Status", alignment: "left" },
|
|
237
244
|
];
|
|
238
245
|
if (collapseFeedbackColumn) {
|
|
@@ -245,7 +252,7 @@ async function printReporterTable(results, failureMessage) {
|
|
|
245
252
|
defaultColumns.push({
|
|
246
253
|
name: "Feedback",
|
|
247
254
|
alignment: "left",
|
|
248
|
-
minLen: feedbackColumnLength +
|
|
255
|
+
minLen: feedbackColumnLength + 8,
|
|
249
256
|
});
|
|
250
257
|
}
|
|
251
258
|
console.log();
|
|
@@ -5,6 +5,7 @@ import * as path from "node:path";
|
|
|
5
5
|
import * as fs from "node:fs/promises";
|
|
6
6
|
import { STRIP_ANSI_REGEX, TEST_ID_DELIMITER } from "./index.js";
|
|
7
7
|
const FEEDBACK_COLLAPSE_THRESHOLD = 48;
|
|
8
|
+
const MAX_TEST_PARAMS_LENGTH = 18;
|
|
8
9
|
const RESERVED_KEYS = [
|
|
9
10
|
"Name",
|
|
10
11
|
"Result",
|
|
@@ -57,7 +58,9 @@ function formatValue(value) {
|
|
|
57
58
|
.map(([k, v]) => {
|
|
58
59
|
const rawValue = typeof v === "string" ? v : JSON.stringify(v);
|
|
59
60
|
const rawEntry = `${k}: ${rawValue}`;
|
|
60
|
-
const entry = rawEntry.length >
|
|
61
|
+
const entry = rawEntry.length > MAX_TEST_PARAMS_LENGTH
|
|
62
|
+
? rawEntry.slice(0, MAX_TEST_PARAMS_LENGTH - 3) + "..."
|
|
63
|
+
: rawEntry;
|
|
61
64
|
return entry;
|
|
62
65
|
})
|
|
63
66
|
.join("\n");
|
|
@@ -201,9 +204,13 @@ export async function printReporterTable(results, failureMessage) {
|
|
|
201
204
|
}
|
|
202
205
|
const defaultColumns = [
|
|
203
206
|
{ name: "Test", alignment: "left", maxLen: 36 },
|
|
204
|
-
{ name: "Inputs", alignment: "left", minLen:
|
|
205
|
-
{
|
|
206
|
-
|
|
207
|
+
{ name: "Inputs", alignment: "left", minLen: MAX_TEST_PARAMS_LENGTH },
|
|
208
|
+
{
|
|
209
|
+
name: "Reference Outputs",
|
|
210
|
+
alignment: "left",
|
|
211
|
+
minLen: MAX_TEST_PARAMS_LENGTH,
|
|
212
|
+
},
|
|
213
|
+
{ name: "Outputs", alignment: "left", minLen: MAX_TEST_PARAMS_LENGTH },
|
|
207
214
|
{ name: "Status", alignment: "left" },
|
|
208
215
|
];
|
|
209
216
|
if (collapseFeedbackColumn) {
|
|
@@ -216,7 +223,7 @@ export async function printReporterTable(results, failureMessage) {
|
|
|
216
223
|
defaultColumns.push({
|
|
217
224
|
name: "Feedback",
|
|
218
225
|
alignment: "left",
|
|
219
|
-
minLen: feedbackColumnLength +
|
|
226
|
+
minLen: feedbackColumnLength + 8,
|
|
220
227
|
});
|
|
221
228
|
}
|
|
222
229
|
console.log();
|