langsmith 0.5.24 → 0.5.26
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/client.cjs +9 -3
- package/dist/client.js +9 -3
- package/dist/experimental/anthropic/context.cjs +419 -49
- package/dist/experimental/anthropic/context.js +420 -50
- package/dist/experimental/anthropic/index.cjs +78 -10
- package/dist/experimental/anthropic/index.js +80 -12
- package/dist/experimental/anthropic/messages.cjs +53 -0
- package/dist/experimental/anthropic/messages.d.ts +6 -0
- package/dist/experimental/anthropic/messages.js +52 -0
- package/dist/experimental/anthropic/transcripts.cjs +144 -0
- package/dist/experimental/anthropic/transcripts.d.ts +22 -0
- package/dist/experimental/anthropic/transcripts.js +141 -0
- package/dist/experimental/anthropic/types.d.ts +1 -0
- package/dist/experimental/anthropic/usage.cjs +19 -20
- package/dist/experimental/anthropic/usage.d.ts +2 -1
- package/dist/experimental/anthropic/usage.js +18 -20
- package/dist/experimental/opencode/index.cjs +36 -0
- package/dist/experimental/opencode/index.d.ts +3 -0
- package/dist/experimental/opencode/index.js +32 -0
- package/dist/experimental/opencode/tracer.cjs +389 -0
- package/dist/experimental/opencode/tracer.d.ts +30 -0
- package/dist/experimental/opencode/tracer.js +385 -0
- package/dist/experimental/otel/setup.cjs +1 -1
- package/dist/experimental/otel/setup.js +1 -1
- package/dist/experimental/sandbox/sandbox.cjs +6 -2
- package/dist/experimental/sandbox/sandbox.d.ts +5 -1
- package/dist/experimental/sandbox/sandbox.js +6 -2
- package/dist/experimental/sandbox/types.d.ts +3 -1
- package/dist/experimental/vercel/index.cjs +1 -1
- package/dist/experimental/vercel/index.js +1 -1
- package/dist/experimental/vercel/middleware.cjs +2 -1
- package/dist/experimental/vercel/middleware.js +2 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/schemas.d.ts +4 -0
- package/dist/singletons/traceable.cjs +1 -3
- package/dist/singletons/traceable.js +1 -3
- package/dist/traceable.cjs +1 -3
- package/dist/traceable.js +1 -3
- package/dist/utils/_git.cjs +2 -2
- package/dist/utils/_git.js +2 -2
- package/dist/utils/env.cjs +2 -2
- package/dist/utils/env.js +2 -2
- package/dist/utils/error.cjs +2 -2
- package/dist/utils/error.js +2 -2
- package/dist/utils/jestlike/reporter.cjs +1 -1
- package/dist/utils/jestlike/reporter.js +1 -1
- package/dist/utils/jestlike/vendor/chain.cjs +2 -3
- package/dist/utils/jestlike/vendor/chain.js +2 -3
- package/dist/vitest/utils/esm.mjs +4 -4
- package/dist/wrappers/gemini.cjs +1 -1
- package/dist/wrappers/gemini.js +1 -1
- package/dist/wrappers/openai.cjs +2 -6
- package/dist/wrappers/openai.js +2 -6
- package/experimental/opencode.cjs +1 -0
- package/experimental/opencode.d.cts +1 -0
- package/experimental/opencode.d.ts +1 -0
- package/experimental/opencode.js +1 -0
- package/package.json +24 -18
package/dist/client.cjs
CHANGED
|
@@ -719,6 +719,10 @@ class Client {
|
|
|
719
719
|
this.webUrl = "https://aws.smith.langchain.com";
|
|
720
720
|
return this.webUrl;
|
|
721
721
|
}
|
|
722
|
+
else if (this.apiUrl.split(".", 1)[0].includes("apac")) {
|
|
723
|
+
this.webUrl = "https://apac.smith.langchain.com";
|
|
724
|
+
return this.webUrl;
|
|
725
|
+
}
|
|
722
726
|
else if (this.apiUrl.split(".", 1)[0].includes("beta")) {
|
|
723
727
|
this.webUrl = "https://beta.smith.langchain.com";
|
|
724
728
|
return this.webUrl;
|
|
@@ -1199,7 +1203,7 @@ class Client {
|
|
|
1199
1203
|
const response = await this.caller.call(async () => {
|
|
1200
1204
|
const res = await this._fetch(`${this.apiUrl}/info`, {
|
|
1201
1205
|
method: "GET",
|
|
1202
|
-
headers: { Accept: "application/json" },
|
|
1206
|
+
headers: { ...this._mergedHeaders, Accept: "application/json" },
|
|
1203
1207
|
signal: AbortSignal.timeout(SERVER_INFO_REQUEST_TIMEOUT_MS),
|
|
1204
1208
|
...this.fetchOptions,
|
|
1205
1209
|
});
|
|
@@ -2495,7 +2499,7 @@ class Client {
|
|
|
2495
2499
|
// projectId querying
|
|
2496
2500
|
return true;
|
|
2497
2501
|
}
|
|
2498
|
-
catch (
|
|
2502
|
+
catch (_e) {
|
|
2499
2503
|
return false;
|
|
2500
2504
|
}
|
|
2501
2505
|
}
|
|
@@ -4490,6 +4494,8 @@ class Client {
|
|
|
4490
4494
|
commit_hash: result.commit_hash,
|
|
4491
4495
|
manifest: result.manifest,
|
|
4492
4496
|
examples: result.examples,
|
|
4497
|
+
hub_model_config: result.model_config,
|
|
4498
|
+
hub_model_provider: result.model_provider,
|
|
4493
4499
|
};
|
|
4494
4500
|
}
|
|
4495
4501
|
async pullPromptCommit(promptIdentifier, options) {
|
|
@@ -4874,7 +4880,7 @@ class Client {
|
|
|
4874
4880
|
throw new Error(`Invalid public ${kind} URL: ${urlOrToken}`);
|
|
4875
4881
|
}
|
|
4876
4882
|
}
|
|
4877
|
-
catch (
|
|
4883
|
+
catch (_error) {
|
|
4878
4884
|
throw new Error(`Invalid public ${kind} URL or token: ${urlOrToken}`);
|
|
4879
4885
|
}
|
|
4880
4886
|
}
|
package/dist/client.js
CHANGED
|
@@ -681,6 +681,10 @@ export class Client {
|
|
|
681
681
|
this.webUrl = "https://aws.smith.langchain.com";
|
|
682
682
|
return this.webUrl;
|
|
683
683
|
}
|
|
684
|
+
else if (this.apiUrl.split(".", 1)[0].includes("apac")) {
|
|
685
|
+
this.webUrl = "https://apac.smith.langchain.com";
|
|
686
|
+
return this.webUrl;
|
|
687
|
+
}
|
|
684
688
|
else if (this.apiUrl.split(".", 1)[0].includes("beta")) {
|
|
685
689
|
this.webUrl = "https://beta.smith.langchain.com";
|
|
686
690
|
return this.webUrl;
|
|
@@ -1161,7 +1165,7 @@ export class Client {
|
|
|
1161
1165
|
const response = await this.caller.call(async () => {
|
|
1162
1166
|
const res = await this._fetch(`${this.apiUrl}/info`, {
|
|
1163
1167
|
method: "GET",
|
|
1164
|
-
headers: { Accept: "application/json" },
|
|
1168
|
+
headers: { ...this._mergedHeaders, Accept: "application/json" },
|
|
1165
1169
|
signal: AbortSignal.timeout(SERVER_INFO_REQUEST_TIMEOUT_MS),
|
|
1166
1170
|
...this.fetchOptions,
|
|
1167
1171
|
});
|
|
@@ -2457,7 +2461,7 @@ export class Client {
|
|
|
2457
2461
|
// projectId querying
|
|
2458
2462
|
return true;
|
|
2459
2463
|
}
|
|
2460
|
-
catch (
|
|
2464
|
+
catch (_e) {
|
|
2461
2465
|
return false;
|
|
2462
2466
|
}
|
|
2463
2467
|
}
|
|
@@ -4452,6 +4456,8 @@ export class Client {
|
|
|
4452
4456
|
commit_hash: result.commit_hash,
|
|
4453
4457
|
manifest: result.manifest,
|
|
4454
4458
|
examples: result.examples,
|
|
4459
|
+
hub_model_config: result.model_config,
|
|
4460
|
+
hub_model_provider: result.model_provider,
|
|
4455
4461
|
};
|
|
4456
4462
|
}
|
|
4457
4463
|
async pullPromptCommit(promptIdentifier, options) {
|
|
@@ -4836,7 +4842,7 @@ export class Client {
|
|
|
4836
4842
|
throw new Error(`Invalid public ${kind} URL: ${urlOrToken}`);
|
|
4837
4843
|
}
|
|
4838
4844
|
}
|
|
4839
|
-
catch (
|
|
4845
|
+
catch (_error) {
|
|
4840
4846
|
throw new Error(`Invalid public ${kind} URL or token: ${urlOrToken}`);
|
|
4841
4847
|
}
|
|
4842
4848
|
}
|
|
@@ -4,11 +4,27 @@ exports.StreamManager = void 0;
|
|
|
4
4
|
const messages_js_1 = require("./messages.cjs");
|
|
5
5
|
const traceable_js_1 = require("../../traceable.cjs");
|
|
6
6
|
const usage_js_1 = require("./usage.cjs");
|
|
7
|
+
const transcripts_js_1 = require("./transcripts.cjs");
|
|
8
|
+
function isRecord(value) {
|
|
9
|
+
return typeof value === "object" && value != null && !Array.isArray(value);
|
|
10
|
+
}
|
|
11
|
+
function isToolResultError(value) {
|
|
12
|
+
return isRecord(value) && (value.is_error === true || value.isError === true);
|
|
13
|
+
}
|
|
14
|
+
function makeSubagentTranscriptPathKey(path, toolUseId, agentType) {
|
|
15
|
+
return JSON.stringify([path, toolUseId ?? null, agentType ?? null]);
|
|
16
|
+
}
|
|
7
17
|
/**
|
|
8
18
|
* @internal
|
|
9
19
|
*/
|
|
10
20
|
class StreamManager {
|
|
11
21
|
constructor() {
|
|
22
|
+
Object.defineProperty(this, "rootRun", {
|
|
23
|
+
enumerable: true,
|
|
24
|
+
configurable: true,
|
|
25
|
+
writable: true,
|
|
26
|
+
value: void 0
|
|
27
|
+
});
|
|
12
28
|
Object.defineProperty(this, "namespaces", {
|
|
13
29
|
enumerable: true,
|
|
14
30
|
configurable: true,
|
|
@@ -33,6 +49,48 @@ class StreamManager {
|
|
|
33
49
|
writable: true,
|
|
34
50
|
value: {}
|
|
35
51
|
});
|
|
52
|
+
Object.defineProperty(this, "subagents", {
|
|
53
|
+
enumerable: true,
|
|
54
|
+
configurable: true,
|
|
55
|
+
writable: true,
|
|
56
|
+
value: {}
|
|
57
|
+
});
|
|
58
|
+
Object.defineProperty(this, "mainTranscriptPath", {
|
|
59
|
+
enumerable: true,
|
|
60
|
+
configurable: true,
|
|
61
|
+
writable: true,
|
|
62
|
+
value: void 0
|
|
63
|
+
});
|
|
64
|
+
Object.defineProperty(this, "subagentTranscriptPaths", {
|
|
65
|
+
enumerable: true,
|
|
66
|
+
configurable: true,
|
|
67
|
+
writable: true,
|
|
68
|
+
value: []
|
|
69
|
+
});
|
|
70
|
+
Object.defineProperty(this, "pendingAgentTools", {
|
|
71
|
+
enumerable: true,
|
|
72
|
+
configurable: true,
|
|
73
|
+
writable: true,
|
|
74
|
+
value: new Map()
|
|
75
|
+
});
|
|
76
|
+
Object.defineProperty(this, "agentToToolUseId", {
|
|
77
|
+
enumerable: true,
|
|
78
|
+
configurable: true,
|
|
79
|
+
writable: true,
|
|
80
|
+
value: new Map()
|
|
81
|
+
});
|
|
82
|
+
Object.defineProperty(this, "transcriptPathKeys", {
|
|
83
|
+
enumerable: true,
|
|
84
|
+
configurable: true,
|
|
85
|
+
writable: true,
|
|
86
|
+
value: new Set()
|
|
87
|
+
});
|
|
88
|
+
Object.defineProperty(this, "resultModelUsage", {
|
|
89
|
+
enumerable: true,
|
|
90
|
+
configurable: true,
|
|
91
|
+
writable: true,
|
|
92
|
+
value: void 0
|
|
93
|
+
});
|
|
36
94
|
Object.defineProperty(this, "postRunQueue", {
|
|
37
95
|
enumerable: true,
|
|
38
96
|
configurable: true,
|
|
@@ -46,10 +104,21 @@ class StreamManager {
|
|
|
46
104
|
value: []
|
|
47
105
|
});
|
|
48
106
|
const rootRun = (0, traceable_js_1.getCurrentRunTree)(true);
|
|
107
|
+
this.rootRun = rootRun;
|
|
49
108
|
this.namespaces = rootRun?.createChild ? { root: rootRun } : {};
|
|
50
109
|
this.history = { root: [] };
|
|
110
|
+
if (rootRun != null) {
|
|
111
|
+
StreamManager.managersByRootRun.set(rootRun, this);
|
|
112
|
+
}
|
|
113
|
+
StreamManager.liveManagers.add(this);
|
|
51
114
|
}
|
|
52
|
-
|
|
115
|
+
dispose() {
|
|
116
|
+
StreamManager.liveManagers.delete(this);
|
|
117
|
+
if (this.rootRun != null) {
|
|
118
|
+
StreamManager.managersByRootRun.delete(this.rootRun);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
async addMessage(message) {
|
|
53
122
|
const eventTime = Date.now();
|
|
54
123
|
// Short-circuit if no root run found
|
|
55
124
|
// This can happen if tracing is disabled globally
|
|
@@ -57,7 +126,7 @@ class StreamManager {
|
|
|
57
126
|
return;
|
|
58
127
|
if (message.type === "result") {
|
|
59
128
|
if (message.modelUsage) {
|
|
60
|
-
|
|
129
|
+
this.resultModelUsage = message.modelUsage;
|
|
61
130
|
}
|
|
62
131
|
const usage = message.modelUsage
|
|
63
132
|
? (0, usage_js_1.aggregateUsageFromModelUsage)(message.modelUsage)
|
|
@@ -105,7 +174,11 @@ class StreamManager {
|
|
|
105
174
|
this.assistant[messageId].outputs = (() => {
|
|
106
175
|
const prevMessages = this.assistant[messageId].outputs?.output.messages ?? [];
|
|
107
176
|
const newMessages = (0, messages_js_1.convertFromAnthropicMessage)([message]);
|
|
108
|
-
return {
|
|
177
|
+
return {
|
|
178
|
+
output: {
|
|
179
|
+
messages: (0, messages_js_1.mergeMessagesById)(prevMessages, newMessages),
|
|
180
|
+
},
|
|
181
|
+
};
|
|
109
182
|
})();
|
|
110
183
|
this.assistant[messageId].end_time = eventTime;
|
|
111
184
|
this.assistant[messageId].extra ??= {};
|
|
@@ -121,35 +194,10 @@ class StreamManager {
|
|
|
121
194
|
: [];
|
|
122
195
|
for (const block of tools) {
|
|
123
196
|
if ((0, messages_js_1.isTaskTool)(block)) {
|
|
124
|
-
|
|
125
|
-
block.input.agent_type ||
|
|
126
|
-
(block.input.description
|
|
127
|
-
? block.input.description.split(" ")[0]
|
|
128
|
-
: null) ||
|
|
129
|
-
"unknown-agent";
|
|
130
|
-
this.tools[block.id] ??=
|
|
131
|
-
this.createChild("root", {
|
|
132
|
-
name,
|
|
133
|
-
run_type: "chain",
|
|
134
|
-
inputs: block.input,
|
|
135
|
-
start_time: eventTime,
|
|
136
|
-
extra: {
|
|
137
|
-
metadata: {
|
|
138
|
-
ls_agent_type: "subagent",
|
|
139
|
-
},
|
|
140
|
-
},
|
|
141
|
-
}) ?? this.tools[block.id];
|
|
142
|
-
this.namespaces[block.id] ??= this.tools[block.id];
|
|
197
|
+
this.createAgentToolRun(namespace, block, eventTime);
|
|
143
198
|
}
|
|
144
199
|
else {
|
|
145
|
-
|
|
146
|
-
this.tools[block.id] ??=
|
|
147
|
-
this.createChild(namespace, {
|
|
148
|
-
name,
|
|
149
|
-
run_type: "tool",
|
|
150
|
-
inputs: block.input ? { input: block.input } : {},
|
|
151
|
-
start_time: eventTime,
|
|
152
|
-
}) ?? this.tools[block.id];
|
|
200
|
+
this.createToolRun(namespace, block, eventTime);
|
|
153
201
|
}
|
|
154
202
|
}
|
|
155
203
|
}
|
|
@@ -158,9 +206,7 @@ class StreamManager {
|
|
|
158
206
|
? message.message.content.filter((block) => "tool_use_id" in block)
|
|
159
207
|
: [];
|
|
160
208
|
const getToolOutput = (result) => {
|
|
161
|
-
if (
|
|
162
|
-
result != null &&
|
|
163
|
-
!Array.isArray(result)) {
|
|
209
|
+
if (isRecord(result)) {
|
|
164
210
|
return result;
|
|
165
211
|
}
|
|
166
212
|
return { content: result };
|
|
@@ -169,10 +215,23 @@ class StreamManager {
|
|
|
169
215
|
if (["string", "number", "boolean"].includes(typeof result)) {
|
|
170
216
|
return String(result);
|
|
171
217
|
}
|
|
218
|
+
if (Array.isArray(result)) {
|
|
219
|
+
return result.map(getToolError).join("\n");
|
|
220
|
+
}
|
|
221
|
+
if (isRecord(result)) {
|
|
222
|
+
if (typeof result.error === "string")
|
|
223
|
+
return result.error;
|
|
224
|
+
if (typeof result.text === "string")
|
|
225
|
+
return result.text;
|
|
226
|
+
if ("content" in result)
|
|
227
|
+
return getToolError(result.content);
|
|
228
|
+
}
|
|
172
229
|
return JSON.stringify(result);
|
|
173
230
|
};
|
|
174
231
|
for (const block of toolResultBlocks) {
|
|
175
|
-
|
|
232
|
+
const tool = this.tools[block.tool_use_id];
|
|
233
|
+
const subagent = this.subagents[block.tool_use_id];
|
|
234
|
+
if (tool != null || subagent != null) {
|
|
176
235
|
// Previous versions of @anthropic-ai/claude-agent-sdk did provide
|
|
177
236
|
// tool result in `message.tool_use_result`, but at least since 0.2.50 it disappeared,
|
|
178
237
|
// so we rely on the last tool result block instead.
|
|
@@ -180,36 +239,347 @@ class StreamManager {
|
|
|
180
239
|
? message.tool_use_result
|
|
181
240
|
: block.content;
|
|
182
241
|
const toolOutput = getToolOutput(result);
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
242
|
+
const isError = isToolResultError(block) || isToolResultError(result);
|
|
243
|
+
const toolError = isError ? getToolError(result) : undefined;
|
|
244
|
+
await tool?.end(toolOutput, toolError);
|
|
245
|
+
// Match Python's lifecycle: PostToolUse sets outputs on the
|
|
246
|
+
// subagent chain, but the subagent itself is not ended until after
|
|
247
|
+
// transcript reconciliation. Hidden transcript LLM/tool children can
|
|
248
|
+
// arrive after the Agent/Task tool result, so ending the chain here
|
|
249
|
+
// can make reconciled children appear outside their parent bounds.
|
|
250
|
+
if (subagent != null) {
|
|
251
|
+
subagent.outputs ??= toolOutput;
|
|
252
|
+
subagent.error ??= toolError;
|
|
253
|
+
}
|
|
187
254
|
}
|
|
188
255
|
}
|
|
189
256
|
}
|
|
190
257
|
this.history[namespace].push(message);
|
|
191
258
|
}
|
|
259
|
+
addHookEvent(input, toolUseId) {
|
|
260
|
+
if (typeof input !== "object" || input == null)
|
|
261
|
+
return;
|
|
262
|
+
const data = input;
|
|
263
|
+
if (this.mainTranscriptPath == null &&
|
|
264
|
+
typeof data.transcript_path === "string") {
|
|
265
|
+
this.mainTranscriptPath = data.transcript_path;
|
|
266
|
+
}
|
|
267
|
+
if (data.hook_event_name === "PreToolUse" &&
|
|
268
|
+
typeof toolUseId === "string" &&
|
|
269
|
+
(data.tool_name === "Agent" || data.tool_name === "Task")) {
|
|
270
|
+
this.pendingAgentTools.set(toolUseId, typeof data.tool_input === "object" && data.tool_input != null
|
|
271
|
+
? data.tool_input
|
|
272
|
+
: {});
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
if (data.hook_event_name === "SubagentStart") {
|
|
276
|
+
const agentId = typeof data.agent_id === "string" ? data.agent_id : undefined;
|
|
277
|
+
if (agentId == null)
|
|
278
|
+
return;
|
|
279
|
+
const agentType = typeof data.agent_type === "string" ? data.agent_type : undefined;
|
|
280
|
+
let matchedToolUseId;
|
|
281
|
+
for (const [pendingToolUseId, toolInput] of this.pendingAgentTools) {
|
|
282
|
+
const pendingAgentType = typeof toolInput.subagent_type === "string"
|
|
283
|
+
? toolInput.subagent_type
|
|
284
|
+
: typeof toolInput.agent_type === "string"
|
|
285
|
+
? toolInput.agent_type
|
|
286
|
+
: undefined;
|
|
287
|
+
if (agentType == null ||
|
|
288
|
+
pendingAgentType == null ||
|
|
289
|
+
pendingAgentType === agentType) {
|
|
290
|
+
matchedToolUseId = pendingToolUseId;
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
matchedToolUseId ??= this.pendingAgentTools.keys().next().value;
|
|
295
|
+
if (matchedToolUseId != null) {
|
|
296
|
+
this.agentToToolUseId.set(agentId, matchedToolUseId);
|
|
297
|
+
this.pendingAgentTools.delete(matchedToolUseId);
|
|
298
|
+
}
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
if (data.hook_event_name === "SubagentStop") {
|
|
302
|
+
const transcriptPath = typeof data.agent_transcript_path === "string"
|
|
303
|
+
? data.agent_transcript_path
|
|
304
|
+
: undefined;
|
|
305
|
+
if (!transcriptPath)
|
|
306
|
+
return;
|
|
307
|
+
const agentId = typeof data.agent_id === "string" ? data.agent_id : undefined;
|
|
308
|
+
const agentType = typeof data.agent_type === "string" ? data.agent_type : undefined;
|
|
309
|
+
const mappedToolUseId = agentId != null ? this.agentToToolUseId.get(agentId) : undefined;
|
|
310
|
+
if (agentId != null)
|
|
311
|
+
this.agentToToolUseId.delete(agentId);
|
|
312
|
+
this.addSubagentTranscriptPath(transcriptPath, mappedToolUseId, agentType);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
addSubagentTranscriptPath(path, toolUseId, agentType) {
|
|
316
|
+
const key = makeSubagentTranscriptPathKey(path, toolUseId, agentType);
|
|
317
|
+
if (this.transcriptPathKeys.has(key))
|
|
318
|
+
return;
|
|
319
|
+
this.transcriptPathKeys.add(key);
|
|
320
|
+
this.subagentTranscriptPaths.push({ path, toolUseId, agentType });
|
|
321
|
+
}
|
|
322
|
+
static getActiveToolRun(toolName, input) {
|
|
323
|
+
const currentRun = (0, traceable_js_1.getCurrentRunTree)(true);
|
|
324
|
+
const currentManager = currentRun != null
|
|
325
|
+
? StreamManager.managersByRootRun.get(currentRun)
|
|
326
|
+
: undefined;
|
|
327
|
+
const currentRunTree = currentManager?.getActiveToolRun(toolName, input);
|
|
328
|
+
if (currentRunTree != null)
|
|
329
|
+
return currentRunTree;
|
|
330
|
+
// Last resort: the SDK invoked an MCP handler from a detached async context
|
|
331
|
+
// that did not inherit the existing LangSmith AsyncLocalStorage. Require
|
|
332
|
+
// both tool name and input to match before scanning live managers to avoid
|
|
333
|
+
// cross-query attribution.
|
|
334
|
+
if (toolName == null || input === undefined)
|
|
335
|
+
return undefined;
|
|
336
|
+
for (const manager of Array.from(StreamManager.liveManagers).reverse()) {
|
|
337
|
+
if (manager === currentManager)
|
|
338
|
+
continue;
|
|
339
|
+
const runTree = manager.getActiveToolRun(toolName, input);
|
|
340
|
+
if (runTree != null)
|
|
341
|
+
return runTree;
|
|
342
|
+
}
|
|
343
|
+
return undefined;
|
|
344
|
+
}
|
|
345
|
+
getActiveToolRun(toolName, input) {
|
|
346
|
+
const toolEntries = Object.values(this.tools).filter((runTree) => runTree != null && runTree.end_time == null && runTree.error == null);
|
|
347
|
+
return toolEntries.find((runTree) => {
|
|
348
|
+
if (toolName != null) {
|
|
349
|
+
const runName = String(runTree.name);
|
|
350
|
+
const nameMatches = runName === toolName ||
|
|
351
|
+
runName.includes(toolName) ||
|
|
352
|
+
toolName.includes(runName);
|
|
353
|
+
if (!nameMatches)
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
if (input !== undefined) {
|
|
357
|
+
const recorded = runTree.inputs?.input ?? {};
|
|
358
|
+
try {
|
|
359
|
+
return JSON.stringify(recorded) === JSON.stringify(input ?? {});
|
|
360
|
+
}
|
|
361
|
+
catch {
|
|
362
|
+
return false;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return true;
|
|
366
|
+
});
|
|
367
|
+
}
|
|
192
368
|
createChild(namespace, args) {
|
|
193
|
-
const
|
|
369
|
+
const parentRunTree = this.namespaces[namespace];
|
|
370
|
+
if (parentRunTree == null)
|
|
371
|
+
return undefined;
|
|
372
|
+
return this.createChildRun(parentRunTree, args);
|
|
373
|
+
}
|
|
374
|
+
createChildRun(parentRunTree, args) {
|
|
375
|
+
const runTree = parentRunTree.createChild(args);
|
|
194
376
|
if (runTree == null)
|
|
195
377
|
return undefined;
|
|
196
378
|
this.postRunQueue.push(runTree.postRun());
|
|
197
379
|
this.runTrees.push(runTree);
|
|
198
380
|
return runTree;
|
|
199
381
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
382
|
+
getAgentName(block) {
|
|
383
|
+
const subagentType = block.input.subagent_type;
|
|
384
|
+
if (typeof subagentType === "string" && subagentType.length > 0) {
|
|
385
|
+
return subagentType;
|
|
386
|
+
}
|
|
387
|
+
const agentType = block.input.agent_type;
|
|
388
|
+
if (typeof agentType === "string" && agentType.length > 0) {
|
|
389
|
+
return agentType;
|
|
390
|
+
}
|
|
391
|
+
const description = block.input.description;
|
|
392
|
+
if (typeof description === "string" && description.length > 0) {
|
|
393
|
+
return description.split(" ")[0] || "unknown-agent";
|
|
394
|
+
}
|
|
395
|
+
return "unknown-agent";
|
|
396
|
+
}
|
|
397
|
+
createToolRun(namespace, block, startTime) {
|
|
398
|
+
if (typeof block.id !== "string")
|
|
399
|
+
return undefined;
|
|
400
|
+
const name = typeof block.name === "string" ? block.name : "unknown-tool";
|
|
401
|
+
this.tools[block.id] ??=
|
|
402
|
+
this.createChild(namespace, {
|
|
403
|
+
name,
|
|
404
|
+
run_type: "tool",
|
|
405
|
+
inputs: block.input ? { input: block.input } : {},
|
|
406
|
+
start_time: startTime,
|
|
407
|
+
}) ?? this.tools[block.id];
|
|
408
|
+
return this.tools[block.id];
|
|
409
|
+
}
|
|
410
|
+
createAgentToolRun(namespace, block, startTime) {
|
|
411
|
+
if (typeof block.id !== "string")
|
|
412
|
+
return;
|
|
413
|
+
if (typeof block.input !== "object" ||
|
|
414
|
+
block.input == null ||
|
|
415
|
+
Array.isArray(block.input)) {
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
const input = block.input;
|
|
419
|
+
const agentToolRun = this.createToolRun(namespace, block, startTime);
|
|
420
|
+
if (agentToolRun == null)
|
|
421
|
+
return;
|
|
422
|
+
this.subagents[block.id] ??=
|
|
423
|
+
this.createChildRun(agentToolRun, {
|
|
424
|
+
name: this.getAgentName({ input }),
|
|
425
|
+
run_type: "chain",
|
|
426
|
+
inputs: input,
|
|
427
|
+
start_time: startTime,
|
|
428
|
+
extra: {
|
|
429
|
+
metadata: {
|
|
430
|
+
ls_agent_type: "subagent",
|
|
431
|
+
},
|
|
432
|
+
},
|
|
433
|
+
}) ?? this.subagents[block.id];
|
|
434
|
+
this.namespaces[block.id] ??= this.subagents[block.id];
|
|
435
|
+
}
|
|
436
|
+
resolveSubagentNamespace(agentType) {
|
|
437
|
+
const entries = Object.entries(this.namespaces);
|
|
438
|
+
if (agentType == null)
|
|
439
|
+
return undefined;
|
|
440
|
+
return entries.find(([, runTree]) => runTree?.name === agentType)?.[0];
|
|
441
|
+
}
|
|
442
|
+
createSyntheticAssistantRun(namespace, turn) {
|
|
443
|
+
let runTree = this.assistant[turn.messageId];
|
|
444
|
+
if (runTree == null) {
|
|
445
|
+
runTree = this.createChild(namespace, {
|
|
446
|
+
name: "claude.assistant.turn",
|
|
447
|
+
run_type: "llm",
|
|
448
|
+
start_time: turn.timestamp,
|
|
449
|
+
inputs: turn.inputMessages.length > 0 ? { messages: turn.inputMessages } : {},
|
|
450
|
+
outputs: {
|
|
451
|
+
output: { messages: (0, messages_js_1.convertFromAnthropicMessage)(turn.message) },
|
|
452
|
+
},
|
|
453
|
+
extra: {
|
|
454
|
+
metadata: {
|
|
455
|
+
ls_provider: "anthropic",
|
|
456
|
+
...(turn.model != null ? { ls_model_name: turn.model } : {}),
|
|
457
|
+
...(turn.usageMetadata != null
|
|
458
|
+
? { usage_metadata: turn.usageMetadata }
|
|
459
|
+
: {}),
|
|
460
|
+
},
|
|
461
|
+
},
|
|
462
|
+
});
|
|
463
|
+
if (runTree == null)
|
|
464
|
+
return;
|
|
465
|
+
this.assistant[turn.messageId] = runTree;
|
|
466
|
+
}
|
|
467
|
+
else {
|
|
468
|
+
runTree.outputs = {
|
|
469
|
+
output: { messages: (0, messages_js_1.convertFromAnthropicMessage)(turn.message) },
|
|
470
|
+
};
|
|
471
|
+
runTree.extra ??= {};
|
|
472
|
+
runTree.extra.metadata ??= {};
|
|
473
|
+
if (turn.model != null)
|
|
474
|
+
runTree.extra.metadata.ls_model_name = turn.model;
|
|
475
|
+
if (turn.usageMetadata != null) {
|
|
476
|
+
runTree.extra.metadata.usage_metadata = turn.usageMetadata;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
runTree.end_time = turn.timestamp;
|
|
480
|
+
const tools = Array.isArray(turn.message.message.content)
|
|
481
|
+
? turn.message.message.content.filter((block) => (0, messages_js_1.isToolBlock)(block))
|
|
482
|
+
: [];
|
|
483
|
+
for (const block of tools) {
|
|
484
|
+
if ((0, messages_js_1.isTaskTool)(block)) {
|
|
485
|
+
this.createAgentToolRun(namespace, block, turn.timestamp);
|
|
486
|
+
}
|
|
487
|
+
else {
|
|
488
|
+
this.createToolRun(namespace, block, turn.timestamp);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
async reconcileTranscripts() {
|
|
493
|
+
const usageByMessageId = {};
|
|
494
|
+
if (this.mainTranscriptPath != null) {
|
|
495
|
+
const transcript = await (0, transcripts_js_1.readTranscript)(this.mainTranscriptPath);
|
|
496
|
+
Object.assign(usageByMessageId, transcript.usageByMessageId);
|
|
497
|
+
}
|
|
498
|
+
for (const transcriptPath of this.subagentTranscriptPaths) {
|
|
499
|
+
const namespace = transcriptPath.toolUseId ??
|
|
500
|
+
this.resolveSubagentNamespace(transcriptPath.agentType);
|
|
501
|
+
if (namespace == null || this.namespaces[namespace] == null)
|
|
204
502
|
continue;
|
|
205
|
-
|
|
206
|
-
|
|
503
|
+
const transcript = await (0, transcripts_js_1.readTranscript)(transcriptPath.path);
|
|
504
|
+
Object.assign(usageByMessageId, transcript.usageByMessageId);
|
|
505
|
+
for (const turn of transcript.turns) {
|
|
506
|
+
this.createSyntheticAssistantRun(namespace, turn);
|
|
507
|
+
}
|
|
508
|
+
for (const toolResult of transcript.toolResults) {
|
|
509
|
+
const tool = this.tools[toolResult.toolUseId];
|
|
510
|
+
if (tool == null)
|
|
511
|
+
continue;
|
|
512
|
+
const output = isRecord(toolResult.content)
|
|
513
|
+
? toolResult.content
|
|
514
|
+
: { content: toolResult.content };
|
|
515
|
+
const error = toolResult.isError
|
|
516
|
+
? typeof toolResult.content === "string" ||
|
|
517
|
+
typeof toolResult.content === "number" ||
|
|
518
|
+
typeof toolResult.content === "boolean"
|
|
519
|
+
? String(toolResult.content)
|
|
520
|
+
: JSON.stringify(toolResult.content)
|
|
521
|
+
: undefined;
|
|
522
|
+
await tool.end(output, error);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
for (const [messageId, usage] of Object.entries(usageByMessageId)) {
|
|
526
|
+
const runTree = this.assistant[messageId];
|
|
527
|
+
if (runTree == null)
|
|
528
|
+
continue;
|
|
529
|
+
runTree.extra ??= {};
|
|
530
|
+
runTree.extra.metadata ??= {};
|
|
531
|
+
runTree.extra.metadata.usage_metadata = usage;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
async finish() {
|
|
535
|
+
try {
|
|
536
|
+
await this.reconcileTranscripts();
|
|
537
|
+
if (this.resultModelUsage != null) {
|
|
538
|
+
(0, usage_js_1.correctUsageFromResults)(this.resultModelUsage, Object.values(this.assistant).filter((runTree) => runTree != null));
|
|
539
|
+
}
|
|
540
|
+
// Clean up incomplete tools and finalise subagent calls. This mirrors the
|
|
541
|
+
// Python integration: Agent/Task tool runs are ended when their tool result
|
|
542
|
+
// arrives, while subagent chain runs are finalised only after transcript
|
|
543
|
+
// reconciliation so hidden child LLM/tool runs are created first.
|
|
544
|
+
for (const tool of Object.values(this.tools)) {
|
|
545
|
+
if (tool == null)
|
|
546
|
+
continue;
|
|
547
|
+
if (tool.outputs == null && tool.error == null) {
|
|
548
|
+
await tool.end(undefined, "Run not completed (conversation ended)");
|
|
549
|
+
}
|
|
207
550
|
}
|
|
551
|
+
for (const subagent of Object.values(this.subagents)) {
|
|
552
|
+
if (subagent == null)
|
|
553
|
+
continue;
|
|
554
|
+
if (subagent.end_time == null) {
|
|
555
|
+
if (subagent.outputs == null && subagent.error == null) {
|
|
556
|
+
await subagent.end(undefined, "Run not completed (conversation ended)");
|
|
557
|
+
}
|
|
558
|
+
else {
|
|
559
|
+
await subagent.end();
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
// First make sure all the runs are created
|
|
564
|
+
await Promise.allSettled(this.postRunQueue);
|
|
565
|
+
// Then patch the runs
|
|
566
|
+
await Promise.allSettled(this.runTrees.map((runTree) => runTree.patchRun()));
|
|
567
|
+
}
|
|
568
|
+
finally {
|
|
569
|
+
this.dispose();
|
|
208
570
|
}
|
|
209
|
-
// First make sure all the runs are created
|
|
210
|
-
await Promise.allSettled(this.postRunQueue);
|
|
211
|
-
// Then patch the runs
|
|
212
|
-
await Promise.allSettled(this.runTrees.map((runTree) => runTree.patchRun()));
|
|
213
571
|
}
|
|
214
572
|
}
|
|
215
573
|
exports.StreamManager = StreamManager;
|
|
574
|
+
Object.defineProperty(StreamManager, "liveManagers", {
|
|
575
|
+
enumerable: true,
|
|
576
|
+
configurable: true,
|
|
577
|
+
writable: true,
|
|
578
|
+
value: new Set()
|
|
579
|
+
});
|
|
580
|
+
Object.defineProperty(StreamManager, "managersByRootRun", {
|
|
581
|
+
enumerable: true,
|
|
582
|
+
configurable: true,
|
|
583
|
+
writable: true,
|
|
584
|
+
value: new WeakMap()
|
|
585
|
+
});
|