langsmith 0.5.25 → 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 +6 -2
- package/dist/client.js +6 -2
- 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/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
|
@@ -1,11 +1,27 @@
|
|
|
1
|
-
import { convertFromAnthropicMessage, isTaskTool, isToolBlock, } from "./messages.js";
|
|
1
|
+
import { convertFromAnthropicMessage, isTaskTool, isToolBlock, mergeMessagesById, } from "./messages.js";
|
|
2
2
|
import { getCurrentRunTree } from "../../traceable.js";
|
|
3
3
|
import { aggregateUsageFromModelUsage, correctUsageFromResults, extractUsageFromMessage, } from "./usage.js";
|
|
4
|
+
import { readTranscript } from "./transcripts.js";
|
|
5
|
+
function isRecord(value) {
|
|
6
|
+
return typeof value === "object" && value != null && !Array.isArray(value);
|
|
7
|
+
}
|
|
8
|
+
function isToolResultError(value) {
|
|
9
|
+
return isRecord(value) && (value.is_error === true || value.isError === true);
|
|
10
|
+
}
|
|
11
|
+
function makeSubagentTranscriptPathKey(path, toolUseId, agentType) {
|
|
12
|
+
return JSON.stringify([path, toolUseId ?? null, agentType ?? null]);
|
|
13
|
+
}
|
|
4
14
|
/**
|
|
5
15
|
* @internal
|
|
6
16
|
*/
|
|
7
17
|
export class StreamManager {
|
|
8
18
|
constructor() {
|
|
19
|
+
Object.defineProperty(this, "rootRun", {
|
|
20
|
+
enumerable: true,
|
|
21
|
+
configurable: true,
|
|
22
|
+
writable: true,
|
|
23
|
+
value: void 0
|
|
24
|
+
});
|
|
9
25
|
Object.defineProperty(this, "namespaces", {
|
|
10
26
|
enumerable: true,
|
|
11
27
|
configurable: true,
|
|
@@ -30,6 +46,48 @@ export class StreamManager {
|
|
|
30
46
|
writable: true,
|
|
31
47
|
value: {}
|
|
32
48
|
});
|
|
49
|
+
Object.defineProperty(this, "subagents", {
|
|
50
|
+
enumerable: true,
|
|
51
|
+
configurable: true,
|
|
52
|
+
writable: true,
|
|
53
|
+
value: {}
|
|
54
|
+
});
|
|
55
|
+
Object.defineProperty(this, "mainTranscriptPath", {
|
|
56
|
+
enumerable: true,
|
|
57
|
+
configurable: true,
|
|
58
|
+
writable: true,
|
|
59
|
+
value: void 0
|
|
60
|
+
});
|
|
61
|
+
Object.defineProperty(this, "subagentTranscriptPaths", {
|
|
62
|
+
enumerable: true,
|
|
63
|
+
configurable: true,
|
|
64
|
+
writable: true,
|
|
65
|
+
value: []
|
|
66
|
+
});
|
|
67
|
+
Object.defineProperty(this, "pendingAgentTools", {
|
|
68
|
+
enumerable: true,
|
|
69
|
+
configurable: true,
|
|
70
|
+
writable: true,
|
|
71
|
+
value: new Map()
|
|
72
|
+
});
|
|
73
|
+
Object.defineProperty(this, "agentToToolUseId", {
|
|
74
|
+
enumerable: true,
|
|
75
|
+
configurable: true,
|
|
76
|
+
writable: true,
|
|
77
|
+
value: new Map()
|
|
78
|
+
});
|
|
79
|
+
Object.defineProperty(this, "transcriptPathKeys", {
|
|
80
|
+
enumerable: true,
|
|
81
|
+
configurable: true,
|
|
82
|
+
writable: true,
|
|
83
|
+
value: new Set()
|
|
84
|
+
});
|
|
85
|
+
Object.defineProperty(this, "resultModelUsage", {
|
|
86
|
+
enumerable: true,
|
|
87
|
+
configurable: true,
|
|
88
|
+
writable: true,
|
|
89
|
+
value: void 0
|
|
90
|
+
});
|
|
33
91
|
Object.defineProperty(this, "postRunQueue", {
|
|
34
92
|
enumerable: true,
|
|
35
93
|
configurable: true,
|
|
@@ -43,10 +101,21 @@ export class StreamManager {
|
|
|
43
101
|
value: []
|
|
44
102
|
});
|
|
45
103
|
const rootRun = getCurrentRunTree(true);
|
|
104
|
+
this.rootRun = rootRun;
|
|
46
105
|
this.namespaces = rootRun?.createChild ? { root: rootRun } : {};
|
|
47
106
|
this.history = { root: [] };
|
|
107
|
+
if (rootRun != null) {
|
|
108
|
+
StreamManager.managersByRootRun.set(rootRun, this);
|
|
109
|
+
}
|
|
110
|
+
StreamManager.liveManagers.add(this);
|
|
48
111
|
}
|
|
49
|
-
|
|
112
|
+
dispose() {
|
|
113
|
+
StreamManager.liveManagers.delete(this);
|
|
114
|
+
if (this.rootRun != null) {
|
|
115
|
+
StreamManager.managersByRootRun.delete(this.rootRun);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
async addMessage(message) {
|
|
50
119
|
const eventTime = Date.now();
|
|
51
120
|
// Short-circuit if no root run found
|
|
52
121
|
// This can happen if tracing is disabled globally
|
|
@@ -54,7 +123,7 @@ export class StreamManager {
|
|
|
54
123
|
return;
|
|
55
124
|
if (message.type === "result") {
|
|
56
125
|
if (message.modelUsage) {
|
|
57
|
-
|
|
126
|
+
this.resultModelUsage = message.modelUsage;
|
|
58
127
|
}
|
|
59
128
|
const usage = message.modelUsage
|
|
60
129
|
? aggregateUsageFromModelUsage(message.modelUsage)
|
|
@@ -102,7 +171,11 @@ export class StreamManager {
|
|
|
102
171
|
this.assistant[messageId].outputs = (() => {
|
|
103
172
|
const prevMessages = this.assistant[messageId].outputs?.output.messages ?? [];
|
|
104
173
|
const newMessages = convertFromAnthropicMessage([message]);
|
|
105
|
-
return {
|
|
174
|
+
return {
|
|
175
|
+
output: {
|
|
176
|
+
messages: mergeMessagesById(prevMessages, newMessages),
|
|
177
|
+
},
|
|
178
|
+
};
|
|
106
179
|
})();
|
|
107
180
|
this.assistant[messageId].end_time = eventTime;
|
|
108
181
|
this.assistant[messageId].extra ??= {};
|
|
@@ -118,35 +191,10 @@ export class StreamManager {
|
|
|
118
191
|
: [];
|
|
119
192
|
for (const block of tools) {
|
|
120
193
|
if (isTaskTool(block)) {
|
|
121
|
-
|
|
122
|
-
block.input.agent_type ||
|
|
123
|
-
(block.input.description
|
|
124
|
-
? block.input.description.split(" ")[0]
|
|
125
|
-
: null) ||
|
|
126
|
-
"unknown-agent";
|
|
127
|
-
this.tools[block.id] ??=
|
|
128
|
-
this.createChild("root", {
|
|
129
|
-
name,
|
|
130
|
-
run_type: "chain",
|
|
131
|
-
inputs: block.input,
|
|
132
|
-
start_time: eventTime,
|
|
133
|
-
extra: {
|
|
134
|
-
metadata: {
|
|
135
|
-
ls_agent_type: "subagent",
|
|
136
|
-
},
|
|
137
|
-
},
|
|
138
|
-
}) ?? this.tools[block.id];
|
|
139
|
-
this.namespaces[block.id] ??= this.tools[block.id];
|
|
194
|
+
this.createAgentToolRun(namespace, block, eventTime);
|
|
140
195
|
}
|
|
141
196
|
else {
|
|
142
|
-
|
|
143
|
-
this.tools[block.id] ??=
|
|
144
|
-
this.createChild(namespace, {
|
|
145
|
-
name,
|
|
146
|
-
run_type: "tool",
|
|
147
|
-
inputs: block.input ? { input: block.input } : {},
|
|
148
|
-
start_time: eventTime,
|
|
149
|
-
}) ?? this.tools[block.id];
|
|
197
|
+
this.createToolRun(namespace, block, eventTime);
|
|
150
198
|
}
|
|
151
199
|
}
|
|
152
200
|
}
|
|
@@ -155,9 +203,7 @@ export class StreamManager {
|
|
|
155
203
|
? message.message.content.filter((block) => "tool_use_id" in block)
|
|
156
204
|
: [];
|
|
157
205
|
const getToolOutput = (result) => {
|
|
158
|
-
if (
|
|
159
|
-
result != null &&
|
|
160
|
-
!Array.isArray(result)) {
|
|
206
|
+
if (isRecord(result)) {
|
|
161
207
|
return result;
|
|
162
208
|
}
|
|
163
209
|
return { content: result };
|
|
@@ -166,10 +212,23 @@ export class StreamManager {
|
|
|
166
212
|
if (["string", "number", "boolean"].includes(typeof result)) {
|
|
167
213
|
return String(result);
|
|
168
214
|
}
|
|
215
|
+
if (Array.isArray(result)) {
|
|
216
|
+
return result.map(getToolError).join("\n");
|
|
217
|
+
}
|
|
218
|
+
if (isRecord(result)) {
|
|
219
|
+
if (typeof result.error === "string")
|
|
220
|
+
return result.error;
|
|
221
|
+
if (typeof result.text === "string")
|
|
222
|
+
return result.text;
|
|
223
|
+
if ("content" in result)
|
|
224
|
+
return getToolError(result.content);
|
|
225
|
+
}
|
|
169
226
|
return JSON.stringify(result);
|
|
170
227
|
};
|
|
171
228
|
for (const block of toolResultBlocks) {
|
|
172
|
-
|
|
229
|
+
const tool = this.tools[block.tool_use_id];
|
|
230
|
+
const subagent = this.subagents[block.tool_use_id];
|
|
231
|
+
if (tool != null || subagent != null) {
|
|
173
232
|
// Previous versions of @anthropic-ai/claude-agent-sdk did provide
|
|
174
233
|
// tool result in `message.tool_use_result`, but at least since 0.2.50 it disappeared,
|
|
175
234
|
// so we rely on the last tool result block instead.
|
|
@@ -177,35 +236,346 @@ export class StreamManager {
|
|
|
177
236
|
? message.tool_use_result
|
|
178
237
|
: block.content;
|
|
179
238
|
const toolOutput = getToolOutput(result);
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
239
|
+
const isError = isToolResultError(block) || isToolResultError(result);
|
|
240
|
+
const toolError = isError ? getToolError(result) : undefined;
|
|
241
|
+
await tool?.end(toolOutput, toolError);
|
|
242
|
+
// Match Python's lifecycle: PostToolUse sets outputs on the
|
|
243
|
+
// subagent chain, but the subagent itself is not ended until after
|
|
244
|
+
// transcript reconciliation. Hidden transcript LLM/tool children can
|
|
245
|
+
// arrive after the Agent/Task tool result, so ending the chain here
|
|
246
|
+
// can make reconciled children appear outside their parent bounds.
|
|
247
|
+
if (subagent != null) {
|
|
248
|
+
subagent.outputs ??= toolOutput;
|
|
249
|
+
subagent.error ??= toolError;
|
|
250
|
+
}
|
|
184
251
|
}
|
|
185
252
|
}
|
|
186
253
|
}
|
|
187
254
|
this.history[namespace].push(message);
|
|
188
255
|
}
|
|
256
|
+
addHookEvent(input, toolUseId) {
|
|
257
|
+
if (typeof input !== "object" || input == null)
|
|
258
|
+
return;
|
|
259
|
+
const data = input;
|
|
260
|
+
if (this.mainTranscriptPath == null &&
|
|
261
|
+
typeof data.transcript_path === "string") {
|
|
262
|
+
this.mainTranscriptPath = data.transcript_path;
|
|
263
|
+
}
|
|
264
|
+
if (data.hook_event_name === "PreToolUse" &&
|
|
265
|
+
typeof toolUseId === "string" &&
|
|
266
|
+
(data.tool_name === "Agent" || data.tool_name === "Task")) {
|
|
267
|
+
this.pendingAgentTools.set(toolUseId, typeof data.tool_input === "object" && data.tool_input != null
|
|
268
|
+
? data.tool_input
|
|
269
|
+
: {});
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
if (data.hook_event_name === "SubagentStart") {
|
|
273
|
+
const agentId = typeof data.agent_id === "string" ? data.agent_id : undefined;
|
|
274
|
+
if (agentId == null)
|
|
275
|
+
return;
|
|
276
|
+
const agentType = typeof data.agent_type === "string" ? data.agent_type : undefined;
|
|
277
|
+
let matchedToolUseId;
|
|
278
|
+
for (const [pendingToolUseId, toolInput] of this.pendingAgentTools) {
|
|
279
|
+
const pendingAgentType = typeof toolInput.subagent_type === "string"
|
|
280
|
+
? toolInput.subagent_type
|
|
281
|
+
: typeof toolInput.agent_type === "string"
|
|
282
|
+
? toolInput.agent_type
|
|
283
|
+
: undefined;
|
|
284
|
+
if (agentType == null ||
|
|
285
|
+
pendingAgentType == null ||
|
|
286
|
+
pendingAgentType === agentType) {
|
|
287
|
+
matchedToolUseId = pendingToolUseId;
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
matchedToolUseId ??= this.pendingAgentTools.keys().next().value;
|
|
292
|
+
if (matchedToolUseId != null) {
|
|
293
|
+
this.agentToToolUseId.set(agentId, matchedToolUseId);
|
|
294
|
+
this.pendingAgentTools.delete(matchedToolUseId);
|
|
295
|
+
}
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
if (data.hook_event_name === "SubagentStop") {
|
|
299
|
+
const transcriptPath = typeof data.agent_transcript_path === "string"
|
|
300
|
+
? data.agent_transcript_path
|
|
301
|
+
: undefined;
|
|
302
|
+
if (!transcriptPath)
|
|
303
|
+
return;
|
|
304
|
+
const agentId = typeof data.agent_id === "string" ? data.agent_id : undefined;
|
|
305
|
+
const agentType = typeof data.agent_type === "string" ? data.agent_type : undefined;
|
|
306
|
+
const mappedToolUseId = agentId != null ? this.agentToToolUseId.get(agentId) : undefined;
|
|
307
|
+
if (agentId != null)
|
|
308
|
+
this.agentToToolUseId.delete(agentId);
|
|
309
|
+
this.addSubagentTranscriptPath(transcriptPath, mappedToolUseId, agentType);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
addSubagentTranscriptPath(path, toolUseId, agentType) {
|
|
313
|
+
const key = makeSubagentTranscriptPathKey(path, toolUseId, agentType);
|
|
314
|
+
if (this.transcriptPathKeys.has(key))
|
|
315
|
+
return;
|
|
316
|
+
this.transcriptPathKeys.add(key);
|
|
317
|
+
this.subagentTranscriptPaths.push({ path, toolUseId, agentType });
|
|
318
|
+
}
|
|
319
|
+
static getActiveToolRun(toolName, input) {
|
|
320
|
+
const currentRun = getCurrentRunTree(true);
|
|
321
|
+
const currentManager = currentRun != null
|
|
322
|
+
? StreamManager.managersByRootRun.get(currentRun)
|
|
323
|
+
: undefined;
|
|
324
|
+
const currentRunTree = currentManager?.getActiveToolRun(toolName, input);
|
|
325
|
+
if (currentRunTree != null)
|
|
326
|
+
return currentRunTree;
|
|
327
|
+
// Last resort: the SDK invoked an MCP handler from a detached async context
|
|
328
|
+
// that did not inherit the existing LangSmith AsyncLocalStorage. Require
|
|
329
|
+
// both tool name and input to match before scanning live managers to avoid
|
|
330
|
+
// cross-query attribution.
|
|
331
|
+
if (toolName == null || input === undefined)
|
|
332
|
+
return undefined;
|
|
333
|
+
for (const manager of Array.from(StreamManager.liveManagers).reverse()) {
|
|
334
|
+
if (manager === currentManager)
|
|
335
|
+
continue;
|
|
336
|
+
const runTree = manager.getActiveToolRun(toolName, input);
|
|
337
|
+
if (runTree != null)
|
|
338
|
+
return runTree;
|
|
339
|
+
}
|
|
340
|
+
return undefined;
|
|
341
|
+
}
|
|
342
|
+
getActiveToolRun(toolName, input) {
|
|
343
|
+
const toolEntries = Object.values(this.tools).filter((runTree) => runTree != null && runTree.end_time == null && runTree.error == null);
|
|
344
|
+
return toolEntries.find((runTree) => {
|
|
345
|
+
if (toolName != null) {
|
|
346
|
+
const runName = String(runTree.name);
|
|
347
|
+
const nameMatches = runName === toolName ||
|
|
348
|
+
runName.includes(toolName) ||
|
|
349
|
+
toolName.includes(runName);
|
|
350
|
+
if (!nameMatches)
|
|
351
|
+
return false;
|
|
352
|
+
}
|
|
353
|
+
if (input !== undefined) {
|
|
354
|
+
const recorded = runTree.inputs?.input ?? {};
|
|
355
|
+
try {
|
|
356
|
+
return JSON.stringify(recorded) === JSON.stringify(input ?? {});
|
|
357
|
+
}
|
|
358
|
+
catch {
|
|
359
|
+
return false;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return true;
|
|
363
|
+
});
|
|
364
|
+
}
|
|
189
365
|
createChild(namespace, args) {
|
|
190
|
-
const
|
|
366
|
+
const parentRunTree = this.namespaces[namespace];
|
|
367
|
+
if (parentRunTree == null)
|
|
368
|
+
return undefined;
|
|
369
|
+
return this.createChildRun(parentRunTree, args);
|
|
370
|
+
}
|
|
371
|
+
createChildRun(parentRunTree, args) {
|
|
372
|
+
const runTree = parentRunTree.createChild(args);
|
|
191
373
|
if (runTree == null)
|
|
192
374
|
return undefined;
|
|
193
375
|
this.postRunQueue.push(runTree.postRun());
|
|
194
376
|
this.runTrees.push(runTree);
|
|
195
377
|
return runTree;
|
|
196
378
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
379
|
+
getAgentName(block) {
|
|
380
|
+
const subagentType = block.input.subagent_type;
|
|
381
|
+
if (typeof subagentType === "string" && subagentType.length > 0) {
|
|
382
|
+
return subagentType;
|
|
383
|
+
}
|
|
384
|
+
const agentType = block.input.agent_type;
|
|
385
|
+
if (typeof agentType === "string" && agentType.length > 0) {
|
|
386
|
+
return agentType;
|
|
387
|
+
}
|
|
388
|
+
const description = block.input.description;
|
|
389
|
+
if (typeof description === "string" && description.length > 0) {
|
|
390
|
+
return description.split(" ")[0] || "unknown-agent";
|
|
391
|
+
}
|
|
392
|
+
return "unknown-agent";
|
|
393
|
+
}
|
|
394
|
+
createToolRun(namespace, block, startTime) {
|
|
395
|
+
if (typeof block.id !== "string")
|
|
396
|
+
return undefined;
|
|
397
|
+
const name = typeof block.name === "string" ? block.name : "unknown-tool";
|
|
398
|
+
this.tools[block.id] ??=
|
|
399
|
+
this.createChild(namespace, {
|
|
400
|
+
name,
|
|
401
|
+
run_type: "tool",
|
|
402
|
+
inputs: block.input ? { input: block.input } : {},
|
|
403
|
+
start_time: startTime,
|
|
404
|
+
}) ?? this.tools[block.id];
|
|
405
|
+
return this.tools[block.id];
|
|
406
|
+
}
|
|
407
|
+
createAgentToolRun(namespace, block, startTime) {
|
|
408
|
+
if (typeof block.id !== "string")
|
|
409
|
+
return;
|
|
410
|
+
if (typeof block.input !== "object" ||
|
|
411
|
+
block.input == null ||
|
|
412
|
+
Array.isArray(block.input)) {
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
const input = block.input;
|
|
416
|
+
const agentToolRun = this.createToolRun(namespace, block, startTime);
|
|
417
|
+
if (agentToolRun == null)
|
|
418
|
+
return;
|
|
419
|
+
this.subagents[block.id] ??=
|
|
420
|
+
this.createChildRun(agentToolRun, {
|
|
421
|
+
name: this.getAgentName({ input }),
|
|
422
|
+
run_type: "chain",
|
|
423
|
+
inputs: input,
|
|
424
|
+
start_time: startTime,
|
|
425
|
+
extra: {
|
|
426
|
+
metadata: {
|
|
427
|
+
ls_agent_type: "subagent",
|
|
428
|
+
},
|
|
429
|
+
},
|
|
430
|
+
}) ?? this.subagents[block.id];
|
|
431
|
+
this.namespaces[block.id] ??= this.subagents[block.id];
|
|
432
|
+
}
|
|
433
|
+
resolveSubagentNamespace(agentType) {
|
|
434
|
+
const entries = Object.entries(this.namespaces);
|
|
435
|
+
if (agentType == null)
|
|
436
|
+
return undefined;
|
|
437
|
+
return entries.find(([, runTree]) => runTree?.name === agentType)?.[0];
|
|
438
|
+
}
|
|
439
|
+
createSyntheticAssistantRun(namespace, turn) {
|
|
440
|
+
let runTree = this.assistant[turn.messageId];
|
|
441
|
+
if (runTree == null) {
|
|
442
|
+
runTree = this.createChild(namespace, {
|
|
443
|
+
name: "claude.assistant.turn",
|
|
444
|
+
run_type: "llm",
|
|
445
|
+
start_time: turn.timestamp,
|
|
446
|
+
inputs: turn.inputMessages.length > 0 ? { messages: turn.inputMessages } : {},
|
|
447
|
+
outputs: {
|
|
448
|
+
output: { messages: convertFromAnthropicMessage(turn.message) },
|
|
449
|
+
},
|
|
450
|
+
extra: {
|
|
451
|
+
metadata: {
|
|
452
|
+
ls_provider: "anthropic",
|
|
453
|
+
...(turn.model != null ? { ls_model_name: turn.model } : {}),
|
|
454
|
+
...(turn.usageMetadata != null
|
|
455
|
+
? { usage_metadata: turn.usageMetadata }
|
|
456
|
+
: {}),
|
|
457
|
+
},
|
|
458
|
+
},
|
|
459
|
+
});
|
|
460
|
+
if (runTree == null)
|
|
461
|
+
return;
|
|
462
|
+
this.assistant[turn.messageId] = runTree;
|
|
463
|
+
}
|
|
464
|
+
else {
|
|
465
|
+
runTree.outputs = {
|
|
466
|
+
output: { messages: convertFromAnthropicMessage(turn.message) },
|
|
467
|
+
};
|
|
468
|
+
runTree.extra ??= {};
|
|
469
|
+
runTree.extra.metadata ??= {};
|
|
470
|
+
if (turn.model != null)
|
|
471
|
+
runTree.extra.metadata.ls_model_name = turn.model;
|
|
472
|
+
if (turn.usageMetadata != null) {
|
|
473
|
+
runTree.extra.metadata.usage_metadata = turn.usageMetadata;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
runTree.end_time = turn.timestamp;
|
|
477
|
+
const tools = Array.isArray(turn.message.message.content)
|
|
478
|
+
? turn.message.message.content.filter((block) => isToolBlock(block))
|
|
479
|
+
: [];
|
|
480
|
+
for (const block of tools) {
|
|
481
|
+
if (isTaskTool(block)) {
|
|
482
|
+
this.createAgentToolRun(namespace, block, turn.timestamp);
|
|
483
|
+
}
|
|
484
|
+
else {
|
|
485
|
+
this.createToolRun(namespace, block, turn.timestamp);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
async reconcileTranscripts() {
|
|
490
|
+
const usageByMessageId = {};
|
|
491
|
+
if (this.mainTranscriptPath != null) {
|
|
492
|
+
const transcript = await readTranscript(this.mainTranscriptPath);
|
|
493
|
+
Object.assign(usageByMessageId, transcript.usageByMessageId);
|
|
494
|
+
}
|
|
495
|
+
for (const transcriptPath of this.subagentTranscriptPaths) {
|
|
496
|
+
const namespace = transcriptPath.toolUseId ??
|
|
497
|
+
this.resolveSubagentNamespace(transcriptPath.agentType);
|
|
498
|
+
if (namespace == null || this.namespaces[namespace] == null)
|
|
201
499
|
continue;
|
|
202
|
-
|
|
203
|
-
|
|
500
|
+
const transcript = await readTranscript(transcriptPath.path);
|
|
501
|
+
Object.assign(usageByMessageId, transcript.usageByMessageId);
|
|
502
|
+
for (const turn of transcript.turns) {
|
|
503
|
+
this.createSyntheticAssistantRun(namespace, turn);
|
|
504
|
+
}
|
|
505
|
+
for (const toolResult of transcript.toolResults) {
|
|
506
|
+
const tool = this.tools[toolResult.toolUseId];
|
|
507
|
+
if (tool == null)
|
|
508
|
+
continue;
|
|
509
|
+
const output = isRecord(toolResult.content)
|
|
510
|
+
? toolResult.content
|
|
511
|
+
: { content: toolResult.content };
|
|
512
|
+
const error = toolResult.isError
|
|
513
|
+
? typeof toolResult.content === "string" ||
|
|
514
|
+
typeof toolResult.content === "number" ||
|
|
515
|
+
typeof toolResult.content === "boolean"
|
|
516
|
+
? String(toolResult.content)
|
|
517
|
+
: JSON.stringify(toolResult.content)
|
|
518
|
+
: undefined;
|
|
519
|
+
await tool.end(output, error);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
for (const [messageId, usage] of Object.entries(usageByMessageId)) {
|
|
523
|
+
const runTree = this.assistant[messageId];
|
|
524
|
+
if (runTree == null)
|
|
525
|
+
continue;
|
|
526
|
+
runTree.extra ??= {};
|
|
527
|
+
runTree.extra.metadata ??= {};
|
|
528
|
+
runTree.extra.metadata.usage_metadata = usage;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
async finish() {
|
|
532
|
+
try {
|
|
533
|
+
await this.reconcileTranscripts();
|
|
534
|
+
if (this.resultModelUsage != null) {
|
|
535
|
+
correctUsageFromResults(this.resultModelUsage, Object.values(this.assistant).filter((runTree) => runTree != null));
|
|
536
|
+
}
|
|
537
|
+
// Clean up incomplete tools and finalise subagent calls. This mirrors the
|
|
538
|
+
// Python integration: Agent/Task tool runs are ended when their tool result
|
|
539
|
+
// arrives, while subagent chain runs are finalised only after transcript
|
|
540
|
+
// reconciliation so hidden child LLM/tool runs are created first.
|
|
541
|
+
for (const tool of Object.values(this.tools)) {
|
|
542
|
+
if (tool == null)
|
|
543
|
+
continue;
|
|
544
|
+
if (tool.outputs == null && tool.error == null) {
|
|
545
|
+
await tool.end(undefined, "Run not completed (conversation ended)");
|
|
546
|
+
}
|
|
204
547
|
}
|
|
548
|
+
for (const subagent of Object.values(this.subagents)) {
|
|
549
|
+
if (subagent == null)
|
|
550
|
+
continue;
|
|
551
|
+
if (subagent.end_time == null) {
|
|
552
|
+
if (subagent.outputs == null && subagent.error == null) {
|
|
553
|
+
await subagent.end(undefined, "Run not completed (conversation ended)");
|
|
554
|
+
}
|
|
555
|
+
else {
|
|
556
|
+
await subagent.end();
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
// First make sure all the runs are created
|
|
561
|
+
await Promise.allSettled(this.postRunQueue);
|
|
562
|
+
// Then patch the runs
|
|
563
|
+
await Promise.allSettled(this.runTrees.map((runTree) => runTree.patchRun()));
|
|
564
|
+
}
|
|
565
|
+
finally {
|
|
566
|
+
this.dispose();
|
|
205
567
|
}
|
|
206
|
-
// First make sure all the runs are created
|
|
207
|
-
await Promise.allSettled(this.postRunQueue);
|
|
208
|
-
// Then patch the runs
|
|
209
|
-
await Promise.allSettled(this.runTrees.map((runTree) => runTree.patchRun()));
|
|
210
568
|
}
|
|
211
569
|
}
|
|
570
|
+
Object.defineProperty(StreamManager, "liveManagers", {
|
|
571
|
+
enumerable: true,
|
|
572
|
+
configurable: true,
|
|
573
|
+
writable: true,
|
|
574
|
+
value: new Set()
|
|
575
|
+
});
|
|
576
|
+
Object.defineProperty(StreamManager, "managersByRootRun", {
|
|
577
|
+
enumerable: true,
|
|
578
|
+
configurable: true,
|
|
579
|
+
writable: true,
|
|
580
|
+
value: new WeakMap()
|
|
581
|
+
});
|