langsmith 0.4.8 → 0.4.10

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.
@@ -0,0 +1,187 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.StreamManager = void 0;
4
+ const messages_js_1 = require("./messages.cjs");
5
+ const traceable_js_1 = require("../../traceable.cjs");
6
+ const usage_js_1 = require("./usage.cjs");
7
+ /**
8
+ * @internal
9
+ */
10
+ class StreamManager {
11
+ constructor() {
12
+ Object.defineProperty(this, "namespaces", {
13
+ enumerable: true,
14
+ configurable: true,
15
+ writable: true,
16
+ value: void 0
17
+ });
18
+ Object.defineProperty(this, "history", {
19
+ enumerable: true,
20
+ configurable: true,
21
+ writable: true,
22
+ value: void 0
23
+ });
24
+ Object.defineProperty(this, "assistant", {
25
+ enumerable: true,
26
+ configurable: true,
27
+ writable: true,
28
+ value: {}
29
+ });
30
+ Object.defineProperty(this, "tools", {
31
+ enumerable: true,
32
+ configurable: true,
33
+ writable: true,
34
+ value: {}
35
+ });
36
+ Object.defineProperty(this, "postRunQueue", {
37
+ enumerable: true,
38
+ configurable: true,
39
+ writable: true,
40
+ value: []
41
+ });
42
+ Object.defineProperty(this, "runTrees", {
43
+ enumerable: true,
44
+ configurable: true,
45
+ writable: true,
46
+ value: []
47
+ });
48
+ const rootRun = (0, traceable_js_1.getCurrentRunTree)(true);
49
+ this.namespaces = rootRun?.createChild ? { root: rootRun } : {};
50
+ this.history = { root: [] };
51
+ }
52
+ addMessage(message) {
53
+ const eventTime = Date.now();
54
+ // Short-circuit if no root run found
55
+ // This can happen if tracing is disabled globally
56
+ if (this.namespaces["root"] == null)
57
+ return;
58
+ if (message.type === "result") {
59
+ if (message.modelUsage) {
60
+ (0, usage_js_1.correctUsageFromResults)(message.modelUsage, Object.values(this.assistant));
61
+ }
62
+ const usage = message.modelUsage
63
+ ? (0, usage_js_1.aggregateUsageFromModelUsage)(message.modelUsage)
64
+ : (0, usage_js_1.extractUsageFromMessage)(message);
65
+ if (message.total_cost_usd != null && usage != null) {
66
+ usage.total_cost = message.total_cost_usd;
67
+ }
68
+ this.namespaces["root"].extra ??= {};
69
+ this.namespaces["root"].extra.metadata ??= {};
70
+ this.namespaces["root"].extra.metadata.usage_metadata = usage;
71
+ this.namespaces["root"].extra.metadata.is_error = message.is_error;
72
+ this.namespaces["root"].extra.metadata.num_turns = message.num_turns;
73
+ this.namespaces["root"].extra.metadata.session_id = message.session_id;
74
+ this.namespaces["root"].extra.metadata.duration_ms = message.duration_ms;
75
+ this.namespaces["root"].extra.metadata.duration_api_ms =
76
+ message.duration_api_ms;
77
+ }
78
+ // Skip non-user / non-assistant messages
79
+ if (!("message" in message))
80
+ return;
81
+ const namespace = (() => {
82
+ if ("parent_tool_use_id" in message)
83
+ return message.parent_tool_use_id ?? "root";
84
+ return "root";
85
+ })();
86
+ // `eventTime` records the time we receive an event, which for `includePartialMessages: false`
87
+ // equals to the end time of an LLM block, so we need to use the first available end time within namespace.
88
+ const candidateStartTime = this.namespaces[namespace]?.child_runs?.at(-1)?.end_time ??
89
+ this.namespaces[namespace]?.start_time ??
90
+ eventTime;
91
+ this.history[namespace] ??= this.history["root"].slice();
92
+ if (message.type === "assistant") {
93
+ const messageId = message.message.id;
94
+ this.assistant[messageId] ??= this.createChild(namespace, {
95
+ name: "claude.assistant.turn",
96
+ run_type: "llm",
97
+ start_time: candidateStartTime,
98
+ inputs: {
99
+ messages: (0, messages_js_1.convertFromAnthropicMessage)(this.history[namespace]),
100
+ },
101
+ outputs: { output: { messages: [] } },
102
+ });
103
+ this.assistant[messageId].outputs = (() => {
104
+ const prevMessages = this.assistant[messageId].outputs?.output.messages ?? [];
105
+ const newMessages = (0, messages_js_1.convertFromAnthropicMessage)([message]);
106
+ return { output: { messages: [...prevMessages, ...newMessages] } };
107
+ })();
108
+ this.assistant[messageId].end_time = eventTime;
109
+ this.assistant[messageId].extra ??= {};
110
+ this.assistant[messageId].extra.metadata ??= {};
111
+ if (message.message.model != null) {
112
+ this.assistant[messageId].extra.metadata.ls_model_name =
113
+ message.message.model;
114
+ }
115
+ this.assistant[messageId].extra.metadata.usage_metadata =
116
+ (0, usage_js_1.extractUsageFromMessage)(message);
117
+ const tools = Array.isArray(message.message.content)
118
+ ? message.message.content.filter((block) => (0, messages_js_1.isToolBlock)(block))
119
+ : [];
120
+ for (const block of tools) {
121
+ if ((0, messages_js_1.isTaskTool)(block)) {
122
+ const name = block.input.subagent_type ||
123
+ block.input.agent_type ||
124
+ (block.input.description
125
+ ? block.input.description.split(" ")[0]
126
+ : null) ||
127
+ "unknown-agent";
128
+ this.tools[block.id] ??= this.createChild("root", {
129
+ name,
130
+ run_type: "chain",
131
+ inputs: block.input,
132
+ start_time: eventTime,
133
+ });
134
+ this.namespaces[block.id] ??= this.tools[block.id];
135
+ }
136
+ else {
137
+ const name = block.name || "unknown-tool";
138
+ this.tools[block.id] ??= this.createChild(namespace, {
139
+ name,
140
+ run_type: "tool",
141
+ inputs: block.input ? { input: block.input } : {},
142
+ start_time: eventTime,
143
+ });
144
+ }
145
+ }
146
+ }
147
+ if (message.type === "user") {
148
+ if (message.tool_use_result) {
149
+ const toolResult = Array.isArray(message.message.content)
150
+ ? message.message.content.find((block) => "tool_use_id" in block)
151
+ : undefined;
152
+ if (toolResult?.tool_use_id &&
153
+ this.tools[toolResult.tool_use_id] != null) {
154
+ const toolOutput = Array.isArray(message.tool_use_result)
155
+ ? { content: message.tool_use_result }
156
+ : message.tool_use_result;
157
+ const toolError = "is_error" in toolResult && toolResult.is_error === true
158
+ ? ["string", "number", "boolean"].includes(typeof message.tool_use_result)
159
+ ? String(message.tool_use_result)
160
+ : JSON.stringify(message.tool_use_result)
161
+ : undefined;
162
+ void this.tools[toolResult.tool_use_id].end(toolOutput, toolError);
163
+ }
164
+ }
165
+ }
166
+ this.history[namespace].push(message);
167
+ }
168
+ createChild(namespace, args) {
169
+ const runTree = this.namespaces[namespace].createChild(args);
170
+ this.postRunQueue.push(runTree.postRun());
171
+ this.runTrees.push(runTree);
172
+ return runTree;
173
+ }
174
+ async finish() {
175
+ // Clean up incomplete tools and subagent calls
176
+ for (const tool of Object.values(this.tools)) {
177
+ if (tool.outputs == null && tool.error == null) {
178
+ void tool.end(undefined, "Run not completed (conversation ended)");
179
+ }
180
+ }
181
+ // First make sure all the runs are created
182
+ await Promise.allSettled(this.postRunQueue);
183
+ // Then patch the runs
184
+ await Promise.allSettled(this.runTrees.map((runTree) => runTree.patchRun()));
185
+ }
186
+ }
187
+ exports.StreamManager = StreamManager;
@@ -0,0 +1,5 @@
1
+ import type { RunTreeConfig } from "../../run_trees.js";
2
+ /**
3
+ * Configuration options for wrapping Claude Agent SDK with LangSmith tracing.
4
+ */
5
+ export type WrapClaudeAgentSDKConfig = Partial<Omit<RunTreeConfig, "inputs" | "outputs" | "run_type" | "child_runs" | "parent_run" | "error">>;
@@ -0,0 +1,183 @@
1
+ import { convertFromAnthropicMessage, isTaskTool, isToolBlock, } from "./messages.js";
2
+ import { getCurrentRunTree } from "../../traceable.js";
3
+ import { aggregateUsageFromModelUsage, correctUsageFromResults, extractUsageFromMessage, } from "./usage.js";
4
+ /**
5
+ * @internal
6
+ */
7
+ export class StreamManager {
8
+ constructor() {
9
+ Object.defineProperty(this, "namespaces", {
10
+ enumerable: true,
11
+ configurable: true,
12
+ writable: true,
13
+ value: void 0
14
+ });
15
+ Object.defineProperty(this, "history", {
16
+ enumerable: true,
17
+ configurable: true,
18
+ writable: true,
19
+ value: void 0
20
+ });
21
+ Object.defineProperty(this, "assistant", {
22
+ enumerable: true,
23
+ configurable: true,
24
+ writable: true,
25
+ value: {}
26
+ });
27
+ Object.defineProperty(this, "tools", {
28
+ enumerable: true,
29
+ configurable: true,
30
+ writable: true,
31
+ value: {}
32
+ });
33
+ Object.defineProperty(this, "postRunQueue", {
34
+ enumerable: true,
35
+ configurable: true,
36
+ writable: true,
37
+ value: []
38
+ });
39
+ Object.defineProperty(this, "runTrees", {
40
+ enumerable: true,
41
+ configurable: true,
42
+ writable: true,
43
+ value: []
44
+ });
45
+ const rootRun = getCurrentRunTree(true);
46
+ this.namespaces = rootRun?.createChild ? { root: rootRun } : {};
47
+ this.history = { root: [] };
48
+ }
49
+ addMessage(message) {
50
+ const eventTime = Date.now();
51
+ // Short-circuit if no root run found
52
+ // This can happen if tracing is disabled globally
53
+ if (this.namespaces["root"] == null)
54
+ return;
55
+ if (message.type === "result") {
56
+ if (message.modelUsage) {
57
+ correctUsageFromResults(message.modelUsage, Object.values(this.assistant));
58
+ }
59
+ const usage = message.modelUsage
60
+ ? aggregateUsageFromModelUsage(message.modelUsage)
61
+ : extractUsageFromMessage(message);
62
+ if (message.total_cost_usd != null && usage != null) {
63
+ usage.total_cost = message.total_cost_usd;
64
+ }
65
+ this.namespaces["root"].extra ??= {};
66
+ this.namespaces["root"].extra.metadata ??= {};
67
+ this.namespaces["root"].extra.metadata.usage_metadata = usage;
68
+ this.namespaces["root"].extra.metadata.is_error = message.is_error;
69
+ this.namespaces["root"].extra.metadata.num_turns = message.num_turns;
70
+ this.namespaces["root"].extra.metadata.session_id = message.session_id;
71
+ this.namespaces["root"].extra.metadata.duration_ms = message.duration_ms;
72
+ this.namespaces["root"].extra.metadata.duration_api_ms =
73
+ message.duration_api_ms;
74
+ }
75
+ // Skip non-user / non-assistant messages
76
+ if (!("message" in message))
77
+ return;
78
+ const namespace = (() => {
79
+ if ("parent_tool_use_id" in message)
80
+ return message.parent_tool_use_id ?? "root";
81
+ return "root";
82
+ })();
83
+ // `eventTime` records the time we receive an event, which for `includePartialMessages: false`
84
+ // equals to the end time of an LLM block, so we need to use the first available end time within namespace.
85
+ const candidateStartTime = this.namespaces[namespace]?.child_runs?.at(-1)?.end_time ??
86
+ this.namespaces[namespace]?.start_time ??
87
+ eventTime;
88
+ this.history[namespace] ??= this.history["root"].slice();
89
+ if (message.type === "assistant") {
90
+ const messageId = message.message.id;
91
+ this.assistant[messageId] ??= this.createChild(namespace, {
92
+ name: "claude.assistant.turn",
93
+ run_type: "llm",
94
+ start_time: candidateStartTime,
95
+ inputs: {
96
+ messages: convertFromAnthropicMessage(this.history[namespace]),
97
+ },
98
+ outputs: { output: { messages: [] } },
99
+ });
100
+ this.assistant[messageId].outputs = (() => {
101
+ const prevMessages = this.assistant[messageId].outputs?.output.messages ?? [];
102
+ const newMessages = convertFromAnthropicMessage([message]);
103
+ return { output: { messages: [...prevMessages, ...newMessages] } };
104
+ })();
105
+ this.assistant[messageId].end_time = eventTime;
106
+ this.assistant[messageId].extra ??= {};
107
+ this.assistant[messageId].extra.metadata ??= {};
108
+ if (message.message.model != null) {
109
+ this.assistant[messageId].extra.metadata.ls_model_name =
110
+ message.message.model;
111
+ }
112
+ this.assistant[messageId].extra.metadata.usage_metadata =
113
+ extractUsageFromMessage(message);
114
+ const tools = Array.isArray(message.message.content)
115
+ ? message.message.content.filter((block) => isToolBlock(block))
116
+ : [];
117
+ for (const block of tools) {
118
+ if (isTaskTool(block)) {
119
+ const name = block.input.subagent_type ||
120
+ block.input.agent_type ||
121
+ (block.input.description
122
+ ? block.input.description.split(" ")[0]
123
+ : null) ||
124
+ "unknown-agent";
125
+ this.tools[block.id] ??= this.createChild("root", {
126
+ name,
127
+ run_type: "chain",
128
+ inputs: block.input,
129
+ start_time: eventTime,
130
+ });
131
+ this.namespaces[block.id] ??= this.tools[block.id];
132
+ }
133
+ else {
134
+ const name = block.name || "unknown-tool";
135
+ this.tools[block.id] ??= this.createChild(namespace, {
136
+ name,
137
+ run_type: "tool",
138
+ inputs: block.input ? { input: block.input } : {},
139
+ start_time: eventTime,
140
+ });
141
+ }
142
+ }
143
+ }
144
+ if (message.type === "user") {
145
+ if (message.tool_use_result) {
146
+ const toolResult = Array.isArray(message.message.content)
147
+ ? message.message.content.find((block) => "tool_use_id" in block)
148
+ : undefined;
149
+ if (toolResult?.tool_use_id &&
150
+ this.tools[toolResult.tool_use_id] != null) {
151
+ const toolOutput = Array.isArray(message.tool_use_result)
152
+ ? { content: message.tool_use_result }
153
+ : message.tool_use_result;
154
+ const toolError = "is_error" in toolResult && toolResult.is_error === true
155
+ ? ["string", "number", "boolean"].includes(typeof message.tool_use_result)
156
+ ? String(message.tool_use_result)
157
+ : JSON.stringify(message.tool_use_result)
158
+ : undefined;
159
+ void this.tools[toolResult.tool_use_id].end(toolOutput, toolError);
160
+ }
161
+ }
162
+ }
163
+ this.history[namespace].push(message);
164
+ }
165
+ createChild(namespace, args) {
166
+ const runTree = this.namespaces[namespace].createChild(args);
167
+ this.postRunQueue.push(runTree.postRun());
168
+ this.runTrees.push(runTree);
169
+ return runTree;
170
+ }
171
+ async finish() {
172
+ // Clean up incomplete tools and subagent calls
173
+ for (const tool of Object.values(this.tools)) {
174
+ if (tool.outputs == null && tool.error == null) {
175
+ void tool.end(undefined, "Run not completed (conversation ended)");
176
+ }
177
+ }
178
+ // First make sure all the runs are created
179
+ await Promise.allSettled(this.postRunQueue);
180
+ // Then patch the runs
181
+ await Promise.allSettled(this.runTrees.map((runTree) => runTree.patchRun()));
182
+ }
183
+ }