langsmith 0.10.5 → 0.10.7
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/address.cjs +133 -0
- package/dist/address.d.ts +13 -0
- package/dist/address.js +120 -0
- package/dist/client.cjs +58 -22
- package/dist/client.d.ts +12 -0
- package/dist/client.js +58 -22
- package/dist/experimental/otel/constants.cjs +5 -1
- package/dist/experimental/otel/constants.d.ts +3 -0
- package/dist/experimental/otel/constants.js +3 -0
- package/dist/experimental/otel/translator.cjs +14 -0
- package/dist/experimental/otel/translator.js +14 -0
- package/dist/index.cjs +4 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/run_trees.cjs +154 -24
- package/dist/run_trees.d.ts +13 -1
- package/dist/run_trees.js +154 -24
- package/dist/sandbox/errors.cjs +12 -1
- package/dist/sandbox/errors.d.ts +7 -0
- package/dist/sandbox/errors.js +10 -0
- package/dist/sandbox/index.cjs +6 -1
- package/dist/sandbox/index.d.ts +3 -1
- package/dist/sandbox/index.js +2 -1
- package/dist/sandbox/verify.cjs +300 -0
- package/dist/sandbox/verify.d.ts +122 -0
- package/dist/sandbox/verify.js +296 -0
- package/dist/schemas.d.ts +3 -0
- package/dist/traceable.cjs +29 -4
- package/dist/traceable.js +29 -4
- package/dist/utils/agent_addressing.cjs +100 -0
- package/dist/utils/agent_addressing.d.ts +30 -0
- package/dist/utils/agent_addressing.js +91 -0
- package/dist/utils/env.cjs +2 -0
- package/dist/utils/env.js +2 -0
- package/package.json +5 -5
package/dist/address.cjs
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EnvAddressError = void 0;
|
|
4
|
+
exports.normalizeAddress = normalizeAddress;
|
|
5
|
+
exports.wireKeys = wireKeys;
|
|
6
|
+
exports.envNames = envNames;
|
|
7
|
+
exports.toPayload = toPayload;
|
|
8
|
+
exports.toWire = toWire;
|
|
9
|
+
exports.fromWire = fromWire;
|
|
10
|
+
exports.addressFromEnv = addressFromEnv;
|
|
11
|
+
exports.seed = seed;
|
|
12
|
+
exports.sameAddress = sameAddress;
|
|
13
|
+
// Wire key per field; env var is `LANGSMITH_<WIRE KEY>`.
|
|
14
|
+
const FIELDS = {
|
|
15
|
+
agentId: "agent_id",
|
|
16
|
+
agentEnvironment: "agent_environment",
|
|
17
|
+
};
|
|
18
|
+
// The server's agent id rule: a DNS label, so a hostname can carry the id.
|
|
19
|
+
const AGENT_ID_PATTERN = /^[a-z]([a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
20
|
+
// Not `utils/env`: it imports the package index, which imports this module.
|
|
21
|
+
function getEnv(name) {
|
|
22
|
+
try {
|
|
23
|
+
// eslint-disable-next-line no-process-env
|
|
24
|
+
return typeof process !== "undefined" ? process.env?.[name] : undefined;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/** The `LANGSMITH_AGENT_*` / project env vars name half an address, or both. */
|
|
31
|
+
class EnvAddressError extends Error {
|
|
32
|
+
constructor(message) {
|
|
33
|
+
super(message);
|
|
34
|
+
this.name = "EnvAddressError";
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
exports.EnvAddressError = EnvAddressError;
|
|
38
|
+
/**
|
|
39
|
+
* @internal Validate `value` and return a frozen copy of its fields.
|
|
40
|
+
* @throws If it isn't an object with exactly the address fields, or a value is invalid.
|
|
41
|
+
*/
|
|
42
|
+
function normalizeAddress(value) {
|
|
43
|
+
if (value == null || typeof value !== "object" || Array.isArray(value)) {
|
|
44
|
+
throw new Error("`address` must be an object: { agentId, agentEnvironment }.");
|
|
45
|
+
}
|
|
46
|
+
const fields = value;
|
|
47
|
+
const unknown = Object.keys(fields).filter((key) => !(key in FIELDS));
|
|
48
|
+
if (unknown.length > 0) {
|
|
49
|
+
throw new Error(`Unknown address fields: ${unknown.join(", ")}.`);
|
|
50
|
+
}
|
|
51
|
+
for (const name of Object.keys(FIELDS)) {
|
|
52
|
+
const field = fields[name];
|
|
53
|
+
if (typeof field !== "string" || field === "") {
|
|
54
|
+
throw new Error(`Address ${name} must be a non-empty string.`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const { agentId, agentEnvironment } = fields;
|
|
58
|
+
if (!AGENT_ID_PATTERN.test(agentId)) {
|
|
59
|
+
throw new Error("Address agentId must be 1 to 63 lowercase ASCII letters, digits, or " +
|
|
60
|
+
"hyphens, start with a letter, and end with a letter or digit, got " +
|
|
61
|
+
`${JSON.stringify(agentId)}. Use an id such as "support-agent".`);
|
|
62
|
+
}
|
|
63
|
+
return Object.freeze({ agentId, agentEnvironment });
|
|
64
|
+
}
|
|
65
|
+
/** @internal */
|
|
66
|
+
function wireKeys() {
|
|
67
|
+
return Object.values(FIELDS);
|
|
68
|
+
}
|
|
69
|
+
/** @internal */
|
|
70
|
+
function envNames() {
|
|
71
|
+
return wireKeys().map((key) => `LANGSMITH_${key.toUpperCase()}`);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* @internal The `address` object of a run / feedback payload. `kind` and
|
|
75
|
+
* `environment` are uppercase and case-sensitive on the wire.
|
|
76
|
+
*/
|
|
77
|
+
function toPayload(address) {
|
|
78
|
+
return {
|
|
79
|
+
address: {
|
|
80
|
+
kind: "AGENT",
|
|
81
|
+
id: address.agentId,
|
|
82
|
+
environment: address.agentEnvironment.toUpperCase(),
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/** @internal Header / replica fields. */
|
|
87
|
+
function toWire(address) {
|
|
88
|
+
return {
|
|
89
|
+
[FIELDS.agentId]: address.agentId,
|
|
90
|
+
[FIELDS.agentEnvironment]: address.agentEnvironment,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/** @internal `undefined` if no field is set; throws if only some are. */
|
|
94
|
+
function fromWire(values) {
|
|
95
|
+
const fields = Object.fromEntries(Object.entries(FIELDS).map(([name, key]) => [
|
|
96
|
+
name,
|
|
97
|
+
values[key] ?? undefined,
|
|
98
|
+
]));
|
|
99
|
+
const missing = Object.entries(FIELDS)
|
|
100
|
+
.filter(([name]) => fields[name] === undefined)
|
|
101
|
+
.map(([, key]) => key);
|
|
102
|
+
if (missing.length === Object.keys(FIELDS).length) {
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
if (missing.length > 0) {
|
|
106
|
+
throw new Error(`An address needs ${missing.join(" and ")} as well.`);
|
|
107
|
+
}
|
|
108
|
+
return normalizeAddress(fields);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* @internal
|
|
112
|
+
* @throws {EnvAddressError} If only some `LANGSMITH_AGENT_*` vars are set, or one is invalid.
|
|
113
|
+
*/
|
|
114
|
+
function addressFromEnv() {
|
|
115
|
+
const values = Object.fromEntries(wireKeys().map((key, i) => [key, getEnv(envNames()[i]) || undefined]));
|
|
116
|
+
try {
|
|
117
|
+
return fromWire(values);
|
|
118
|
+
}
|
|
119
|
+
catch (e) {
|
|
120
|
+
throw new EnvAddressError(`The LANGSMITH_AGENT_* env vars can't address a run: ${e.message}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/** @internal Replica id derivation seed. */
|
|
124
|
+
function seed(address) {
|
|
125
|
+
return ["agent", ...Object.values(toWire(address))].join("/");
|
|
126
|
+
}
|
|
127
|
+
/** @internal */
|
|
128
|
+
function sameAddress(a, b) {
|
|
129
|
+
if (!a || !b) {
|
|
130
|
+
return a === b;
|
|
131
|
+
}
|
|
132
|
+
return a.agentId === b.agentId && a.agentEnvironment === b.agentEnvironment;
|
|
133
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* (beta) An address that runs are sent to instead of a project.
|
|
3
|
+
* Enabled per workspace; a workspace without it rejects the runs.
|
|
4
|
+
*/
|
|
5
|
+
export interface Address {
|
|
6
|
+
/** 1 to 63 lowercase ASCII letters, digits or hyphens; starts with a letter. */
|
|
7
|
+
agentId: string;
|
|
8
|
+
agentEnvironment: string;
|
|
9
|
+
}
|
|
10
|
+
/** The `LANGSMITH_AGENT_*` / project env vars name half an address, or both. */
|
|
11
|
+
export declare class EnvAddressError extends Error {
|
|
12
|
+
constructor(message: string);
|
|
13
|
+
}
|
package/dist/address.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// Wire key per field; env var is `LANGSMITH_<WIRE KEY>`.
|
|
2
|
+
const FIELDS = {
|
|
3
|
+
agentId: "agent_id",
|
|
4
|
+
agentEnvironment: "agent_environment",
|
|
5
|
+
};
|
|
6
|
+
// The server's agent id rule: a DNS label, so a hostname can carry the id.
|
|
7
|
+
const AGENT_ID_PATTERN = /^[a-z]([a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
8
|
+
// Not `utils/env`: it imports the package index, which imports this module.
|
|
9
|
+
function getEnv(name) {
|
|
10
|
+
try {
|
|
11
|
+
// eslint-disable-next-line no-process-env
|
|
12
|
+
return typeof process !== "undefined" ? process.env?.[name] : undefined;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/** The `LANGSMITH_AGENT_*` / project env vars name half an address, or both. */
|
|
19
|
+
export class EnvAddressError extends Error {
|
|
20
|
+
constructor(message) {
|
|
21
|
+
super(message);
|
|
22
|
+
this.name = "EnvAddressError";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* @internal Validate `value` and return a frozen copy of its fields.
|
|
27
|
+
* @throws If it isn't an object with exactly the address fields, or a value is invalid.
|
|
28
|
+
*/
|
|
29
|
+
export function normalizeAddress(value) {
|
|
30
|
+
if (value == null || typeof value !== "object" || Array.isArray(value)) {
|
|
31
|
+
throw new Error("`address` must be an object: { agentId, agentEnvironment }.");
|
|
32
|
+
}
|
|
33
|
+
const fields = value;
|
|
34
|
+
const unknown = Object.keys(fields).filter((key) => !(key in FIELDS));
|
|
35
|
+
if (unknown.length > 0) {
|
|
36
|
+
throw new Error(`Unknown address fields: ${unknown.join(", ")}.`);
|
|
37
|
+
}
|
|
38
|
+
for (const name of Object.keys(FIELDS)) {
|
|
39
|
+
const field = fields[name];
|
|
40
|
+
if (typeof field !== "string" || field === "") {
|
|
41
|
+
throw new Error(`Address ${name} must be a non-empty string.`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const { agentId, agentEnvironment } = fields;
|
|
45
|
+
if (!AGENT_ID_PATTERN.test(agentId)) {
|
|
46
|
+
throw new Error("Address agentId must be 1 to 63 lowercase ASCII letters, digits, or " +
|
|
47
|
+
"hyphens, start with a letter, and end with a letter or digit, got " +
|
|
48
|
+
`${JSON.stringify(agentId)}. Use an id such as "support-agent".`);
|
|
49
|
+
}
|
|
50
|
+
return Object.freeze({ agentId, agentEnvironment });
|
|
51
|
+
}
|
|
52
|
+
/** @internal */
|
|
53
|
+
export function wireKeys() {
|
|
54
|
+
return Object.values(FIELDS);
|
|
55
|
+
}
|
|
56
|
+
/** @internal */
|
|
57
|
+
export function envNames() {
|
|
58
|
+
return wireKeys().map((key) => `LANGSMITH_${key.toUpperCase()}`);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* @internal The `address` object of a run / feedback payload. `kind` and
|
|
62
|
+
* `environment` are uppercase and case-sensitive on the wire.
|
|
63
|
+
*/
|
|
64
|
+
export function toPayload(address) {
|
|
65
|
+
return {
|
|
66
|
+
address: {
|
|
67
|
+
kind: "AGENT",
|
|
68
|
+
id: address.agentId,
|
|
69
|
+
environment: address.agentEnvironment.toUpperCase(),
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/** @internal Header / replica fields. */
|
|
74
|
+
export function toWire(address) {
|
|
75
|
+
return {
|
|
76
|
+
[FIELDS.agentId]: address.agentId,
|
|
77
|
+
[FIELDS.agentEnvironment]: address.agentEnvironment,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/** @internal `undefined` if no field is set; throws if only some are. */
|
|
81
|
+
export function fromWire(values) {
|
|
82
|
+
const fields = Object.fromEntries(Object.entries(FIELDS).map(([name, key]) => [
|
|
83
|
+
name,
|
|
84
|
+
values[key] ?? undefined,
|
|
85
|
+
]));
|
|
86
|
+
const missing = Object.entries(FIELDS)
|
|
87
|
+
.filter(([name]) => fields[name] === undefined)
|
|
88
|
+
.map(([, key]) => key);
|
|
89
|
+
if (missing.length === Object.keys(FIELDS).length) {
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
if (missing.length > 0) {
|
|
93
|
+
throw new Error(`An address needs ${missing.join(" and ")} as well.`);
|
|
94
|
+
}
|
|
95
|
+
return normalizeAddress(fields);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* @internal
|
|
99
|
+
* @throws {EnvAddressError} If only some `LANGSMITH_AGENT_*` vars are set, or one is invalid.
|
|
100
|
+
*/
|
|
101
|
+
export function addressFromEnv() {
|
|
102
|
+
const values = Object.fromEntries(wireKeys().map((key, i) => [key, getEnv(envNames()[i]) || undefined]));
|
|
103
|
+
try {
|
|
104
|
+
return fromWire(values);
|
|
105
|
+
}
|
|
106
|
+
catch (e) {
|
|
107
|
+
throw new EnvAddressError(`The LANGSMITH_AGENT_* env vars can't address a run: ${e.message}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/** @internal Replica id derivation seed. */
|
|
111
|
+
export function seed(address) {
|
|
112
|
+
return ["agent", ...Object.values(toWire(address))].join("/");
|
|
113
|
+
}
|
|
114
|
+
/** @internal */
|
|
115
|
+
export function sameAddress(a, b) {
|
|
116
|
+
if (!a || !b) {
|
|
117
|
+
return a === b;
|
|
118
|
+
}
|
|
119
|
+
return a.agentId === b.agentId && a.agentEnvironment === b.agentEnvironment;
|
|
120
|
+
}
|
package/dist/client.cjs
CHANGED
|
@@ -47,6 +47,8 @@ const index_js_2 = require("./_openapi_client/index.cjs");
|
|
|
47
47
|
const _uuid_js_1 = require("./utils/_uuid.cjs");
|
|
48
48
|
const sampling_js_1 = require("./utils/sampling.cjs");
|
|
49
49
|
const warn_js_1 = require("./utils/warn.cjs");
|
|
50
|
+
const address_js_1 = require("./address.cjs");
|
|
51
|
+
const agent_addressing_js_1 = require("./utils/agent_addressing.cjs");
|
|
50
52
|
const v2_migration_js_1 = require("./utils/v2_migration.cjs");
|
|
51
53
|
const prompts_js_1 = require("./utils/prompts.cjs");
|
|
52
54
|
const error_js_1 = require("./utils/error.cjs");
|
|
@@ -82,6 +84,26 @@ function _normalizeRunTimestamps(run) {
|
|
|
82
84
|
end_time: _ensureUTCTimestamp(run.end_time),
|
|
83
85
|
};
|
|
84
86
|
}
|
|
87
|
+
/** Index creates by id (last wins); warns when one id goes to two projects. */
|
|
88
|
+
function indexRunCreatesById(creates) {
|
|
89
|
+
const byId = {};
|
|
90
|
+
for (const run of creates) {
|
|
91
|
+
if (!run.id) {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
const previous = byId[run.id];
|
|
95
|
+
if (previous !== undefined && previous.session_name !== run.session_name) {
|
|
96
|
+
console.warn(`LangSmith run ${run.id} was queued for two projects with the same id ` +
|
|
97
|
+
`(${JSON.stringify(previous.session_name)} and ` +
|
|
98
|
+
`${JSON.stringify(run.session_name)}); only one of the projects will ` +
|
|
99
|
+
"keep the run. This usually means two write replicas both keep the " +
|
|
100
|
+
"original run ids, for example a replica marked `primary` plus one " +
|
|
101
|
+
"for the run's own project.");
|
|
102
|
+
}
|
|
103
|
+
byId[run.id] = run;
|
|
104
|
+
}
|
|
105
|
+
return byId;
|
|
106
|
+
}
|
|
85
107
|
function mergeRuntimeEnvIntoRun(run, cachedEnvVars, omitTracedRuntimeInfo, tracingSampleRate) {
|
|
86
108
|
if (omitTracedRuntimeInfo) {
|
|
87
109
|
return run;
|
|
@@ -921,6 +943,7 @@ class Client {
|
|
|
921
943
|
}
|
|
922
944
|
// Cache metadata env vars once during construction to avoid repeatedly scanning process.env
|
|
923
945
|
this.cachedLSEnvVarsForMetadata = (0, env_js_1.getLangSmithEnvVarsMetadata)();
|
|
946
|
+
(0, agent_addressing_js_1.warnOnEnv)();
|
|
924
947
|
// Initialize prompt cache
|
|
925
948
|
// Handle backwards compatibility for deprecated `cache` parameter
|
|
926
949
|
if (config.cache !== undefined && config.disablePromptCache) {
|
|
@@ -1288,8 +1311,9 @@ class Client {
|
|
|
1288
1311
|
return event;
|
|
1289
1312
|
});
|
|
1290
1313
|
}
|
|
1291
|
-
async prepareRunCreateOrUpdateInputs(run) {
|
|
1314
|
+
async prepareRunCreateOrUpdateInputs(run, options) {
|
|
1292
1315
|
const runParams = { ...run };
|
|
1316
|
+
(0, agent_addressing_js_1.applyToPayload)(runParams, { update: options?.update ?? false });
|
|
1293
1317
|
if (runParams.inputs !== undefined) {
|
|
1294
1318
|
runParams.inputs = await this.processInputs(runParams.inputs);
|
|
1295
1319
|
}
|
|
@@ -1771,8 +1795,24 @@ class Client {
|
|
|
1771
1795
|
...this._mergedHeaders,
|
|
1772
1796
|
"Content-Type": "application/json",
|
|
1773
1797
|
};
|
|
1798
|
+
const address = (0, agent_addressing_js_1.checkAddress)(run.address);
|
|
1799
|
+
// `RunTree.postRun` passes a built run body, carrying the wire `session_name`.
|
|
1800
|
+
const bodySessionName = run.session_name;
|
|
1801
|
+
(0, agent_addressing_js_1.rejectConflicting)(run.project_name ?? bodySessionName, address);
|
|
1774
1802
|
const session_name = run.project_name;
|
|
1775
1803
|
delete run.project_name;
|
|
1804
|
+
if (!session_name && bodySessionName == null && !address) {
|
|
1805
|
+
try {
|
|
1806
|
+
run.address = (0, agent_addressing_js_1.resolveFromEnv)()[1];
|
|
1807
|
+
}
|
|
1808
|
+
catch (e) {
|
|
1809
|
+
if (!(e instanceof address_js_1.EnvAddressError)) {
|
|
1810
|
+
throw e;
|
|
1811
|
+
}
|
|
1812
|
+
(0, agent_addressing_js_1.logUntraced)(e);
|
|
1813
|
+
return;
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1776
1816
|
const runCreate = await this.prepareRunCreateOrUpdateInputs({
|
|
1777
1817
|
session_name,
|
|
1778
1818
|
...run,
|
|
@@ -1821,15 +1861,10 @@ class Client {
|
|
|
1821
1861
|
return;
|
|
1822
1862
|
}
|
|
1823
1863
|
let preparedCreateParams = await Promise.all(runCreates?.map((create) => this.prepareRunCreateOrUpdateInputs(create)) ?? []);
|
|
1824
|
-
let preparedUpdateParams = await Promise.all(runUpdates?.map((update) => this.prepareRunCreateOrUpdateInputs(update)) ?? []);
|
|
1864
|
+
let preparedUpdateParams = await Promise.all(runUpdates?.map((update) => this.prepareRunCreateOrUpdateInputs(update, { update: true })) ?? []);
|
|
1865
|
+
// Index even without updates: the collision warning must see creates-only batches.
|
|
1866
|
+
const createById = indexRunCreatesById(preparedCreateParams);
|
|
1825
1867
|
if (preparedCreateParams.length > 0 && preparedUpdateParams.length > 0) {
|
|
1826
|
-
const createById = preparedCreateParams.reduce((params, run) => {
|
|
1827
|
-
if (!run.id) {
|
|
1828
|
-
return params;
|
|
1829
|
-
}
|
|
1830
|
-
params[run.id] = run;
|
|
1831
|
-
return params;
|
|
1832
|
-
}, {});
|
|
1833
1868
|
const standaloneUpdates = [];
|
|
1834
1869
|
for (const updateParam of preparedUpdateParams) {
|
|
1835
1870
|
if (updateParam.id !== undefined && createById[updateParam.id]) {
|
|
@@ -1920,7 +1955,7 @@ class Client {
|
|
|
1920
1955
|
}
|
|
1921
1956
|
let preparedUpdateParams = [];
|
|
1922
1957
|
for (const update of runUpdates ?? []) {
|
|
1923
|
-
preparedUpdateParams.push(await this.prepareRunCreateOrUpdateInputs(update));
|
|
1958
|
+
preparedUpdateParams.push(await this.prepareRunCreateOrUpdateInputs(update, { update: true }));
|
|
1924
1959
|
}
|
|
1925
1960
|
// require trace_id and dotted_order
|
|
1926
1961
|
const invalidRunCreate = preparedCreateParams.find((runCreate) => {
|
|
@@ -1936,14 +1971,8 @@ class Client {
|
|
|
1936
1971
|
throw new Error(`Multipart ingest requires "trace_id" and "dotted_order" to be set when updating a run`);
|
|
1937
1972
|
}
|
|
1938
1973
|
// combine post and patch dicts where possible
|
|
1974
|
+
const createById = indexRunCreatesById(preparedCreateParams);
|
|
1939
1975
|
if (preparedCreateParams.length > 0 && preparedUpdateParams.length > 0) {
|
|
1940
|
-
const createById = preparedCreateParams.reduce((params, run) => {
|
|
1941
|
-
if (!run.id) {
|
|
1942
|
-
return params;
|
|
1943
|
-
}
|
|
1944
|
-
params[run.id] = run;
|
|
1945
|
-
return params;
|
|
1946
|
-
}, {});
|
|
1947
1976
|
const standaloneUpdates = [];
|
|
1948
1977
|
for (const updateParam of preparedUpdateParams) {
|
|
1949
1978
|
if (updateParam.id !== undefined && createById[updateParam.id]) {
|
|
@@ -2193,6 +2222,8 @@ class Client {
|
|
|
2193
2222
|
if (run.events) {
|
|
2194
2223
|
run.events = this._filterNewTokenEvents(run.events);
|
|
2195
2224
|
}
|
|
2225
|
+
// On `run` too: the direct path below serializes it.
|
|
2226
|
+
(0, agent_addressing_js_1.applyToPayload)(run, { update: true });
|
|
2196
2227
|
// TODO: Untangle types
|
|
2197
2228
|
const data = { ...run, id: runId };
|
|
2198
2229
|
if (!this._filterForSampling([data]).length) {
|
|
@@ -2284,6 +2315,9 @@ class Client {
|
|
|
2284
2315
|
"Use client.runs.getURL() instead. " +
|
|
2285
2316
|
"See https://docs.langchain.com/langsmith/smithdb-sdk-migration-runs#runs-get-url for the migration guide.", { type: "DeprecationWarning", code: "LANGSMITH_DEPRECATED_GET_RUN_URL" });
|
|
2286
2317
|
if (run !== undefined) {
|
|
2318
|
+
if (!run.session_id && run.address) {
|
|
2319
|
+
throw new Error("Addressed runs have no URL until read back.");
|
|
2320
|
+
}
|
|
2287
2321
|
let sessionId;
|
|
2288
2322
|
if (run.session_id) {
|
|
2289
2323
|
sessionId = run.session_id;
|
|
@@ -3613,7 +3647,7 @@ class Client {
|
|
|
3613
3647
|
if (attachment_urls) {
|
|
3614
3648
|
example.attachments = Object.entries(attachment_urls).reduce((acc, [key, value]) => {
|
|
3615
3649
|
acc[key.slice("attachment.".length)] = {
|
|
3616
|
-
presigned_url: value.presigned_url,
|
|
3650
|
+
presigned_url: new URL(value.presigned_url, this.apiUrl).href,
|
|
3617
3651
|
mime_type: value.mime_type,
|
|
3618
3652
|
};
|
|
3619
3653
|
return acc;
|
|
@@ -3681,7 +3715,7 @@ class Client {
|
|
|
3681
3715
|
if (attachment_urls) {
|
|
3682
3716
|
example.attachments = Object.entries(attachment_urls).reduce((acc, [key, value]) => {
|
|
3683
3717
|
acc[key.slice("attachment.".length)] = {
|
|
3684
|
-
presigned_url: value.presigned_url,
|
|
3718
|
+
presigned_url: new URL(value.presigned_url, this.apiUrl).href,
|
|
3685
3719
|
mime_type: value.mime_type || undefined,
|
|
3686
3720
|
};
|
|
3687
3721
|
return acc;
|
|
@@ -3906,16 +3940,18 @@ class Client {
|
|
|
3906
3940
|
});
|
|
3907
3941
|
}
|
|
3908
3942
|
async createFeedback(runIdOrParams, keyArg, optionsArg) {
|
|
3909
|
-
const { runId = null, key, score, value, correction, comment, sourceInfo, feedbackSourceType = "api", sourceRunId, feedbackId, feedbackConfig, projectId, traceId, comparativeExperimentId, sessionId, startTime, extendTraceRetention, } = typeof runIdOrParams === "object" && runIdOrParams !== null
|
|
3943
|
+
const { runId = null, key, score, value, correction, comment, sourceInfo, feedbackSourceType = "api", sourceRunId, feedbackId, feedbackConfig, projectId, traceId, comparativeExperimentId, sessionId, startTime, extendTraceRetention, address: rawAddress, } = typeof runIdOrParams === "object" && runIdOrParams !== null
|
|
3910
3944
|
? runIdOrParams
|
|
3911
3945
|
: { runId: runIdOrParams, key: keyArg, ...optionsArg };
|
|
3946
|
+
const address = (0, agent_addressing_js_1.checkAddress)(rawAddress);
|
|
3947
|
+
(0, agent_addressing_js_1.rejectConflicting)(sessionId ?? projectId, address);
|
|
3912
3948
|
if (!runId && !projectId) {
|
|
3913
3949
|
throw new Error("One of runId or projectId must be provided");
|
|
3914
3950
|
}
|
|
3915
3951
|
if (runId && projectId) {
|
|
3916
3952
|
throw new Error("Only one of runId or projectId can be provided");
|
|
3917
3953
|
}
|
|
3918
|
-
if (runId && sessionId === undefined) {
|
|
3954
|
+
if (runId && sessionId === undefined && !address) {
|
|
3919
3955
|
await this._checkFeedbackSessionId();
|
|
3920
3956
|
}
|
|
3921
3957
|
const feedback_source = {
|
|
@@ -3951,7 +3987,7 @@ class Client {
|
|
|
3951
3987
|
if (samplingId != null && !this._shouldSample(samplingId)) {
|
|
3952
3988
|
return feedback;
|
|
3953
3989
|
}
|
|
3954
|
-
const body = JSON.stringify(feedback);
|
|
3990
|
+
const body = JSON.stringify(address ? { ...feedback, ...(0, address_js_1.toPayload)(address) } : feedback);
|
|
3955
3991
|
const url = `${this.apiUrl}/feedback`;
|
|
3956
3992
|
await this.caller.call(async () => {
|
|
3957
3993
|
const res = await this._fetch(url, {
|
package/dist/client.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { AnnotationQueues } from "./_openapi_client/resources/annotation-queues/
|
|
|
11
11
|
import { Threads } from "./_openapi_client/resources/threads.js";
|
|
12
12
|
import { Traces } from "./_openapi_client/resources/traces.js";
|
|
13
13
|
import { Public } from "./_openapi_client/resources/public/public.js";
|
|
14
|
+
import { type Address } from "./address.js";
|
|
14
15
|
import { PromptCache } from "./utils/prompt_cache/index.js";
|
|
15
16
|
import { ProfileAuth } from "./utils/profiles.js";
|
|
16
17
|
export interface ClientConfig {
|
|
@@ -326,6 +327,8 @@ interface CreateRunParams {
|
|
|
326
327
|
child_runs?: RunCreate[];
|
|
327
328
|
parent_run_id?: string;
|
|
328
329
|
project_name?: string;
|
|
330
|
+
/** (beta) Send the run to this address instead of a project. */
|
|
331
|
+
address?: Address;
|
|
329
332
|
revision_id?: string;
|
|
330
333
|
trace_id?: string;
|
|
331
334
|
dotted_order?: string;
|
|
@@ -378,11 +381,20 @@ export type CreateFeedbackParams = CreateFeedbackOptions & ({
|
|
|
378
381
|
/** The session (project) ID of the run. */
|
|
379
382
|
sessionId: string;
|
|
380
383
|
projectId?: never;
|
|
384
|
+
address?: never;
|
|
385
|
+
} | {
|
|
386
|
+
/** The run to provide feedback on. */
|
|
387
|
+
runId: string;
|
|
388
|
+
/** (beta) The address the run was sent to, e.g. `runTree.address`. */
|
|
389
|
+
address: Address;
|
|
390
|
+
sessionId?: never;
|
|
391
|
+
projectId?: never;
|
|
381
392
|
} | {
|
|
382
393
|
runId?: null;
|
|
383
394
|
/** The project (or experiment) to provide feedback on. */
|
|
384
395
|
projectId: string;
|
|
385
396
|
sessionId?: never;
|
|
397
|
+
address?: never;
|
|
386
398
|
});
|
|
387
399
|
export type CreateExampleOptions = {
|
|
388
400
|
/** The ID of the dataset to create the example in. */
|
package/dist/client.js
CHANGED
|
@@ -9,6 +9,8 @@ import { Langsmith as OpenAPILangsmith } from "./_openapi_client/index.js";
|
|
|
9
9
|
import { assertUuid } from "./utils/_uuid.js";
|
|
10
10
|
import { isSampledById } from "./utils/sampling.js";
|
|
11
11
|
import { warnOnce } from "./utils/warn.js";
|
|
12
|
+
import { EnvAddressError, toPayload } from "./address.js";
|
|
13
|
+
import { applyToPayload, checkAddress, logUntraced, rejectConflicting, resolveFromEnv, warnOnEnv, } from "./utils/agent_addressing.js";
|
|
12
14
|
import { getQueryBackend, QueryBackend } from "./utils/v2_migration.js";
|
|
13
15
|
import { parseHubIdentifier } from "./utils/prompts.js";
|
|
14
16
|
import { raiseForStatus, isLangSmithNotFoundError, isLangSmithConflictError, } from "./utils/error.js";
|
|
@@ -44,6 +46,26 @@ function _normalizeRunTimestamps(run) {
|
|
|
44
46
|
end_time: _ensureUTCTimestamp(run.end_time),
|
|
45
47
|
};
|
|
46
48
|
}
|
|
49
|
+
/** Index creates by id (last wins); warns when one id goes to two projects. */
|
|
50
|
+
function indexRunCreatesById(creates) {
|
|
51
|
+
const byId = {};
|
|
52
|
+
for (const run of creates) {
|
|
53
|
+
if (!run.id) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const previous = byId[run.id];
|
|
57
|
+
if (previous !== undefined && previous.session_name !== run.session_name) {
|
|
58
|
+
console.warn(`LangSmith run ${run.id} was queued for two projects with the same id ` +
|
|
59
|
+
`(${JSON.stringify(previous.session_name)} and ` +
|
|
60
|
+
`${JSON.stringify(run.session_name)}); only one of the projects will ` +
|
|
61
|
+
"keep the run. This usually means two write replicas both keep the " +
|
|
62
|
+
"original run ids, for example a replica marked `primary` plus one " +
|
|
63
|
+
"for the run's own project.");
|
|
64
|
+
}
|
|
65
|
+
byId[run.id] = run;
|
|
66
|
+
}
|
|
67
|
+
return byId;
|
|
68
|
+
}
|
|
47
69
|
export function mergeRuntimeEnvIntoRun(run, cachedEnvVars, omitTracedRuntimeInfo, tracingSampleRate) {
|
|
48
70
|
if (omitTracedRuntimeInfo) {
|
|
49
71
|
return run;
|
|
@@ -882,6 +904,7 @@ export class Client {
|
|
|
882
904
|
}
|
|
883
905
|
// Cache metadata env vars once during construction to avoid repeatedly scanning process.env
|
|
884
906
|
this.cachedLSEnvVarsForMetadata = getLangSmithEnvVarsMetadata();
|
|
907
|
+
warnOnEnv();
|
|
885
908
|
// Initialize prompt cache
|
|
886
909
|
// Handle backwards compatibility for deprecated `cache` parameter
|
|
887
910
|
if (config.cache !== undefined && config.disablePromptCache) {
|
|
@@ -1249,8 +1272,9 @@ export class Client {
|
|
|
1249
1272
|
return event;
|
|
1250
1273
|
});
|
|
1251
1274
|
}
|
|
1252
|
-
async prepareRunCreateOrUpdateInputs(run) {
|
|
1275
|
+
async prepareRunCreateOrUpdateInputs(run, options) {
|
|
1253
1276
|
const runParams = { ...run };
|
|
1277
|
+
applyToPayload(runParams, { update: options?.update ?? false });
|
|
1254
1278
|
if (runParams.inputs !== undefined) {
|
|
1255
1279
|
runParams.inputs = await this.processInputs(runParams.inputs);
|
|
1256
1280
|
}
|
|
@@ -1732,8 +1756,24 @@ export class Client {
|
|
|
1732
1756
|
...this._mergedHeaders,
|
|
1733
1757
|
"Content-Type": "application/json",
|
|
1734
1758
|
};
|
|
1759
|
+
const address = checkAddress(run.address);
|
|
1760
|
+
// `RunTree.postRun` passes a built run body, carrying the wire `session_name`.
|
|
1761
|
+
const bodySessionName = run.session_name;
|
|
1762
|
+
rejectConflicting(run.project_name ?? bodySessionName, address);
|
|
1735
1763
|
const session_name = run.project_name;
|
|
1736
1764
|
delete run.project_name;
|
|
1765
|
+
if (!session_name && bodySessionName == null && !address) {
|
|
1766
|
+
try {
|
|
1767
|
+
run.address = resolveFromEnv()[1];
|
|
1768
|
+
}
|
|
1769
|
+
catch (e) {
|
|
1770
|
+
if (!(e instanceof EnvAddressError)) {
|
|
1771
|
+
throw e;
|
|
1772
|
+
}
|
|
1773
|
+
logUntraced(e);
|
|
1774
|
+
return;
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1737
1777
|
const runCreate = await this.prepareRunCreateOrUpdateInputs({
|
|
1738
1778
|
session_name,
|
|
1739
1779
|
...run,
|
|
@@ -1782,15 +1822,10 @@ export class Client {
|
|
|
1782
1822
|
return;
|
|
1783
1823
|
}
|
|
1784
1824
|
let preparedCreateParams = await Promise.all(runCreates?.map((create) => this.prepareRunCreateOrUpdateInputs(create)) ?? []);
|
|
1785
|
-
let preparedUpdateParams = await Promise.all(runUpdates?.map((update) => this.prepareRunCreateOrUpdateInputs(update)) ?? []);
|
|
1825
|
+
let preparedUpdateParams = await Promise.all(runUpdates?.map((update) => this.prepareRunCreateOrUpdateInputs(update, { update: true })) ?? []);
|
|
1826
|
+
// Index even without updates: the collision warning must see creates-only batches.
|
|
1827
|
+
const createById = indexRunCreatesById(preparedCreateParams);
|
|
1786
1828
|
if (preparedCreateParams.length > 0 && preparedUpdateParams.length > 0) {
|
|
1787
|
-
const createById = preparedCreateParams.reduce((params, run) => {
|
|
1788
|
-
if (!run.id) {
|
|
1789
|
-
return params;
|
|
1790
|
-
}
|
|
1791
|
-
params[run.id] = run;
|
|
1792
|
-
return params;
|
|
1793
|
-
}, {});
|
|
1794
1829
|
const standaloneUpdates = [];
|
|
1795
1830
|
for (const updateParam of preparedUpdateParams) {
|
|
1796
1831
|
if (updateParam.id !== undefined && createById[updateParam.id]) {
|
|
@@ -1881,7 +1916,7 @@ export class Client {
|
|
|
1881
1916
|
}
|
|
1882
1917
|
let preparedUpdateParams = [];
|
|
1883
1918
|
for (const update of runUpdates ?? []) {
|
|
1884
|
-
preparedUpdateParams.push(await this.prepareRunCreateOrUpdateInputs(update));
|
|
1919
|
+
preparedUpdateParams.push(await this.prepareRunCreateOrUpdateInputs(update, { update: true }));
|
|
1885
1920
|
}
|
|
1886
1921
|
// require trace_id and dotted_order
|
|
1887
1922
|
const invalidRunCreate = preparedCreateParams.find((runCreate) => {
|
|
@@ -1897,14 +1932,8 @@ export class Client {
|
|
|
1897
1932
|
throw new Error(`Multipart ingest requires "trace_id" and "dotted_order" to be set when updating a run`);
|
|
1898
1933
|
}
|
|
1899
1934
|
// combine post and patch dicts where possible
|
|
1935
|
+
const createById = indexRunCreatesById(preparedCreateParams);
|
|
1900
1936
|
if (preparedCreateParams.length > 0 && preparedUpdateParams.length > 0) {
|
|
1901
|
-
const createById = preparedCreateParams.reduce((params, run) => {
|
|
1902
|
-
if (!run.id) {
|
|
1903
|
-
return params;
|
|
1904
|
-
}
|
|
1905
|
-
params[run.id] = run;
|
|
1906
|
-
return params;
|
|
1907
|
-
}, {});
|
|
1908
1937
|
const standaloneUpdates = [];
|
|
1909
1938
|
for (const updateParam of preparedUpdateParams) {
|
|
1910
1939
|
if (updateParam.id !== undefined && createById[updateParam.id]) {
|
|
@@ -2154,6 +2183,8 @@ export class Client {
|
|
|
2154
2183
|
if (run.events) {
|
|
2155
2184
|
run.events = this._filterNewTokenEvents(run.events);
|
|
2156
2185
|
}
|
|
2186
|
+
// On `run` too: the direct path below serializes it.
|
|
2187
|
+
applyToPayload(run, { update: true });
|
|
2157
2188
|
// TODO: Untangle types
|
|
2158
2189
|
const data = { ...run, id: runId };
|
|
2159
2190
|
if (!this._filterForSampling([data]).length) {
|
|
@@ -2245,6 +2276,9 @@ export class Client {
|
|
|
2245
2276
|
"Use client.runs.getURL() instead. " +
|
|
2246
2277
|
"See https://docs.langchain.com/langsmith/smithdb-sdk-migration-runs#runs-get-url for the migration guide.", { type: "DeprecationWarning", code: "LANGSMITH_DEPRECATED_GET_RUN_URL" });
|
|
2247
2278
|
if (run !== undefined) {
|
|
2279
|
+
if (!run.session_id && run.address) {
|
|
2280
|
+
throw new Error("Addressed runs have no URL until read back.");
|
|
2281
|
+
}
|
|
2248
2282
|
let sessionId;
|
|
2249
2283
|
if (run.session_id) {
|
|
2250
2284
|
sessionId = run.session_id;
|
|
@@ -3574,7 +3608,7 @@ export class Client {
|
|
|
3574
3608
|
if (attachment_urls) {
|
|
3575
3609
|
example.attachments = Object.entries(attachment_urls).reduce((acc, [key, value]) => {
|
|
3576
3610
|
acc[key.slice("attachment.".length)] = {
|
|
3577
|
-
presigned_url: value.presigned_url,
|
|
3611
|
+
presigned_url: new URL(value.presigned_url, this.apiUrl).href,
|
|
3578
3612
|
mime_type: value.mime_type,
|
|
3579
3613
|
};
|
|
3580
3614
|
return acc;
|
|
@@ -3642,7 +3676,7 @@ export class Client {
|
|
|
3642
3676
|
if (attachment_urls) {
|
|
3643
3677
|
example.attachments = Object.entries(attachment_urls).reduce((acc, [key, value]) => {
|
|
3644
3678
|
acc[key.slice("attachment.".length)] = {
|
|
3645
|
-
presigned_url: value.presigned_url,
|
|
3679
|
+
presigned_url: new URL(value.presigned_url, this.apiUrl).href,
|
|
3646
3680
|
mime_type: value.mime_type || undefined,
|
|
3647
3681
|
};
|
|
3648
3682
|
return acc;
|
|
@@ -3867,16 +3901,18 @@ export class Client {
|
|
|
3867
3901
|
});
|
|
3868
3902
|
}
|
|
3869
3903
|
async createFeedback(runIdOrParams, keyArg, optionsArg) {
|
|
3870
|
-
const { runId = null, key, score, value, correction, comment, sourceInfo, feedbackSourceType = "api", sourceRunId, feedbackId, feedbackConfig, projectId, traceId, comparativeExperimentId, sessionId, startTime, extendTraceRetention, } = typeof runIdOrParams === "object" && runIdOrParams !== null
|
|
3904
|
+
const { runId = null, key, score, value, correction, comment, sourceInfo, feedbackSourceType = "api", sourceRunId, feedbackId, feedbackConfig, projectId, traceId, comparativeExperimentId, sessionId, startTime, extendTraceRetention, address: rawAddress, } = typeof runIdOrParams === "object" && runIdOrParams !== null
|
|
3871
3905
|
? runIdOrParams
|
|
3872
3906
|
: { runId: runIdOrParams, key: keyArg, ...optionsArg };
|
|
3907
|
+
const address = checkAddress(rawAddress);
|
|
3908
|
+
rejectConflicting(sessionId ?? projectId, address);
|
|
3873
3909
|
if (!runId && !projectId) {
|
|
3874
3910
|
throw new Error("One of runId or projectId must be provided");
|
|
3875
3911
|
}
|
|
3876
3912
|
if (runId && projectId) {
|
|
3877
3913
|
throw new Error("Only one of runId or projectId can be provided");
|
|
3878
3914
|
}
|
|
3879
|
-
if (runId && sessionId === undefined) {
|
|
3915
|
+
if (runId && sessionId === undefined && !address) {
|
|
3880
3916
|
await this._checkFeedbackSessionId();
|
|
3881
3917
|
}
|
|
3882
3918
|
const feedback_source = {
|
|
@@ -3912,7 +3948,7 @@ export class Client {
|
|
|
3912
3948
|
if (samplingId != null && !this._shouldSample(samplingId)) {
|
|
3913
3949
|
return feedback;
|
|
3914
3950
|
}
|
|
3915
|
-
const body = JSON.stringify(feedback);
|
|
3951
|
+
const body = JSON.stringify(address ? { ...feedback, ...toPayload(address) } : feedback);
|
|
3916
3952
|
const url = `${this.apiUrl}/feedback`;
|
|
3917
3953
|
await this.caller.call(async () => {
|
|
3918
3954
|
const res = await this._fetch(url, {
|