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.
Files changed (37) hide show
  1. package/dist/client.cjs +10 -2
  2. package/dist/client.d.ts +1 -0
  3. package/dist/client.js +10 -2
  4. package/dist/experimental/anthropic/context.cjs +187 -0
  5. package/dist/experimental/anthropic/context.d.ts +5 -0
  6. package/dist/experimental/anthropic/context.js +183 -0
  7. package/dist/experimental/anthropic/index.cjs +188 -0
  8. package/dist/experimental/anthropic/index.d.ts +30 -0
  9. package/dist/experimental/anthropic/index.js +185 -0
  10. package/dist/experimental/anthropic/messages.cjs +102 -0
  11. package/dist/experimental/anthropic/messages.d.ts +6 -0
  12. package/dist/experimental/anthropic/messages.js +96 -0
  13. package/dist/experimental/anthropic/types.cjs +3 -0
  14. package/dist/experimental/anthropic/types.d.ts +50 -0
  15. package/dist/experimental/anthropic/types.js +2 -0
  16. package/dist/experimental/anthropic/usage.cjs +180 -0
  17. package/dist/experimental/anthropic/usage.d.ts +1 -0
  18. package/dist/experimental/anthropic/usage.js +175 -0
  19. package/dist/experimental/anthropic/utils.cjs +24 -0
  20. package/dist/experimental/anthropic/utils.d.ts +1 -0
  21. package/dist/experimental/anthropic/utils.js +20 -0
  22. package/dist/index.cjs +1 -1
  23. package/dist/index.d.ts +1 -1
  24. package/dist/index.js +1 -1
  25. package/dist/traceable.cjs +38 -4
  26. package/dist/traceable.d.ts +4 -0
  27. package/dist/traceable.js +38 -4
  28. package/dist/utils/usage.cjs +6 -7
  29. package/dist/utils/usage.js +6 -7
  30. package/dist/wrappers/gemini.cjs +434 -0
  31. package/dist/wrappers/gemini.d.ts +46 -0
  32. package/dist/wrappers/gemini.js +431 -0
  33. package/experimental/anthropic.cjs +1 -0
  34. package/experimental/anthropic.d.cts +1 -0
  35. package/experimental/anthropic.d.ts +1 -0
  36. package/experimental/anthropic.js +1 -0
  37. 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 = serverInfo?.instance_flags?.gzip_body_enabled;
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
- formData.append("file", csvFile, fileName);
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 = serverInfo?.instance_flags?.gzip_body_enabled;
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
- formData.append("file", csvFile, fileName);
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;