langsmith 0.4.7 → 0.4.9
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 +10 -2
- package/dist/client.d.ts +1 -0
- package/dist/client.js +10 -2
- package/dist/experimental/anthropic/context.cjs +187 -0
- package/dist/experimental/anthropic/context.d.ts +5 -0
- package/dist/experimental/anthropic/context.js +183 -0
- package/dist/experimental/anthropic/index.cjs +188 -0
- package/dist/experimental/anthropic/index.d.ts +30 -0
- package/dist/experimental/anthropic/index.js +185 -0
- package/dist/experimental/anthropic/messages.cjs +102 -0
- package/dist/experimental/anthropic/messages.d.ts +6 -0
- package/dist/experimental/anthropic/messages.js +96 -0
- package/dist/experimental/anthropic/types.cjs +3 -0
- package/dist/experimental/anthropic/types.d.ts +50 -0
- package/dist/experimental/anthropic/types.js +2 -0
- package/dist/experimental/anthropic/usage.cjs +180 -0
- package/dist/experimental/anthropic/usage.d.ts +1 -0
- package/dist/experimental/anthropic/usage.js +175 -0
- package/dist/experimental/anthropic/utils.cjs +24 -0
- package/dist/experimental/anthropic/utils.d.ts +1 -0
- package/dist/experimental/anthropic/utils.js +20 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/traceable.cjs +38 -4
- package/dist/traceable.d.ts +4 -0
- package/dist/traceable.js +38 -4
- package/dist/utils/usage.cjs +6 -7
- package/dist/utils/usage.js +6 -7
- package/dist/wrappers/gemini.cjs +434 -0
- package/dist/wrappers/gemini.d.ts +46 -0
- package/dist/wrappers/gemini.js +431 -0
- package/experimental/anthropic.cjs +1 -0
- package/experimental/anthropic.d.cts +1 -0
- package/experimental/anthropic.d.ts +1 -0
- package/experimental/anthropic.js +1 -0
- package/package.json +16 -1
package/dist/client.cjs
CHANGED
|
@@ -429,6 +429,12 @@ class Client {
|
|
|
429
429
|
writable: true,
|
|
430
430
|
value: false
|
|
431
431
|
});
|
|
432
|
+
Object.defineProperty(this, "_runCompressionDisabled", {
|
|
433
|
+
enumerable: true,
|
|
434
|
+
configurable: true,
|
|
435
|
+
writable: true,
|
|
436
|
+
value: (0, env_js_1.getLangSmithEnvironmentVariable)("DISABLE_RUN_COMPRESSION") === "true"
|
|
437
|
+
});
|
|
432
438
|
Object.defineProperty(this, "debug", {
|
|
433
439
|
enumerable: true,
|
|
434
440
|
configurable: true,
|
|
@@ -806,7 +812,8 @@ class Client {
|
|
|
806
812
|
const useMultipart = !this._multipartDisabled &&
|
|
807
813
|
(serverInfo?.batch_ingest_config?.use_multipart_endpoint ?? true);
|
|
808
814
|
if (useMultipart) {
|
|
809
|
-
const useGzip =
|
|
815
|
+
const useGzip = !this._runCompressionDisabled &&
|
|
816
|
+
serverInfo?.instance_flags?.gzip_body_enabled;
|
|
810
817
|
try {
|
|
811
818
|
await this.multipartIngestRuns(ingestParams, {
|
|
812
819
|
...options,
|
|
@@ -2187,7 +2194,8 @@ class Client {
|
|
|
2187
2194
|
async uploadCsv({ csvFile, fileName, inputKeys, outputKeys, description, dataType, name, }) {
|
|
2188
2195
|
const url = `${this.apiUrl}/datasets/upload`;
|
|
2189
2196
|
const formData = new FormData();
|
|
2190
|
-
|
|
2197
|
+
const csvBlob = new Blob([csvFile], { type: "text/csv" });
|
|
2198
|
+
formData.append("file", csvBlob, fileName);
|
|
2191
2199
|
inputKeys.forEach((key) => {
|
|
2192
2200
|
formData.append("input_keys", key);
|
|
2193
2201
|
});
|
package/dist/client.d.ts
CHANGED
|
@@ -378,6 +378,7 @@ export declare class Client implements LangSmithTracingClientInterface {
|
|
|
378
378
|
private get _fetch();
|
|
379
379
|
private multipartStreamingDisabled;
|
|
380
380
|
private _multipartDisabled;
|
|
381
|
+
private _runCompressionDisabled;
|
|
381
382
|
debug: boolean;
|
|
382
383
|
constructor(config?: ClientConfig);
|
|
383
384
|
static getDefaultClientConfig(): {
|
package/dist/client.js
CHANGED
|
@@ -391,6 +391,12 @@ export class Client {
|
|
|
391
391
|
writable: true,
|
|
392
392
|
value: false
|
|
393
393
|
});
|
|
394
|
+
Object.defineProperty(this, "_runCompressionDisabled", {
|
|
395
|
+
enumerable: true,
|
|
396
|
+
configurable: true,
|
|
397
|
+
writable: true,
|
|
398
|
+
value: getLangSmithEnvironmentVariable("DISABLE_RUN_COMPRESSION") === "true"
|
|
399
|
+
});
|
|
394
400
|
Object.defineProperty(this, "debug", {
|
|
395
401
|
enumerable: true,
|
|
396
402
|
configurable: true,
|
|
@@ -768,7 +774,8 @@ export class Client {
|
|
|
768
774
|
const useMultipart = !this._multipartDisabled &&
|
|
769
775
|
(serverInfo?.batch_ingest_config?.use_multipart_endpoint ?? true);
|
|
770
776
|
if (useMultipart) {
|
|
771
|
-
const useGzip =
|
|
777
|
+
const useGzip = !this._runCompressionDisabled &&
|
|
778
|
+
serverInfo?.instance_flags?.gzip_body_enabled;
|
|
772
779
|
try {
|
|
773
780
|
await this.multipartIngestRuns(ingestParams, {
|
|
774
781
|
...options,
|
|
@@ -2149,7 +2156,8 @@ export class Client {
|
|
|
2149
2156
|
async uploadCsv({ csvFile, fileName, inputKeys, outputKeys, description, dataType, name, }) {
|
|
2150
2157
|
const url = `${this.apiUrl}/datasets/upload`;
|
|
2151
2158
|
const formData = new FormData();
|
|
2152
|
-
|
|
2159
|
+
const csvBlob = new Blob([csvFile], { type: "text/csv" });
|
|
2160
|
+
formData.append("file", csvBlob, fileName);
|
|
2153
2161
|
inputKeys.forEach((key) => {
|
|
2154
2162
|
formData.append("input_keys", key);
|
|
2155
2163
|
});
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.wrapClaudeAgentSDK = wrapClaudeAgentSDK;
|
|
4
|
+
const traceable_js_1 = require("../../traceable.cjs");
|
|
5
|
+
const context_js_1 = require("./context.cjs");
|
|
6
|
+
const messages_js_1 = require("./messages.cjs");
|
|
7
|
+
/**
|
|
8
|
+
* Wraps the Claude Agent SDK's query function to add LangSmith tracing.
|
|
9
|
+
* Traces the entire agent interaction including all streaming messages.
|
|
10
|
+
* @internal Use `wrapClaudeAgentSDK` instead.
|
|
11
|
+
*/
|
|
12
|
+
function wrapClaudeAgentQuery(queryFn, defaultThis, baseConfig) {
|
|
13
|
+
async function* generator(originalGenerator, prompt) {
|
|
14
|
+
const streamManager = new context_js_1.StreamManager();
|
|
15
|
+
try {
|
|
16
|
+
let systemCount = 0;
|
|
17
|
+
for await (const message of originalGenerator) {
|
|
18
|
+
if (message.type === "system") {
|
|
19
|
+
const content = getLatestInput(prompt, systemCount);
|
|
20
|
+
systemCount += 1;
|
|
21
|
+
if (content != null)
|
|
22
|
+
streamManager.addMessage(content);
|
|
23
|
+
}
|
|
24
|
+
streamManager.addMessage(message);
|
|
25
|
+
yield message;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
finally {
|
|
29
|
+
await streamManager.finish();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function getLatestInput(arg, systemCount) {
|
|
33
|
+
const value = (() => {
|
|
34
|
+
if (typeof arg !== "object" || arg == null)
|
|
35
|
+
return arg;
|
|
36
|
+
const toJSON = arg["toJSON"];
|
|
37
|
+
if (typeof toJSON !== "function")
|
|
38
|
+
return undefined;
|
|
39
|
+
const latest = toJSON();
|
|
40
|
+
return latest?.at(systemCount);
|
|
41
|
+
})();
|
|
42
|
+
if (value == null)
|
|
43
|
+
return undefined;
|
|
44
|
+
if (typeof value === "string") {
|
|
45
|
+
return {
|
|
46
|
+
type: "user",
|
|
47
|
+
message: { content: value, role: "user" },
|
|
48
|
+
parent_tool_use_id: null,
|
|
49
|
+
session_id: "",
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return typeof value === "object" && value != null ? value : undefined;
|
|
53
|
+
}
|
|
54
|
+
async function processInputs(rawInputs) {
|
|
55
|
+
const inputs = rawInputs;
|
|
56
|
+
const newInputs = { ...inputs };
|
|
57
|
+
return Object.assign(newInputs, {
|
|
58
|
+
toJSON: () => {
|
|
59
|
+
const toJSON = (value) => {
|
|
60
|
+
if (typeof value !== "object" || value == null)
|
|
61
|
+
return value;
|
|
62
|
+
const fn = value?.toJSON;
|
|
63
|
+
if (typeof fn === "function")
|
|
64
|
+
return fn();
|
|
65
|
+
return value;
|
|
66
|
+
};
|
|
67
|
+
const prompt = toJSON(inputs.prompt);
|
|
68
|
+
const options = inputs.options != null
|
|
69
|
+
? { ...inputs.options }
|
|
70
|
+
: undefined;
|
|
71
|
+
if (options?.mcpServers != null) {
|
|
72
|
+
options.mcpServers = Object.fromEntries(Object.entries(options.mcpServers ?? {}).map(([key, value]) => [
|
|
73
|
+
key,
|
|
74
|
+
{ name: value.name, type: value.type },
|
|
75
|
+
]));
|
|
76
|
+
}
|
|
77
|
+
return { messages: (0, messages_js_1.convertFromAnthropicMessage)(prompt), options };
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
function processOutputs(rawOutputs) {
|
|
82
|
+
if ("outputs" in rawOutputs && Array.isArray(rawOutputs.outputs)) {
|
|
83
|
+
const sdkMessages = rawOutputs.outputs;
|
|
84
|
+
const messages = sdkMessages
|
|
85
|
+
.filter((message) => {
|
|
86
|
+
if (!("message" in message))
|
|
87
|
+
return true;
|
|
88
|
+
return message.parent_tool_use_id == null;
|
|
89
|
+
})
|
|
90
|
+
.flatMap(messages_js_1.convertFromAnthropicMessage);
|
|
91
|
+
return { output: { messages } };
|
|
92
|
+
}
|
|
93
|
+
return rawOutputs;
|
|
94
|
+
}
|
|
95
|
+
return (0, traceable_js_1.traceable)((params, ...args) => {
|
|
96
|
+
const actualGenerator = queryFn.call(defaultThis, params, ...args);
|
|
97
|
+
const wrappedGenerator = generator(actualGenerator, params.prompt);
|
|
98
|
+
for (const method of Object.getOwnPropertyNames(Object.getPrototypeOf(actualGenerator)).filter((method) => !["constructor", "next", "throw", "return"].includes(method))) {
|
|
99
|
+
Object.defineProperty(wrappedGenerator, method, {
|
|
100
|
+
get() {
|
|
101
|
+
const getValue = actualGenerator?.[method];
|
|
102
|
+
if (typeof getValue === "function")
|
|
103
|
+
return getValue.bind(actualGenerator);
|
|
104
|
+
return getValue;
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
return wrappedGenerator;
|
|
109
|
+
}, {
|
|
110
|
+
name: "claude.conversation",
|
|
111
|
+
run_type: "chain",
|
|
112
|
+
...baseConfig,
|
|
113
|
+
metadata: { ...baseConfig?.metadata },
|
|
114
|
+
__deferredSerializableArgOptions: { maxDepth: 1 },
|
|
115
|
+
processInputs,
|
|
116
|
+
processOutputs,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Wraps the Claude Agent SDK with LangSmith tracing. This returns wrapped versions
|
|
121
|
+
* of query and tool that automatically trace all agent interactions.
|
|
122
|
+
*
|
|
123
|
+
* @param sdk - The Claude Agent SDK module
|
|
124
|
+
* @param config - Optional LangSmith configuration
|
|
125
|
+
* @returns Object with wrapped query, tool, and createSdkMcpServer functions
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* ```typescript
|
|
129
|
+
* import * as claudeSDK from "@anthropic-ai/claude-agent-sdk";
|
|
130
|
+
* import { wrapClaudeAgentSDK } from "langsmith/experimental/claude_agent_sdk";
|
|
131
|
+
*
|
|
132
|
+
* // Wrap once - returns { query, tool, createSdkMcpServer } with tracing built-in
|
|
133
|
+
* const { query, tool, createSdkMcpServer } = wrapClaudeAgentSDK(claudeSDK);
|
|
134
|
+
*
|
|
135
|
+
* // Use normally - tracing is automatic
|
|
136
|
+
* for await (const message of query({
|
|
137
|
+
* prompt: "Hello, Claude!",
|
|
138
|
+
* options: { model: "claude-haiku-4-5-20251001" }
|
|
139
|
+
* })) {
|
|
140
|
+
* console.log(message);
|
|
141
|
+
* }
|
|
142
|
+
*
|
|
143
|
+
* // Tools created with wrapped tool() are automatically traced
|
|
144
|
+
* const calculator = tool("calculator", "Does math", schema, handler);
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
147
|
+
function wrapClaudeAgentSDK(sdk, config) {
|
|
148
|
+
const inputSdk = sdk;
|
|
149
|
+
const wrappedSdk = { ...sdk };
|
|
150
|
+
if ("query" in inputSdk && (0, traceable_js_1.isTraceableFunction)(inputSdk.query)) {
|
|
151
|
+
throw new Error("This instance of Claude Agent SDK has been already wrapped by `wrapClaudeAgentSDK`.");
|
|
152
|
+
}
|
|
153
|
+
// Wrap the query method if it exists
|
|
154
|
+
if ("query" in inputSdk && typeof inputSdk.query === "function") {
|
|
155
|
+
wrappedSdk.query = wrapClaudeAgentQuery(inputSdk.query, inputSdk, config);
|
|
156
|
+
}
|
|
157
|
+
// Wrap the tool method if it exists
|
|
158
|
+
if ("tool" in inputSdk && typeof inputSdk.tool === "function") {
|
|
159
|
+
wrappedSdk.tool = inputSdk.tool.bind(inputSdk);
|
|
160
|
+
}
|
|
161
|
+
// Keep createSdkMcpServer and other methods as-is (bound to original SDK)
|
|
162
|
+
if ("createSdkMcpServer" in inputSdk &&
|
|
163
|
+
typeof inputSdk.createSdkMcpServer === "function") {
|
|
164
|
+
wrappedSdk.createSdkMcpServer = inputSdk.createSdkMcpServer.bind(inputSdk);
|
|
165
|
+
}
|
|
166
|
+
if ("unstable_v2_createSession" in inputSdk &&
|
|
167
|
+
typeof inputSdk.unstable_v2_createSession === "function") {
|
|
168
|
+
wrappedSdk.unstable_v2_createSession = (...args) => {
|
|
169
|
+
console.warn("Tracing of `unstable_v2_createSession` is not supported by LangSmith. Tracing will not be applied.");
|
|
170
|
+
return inputSdk.unstable_v2_createSession?.(...args);
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
if ("unstable_v2_prompt" in inputSdk &&
|
|
174
|
+
typeof inputSdk.unstable_v2_prompt === "function") {
|
|
175
|
+
wrappedSdk.unstable_v2_prompt = (...args) => {
|
|
176
|
+
console.warn("Tracing of `unstable_v2_prompt` is not supported by LangSmith. Tracing will not be applied.");
|
|
177
|
+
return inputSdk.unstable_v2_prompt?.(...args);
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
if ("unstable_v2_resumeSession" in inputSdk &&
|
|
181
|
+
typeof inputSdk.unstable_v2_resumeSession === "function") {
|
|
182
|
+
wrappedSdk.unstable_v2_resumeSession = (...args) => {
|
|
183
|
+
console.warn("Tracing of `unstable_v2_resumeSession` is not supported by LangSmith. Tracing will not be applied.");
|
|
184
|
+
return inputSdk.unstable_v2_resumeSession?.(...args);
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
return wrappedSdk;
|
|
188
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type WrapClaudeAgentSDKConfig } from "./context.js";
|
|
2
|
+
/**
|
|
3
|
+
* Wraps the Claude Agent SDK with LangSmith tracing. This returns wrapped versions
|
|
4
|
+
* of query and tool that automatically trace all agent interactions.
|
|
5
|
+
*
|
|
6
|
+
* @param sdk - The Claude Agent SDK module
|
|
7
|
+
* @param config - Optional LangSmith configuration
|
|
8
|
+
* @returns Object with wrapped query, tool, and createSdkMcpServer functions
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import * as claudeSDK from "@anthropic-ai/claude-agent-sdk";
|
|
13
|
+
* import { wrapClaudeAgentSDK } from "langsmith/experimental/claude_agent_sdk";
|
|
14
|
+
*
|
|
15
|
+
* // Wrap once - returns { query, tool, createSdkMcpServer } with tracing built-in
|
|
16
|
+
* const { query, tool, createSdkMcpServer } = wrapClaudeAgentSDK(claudeSDK);
|
|
17
|
+
*
|
|
18
|
+
* // Use normally - tracing is automatic
|
|
19
|
+
* for await (const message of query({
|
|
20
|
+
* prompt: "Hello, Claude!",
|
|
21
|
+
* options: { model: "claude-haiku-4-5-20251001" }
|
|
22
|
+
* })) {
|
|
23
|
+
* console.log(message);
|
|
24
|
+
* }
|
|
25
|
+
*
|
|
26
|
+
* // Tools created with wrapped tool() are automatically traced
|
|
27
|
+
* const calculator = tool("calculator", "Does math", schema, handler);
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export declare function wrapClaudeAgentSDK<T extends object>(sdk: T, config?: WrapClaudeAgentSDKConfig): T;
|