Skip to content

Commit f74479a

Browse files
feat(anthropic): support code execution tool
1 parent e0e4ce7 commit f74479a

File tree

6 files changed

+290
-0
lines changed

6 files changed

+290
-0
lines changed

libs/providers/langchain-anthropic/README.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,58 @@ const computer = tools.computer_20251124({
454454

455455
For more information, see [Anthropic's Computer Use documentation](https://docs.anthropic.com/en/docs/agents-and-tools/tool-use/computer-use).
456456

457+
### Code Execution Tool
458+
459+
The code execution tool (`codeExecution_20250825`) allows Claude to run Bash commands and manipulate files in a secure, sandboxed environment. Claude can analyze data, create visualizations, perform calculations, and process files.
460+
461+
When this tool is provided, Claude automatically gains access to:
462+
463+
- **Bash commands** - Execute shell commands for system operations
464+
- **File operations** - Create, view, and edit files directly
465+
466+
```typescript
467+
import { ChatAnthropic, tools } from "@langchain/anthropic";
468+
469+
const llm = new ChatAnthropic({
470+
model: "claude-sonnet-4-5-20250929",
471+
});
472+
473+
// Basic usage - calculations and data analysis
474+
const response = await llm.invoke(
475+
"Calculate the mean and standard deviation of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]",
476+
{ tools: [tools.codeExecution_20250825()] }
477+
);
478+
479+
// File operations and visualization
480+
const response2 = await llm.invoke(
481+
"Create a matplotlib visualization of sales data and save it as chart.png",
482+
{ tools: [tools.codeExecution_20250825()] }
483+
);
484+
```
485+
486+
Container reuse for multi-step workflows:
487+
488+
```typescript
489+
// First request - creates a container
490+
const response1 = await llm.invoke("Write a random number to /tmp/number.txt", {
491+
tools: [tools.codeExecution_20250825()],
492+
});
493+
494+
// Extract container ID from response for reuse
495+
const containerId = response1.response_metadata?.container?.id;
496+
497+
// Second request - reuse container to access the file
498+
const response2 = await llm.invoke(
499+
"Read /tmp/number.txt and calculate its square",
500+
{
501+
tools: [tools.codeExecution_20250825()],
502+
container: containerId,
503+
}
504+
);
505+
```
506+
507+
For more information, see [Anthropic's Code Execution Tool documentation](https://docs.anthropic.com/en/docs/agents-and-tools/tool-use/code-execution-tool).
508+
457509
## Development
458510

459511
To develop the Anthropic package, you'll need to follow these instructions:
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import Anthropic from "@anthropic-ai/sdk";
2+
3+
/**
4+
* Options for the code execution tool.
5+
*/
6+
interface CodeExecution20250825Options {
7+
/**
8+
* Create a cache control breakpoint at this content block.
9+
*/
10+
cacheControl?: Anthropic.Beta.BetaCacheControlEphemeral;
11+
}
12+
13+
/**
14+
* Creates a code execution tool that allows Claude to run Bash commands and manipulate files
15+
* in a secure, sandboxed environment. Claude can analyze data, create visualizations,
16+
* perform calculations, and process files.
17+
*
18+
* When this tool is provided, Claude automatically gains access to:
19+
* - **Bash commands**: Execute shell commands for system operations
20+
* - **File operations**: Create, view, and edit files directly
21+
*
22+
* @note This tool requires the beta header `code-execution-2025-08-25` in API requests.
23+
*
24+
* @see {@link https://docs.anthropic.com/en/docs/agents-and-tools/tool-use/code-execution-tool | Anthropic Code Execution Documentation}
25+
* @param options - Configuration options for the code execution tool
26+
* @returns A code execution tool definition to be passed to the Anthropic API
27+
*
28+
* @example
29+
* ```typescript
30+
* import { ChatAnthropic, tools } from "@langchain/anthropic";
31+
*
32+
* const model = new ChatAnthropic({
33+
* model: "claude-sonnet-4-5-20250929",
34+
* });
35+
*
36+
* // Basic usage - calculations and data analysis
37+
* const response = await model.invoke(
38+
* "Calculate the mean and standard deviation of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]",
39+
* { tools: [tools.codeExecution_20250825()] }
40+
* );
41+
*
42+
* // File operations and visualization
43+
* const response2 = await model.invoke(
44+
* "Create a matplotlib visualization of sales data and save it as chart.png",
45+
* { tools: [tools.codeExecution_20250825()] }
46+
* );
47+
* ```
48+
*
49+
* @example Container reuse
50+
* ```typescript
51+
* // First request - creates a container
52+
* const response1 = await model.invoke(
53+
* "Write a random number to /tmp/number.txt",
54+
* { tools: [tools.codeExecution_20250825()] }
55+
* );
56+
*
57+
* // Extract container ID from response for reuse
58+
* const containerId = response1.response_metadata?.container?.id;
59+
*
60+
* // Second request - reuse container to access the file
61+
* const response2 = await model.invoke(
62+
* "Read /tmp/number.txt and calculate its square",
63+
* {
64+
* tools: [tools.codeExecution_20250825()],
65+
* // Pass container ID to reuse the same environment
66+
* }
67+
* );
68+
* ```
69+
*/
70+
export function codeExecution_20250825(
71+
options?: CodeExecution20250825Options
72+
): Anthropic.Beta.BetaCodeExecutionTool20250825 {
73+
return {
74+
type: "code_execution_20250825",
75+
name: "code_execution",
76+
cache_control: options?.cacheControl,
77+
};
78+
}

libs/providers/langchain-anthropic/src/tools/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
} from "./toolSearch.js";
88
import { textEditor_20250728 } from "./textEditor.js";
99
import { computer_20251124, computer_20250124 } from "./computer.js";
10+
import { codeExecution_20250825 } from "./codeExecution.js";
1011

1112
export const tools = {
1213
memory_20250818,
@@ -17,6 +18,7 @@ export const tools = {
1718
textEditor_20250728,
1819
computer_20251124,
1920
computer_20250124,
21+
codeExecution_20250825,
2022
};
2123

2224
export type * from "./types.js";
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import { expect, it, describe } from "vitest";
2+
import { AIMessage, HumanMessage } from "@langchain/core/messages";
3+
4+
import { ChatAnthropic } from "../../chat_models.js";
5+
import { codeExecution_20250825 } from "../codeExecution.js";
6+
7+
const createModel = () =>
8+
new ChatAnthropic({
9+
model: "claude-sonnet-4-5",
10+
temperature: 0,
11+
});
12+
13+
describe("Anthropic Code Execution Tool Integration Tests", () => {
14+
it("code execution tool can be bound to ChatAnthropic and performs calculations", async () => {
15+
const llm = createModel();
16+
const llmWithCodeExecution = llm.bindTools([codeExecution_20250825()]);
17+
18+
const response = await llmWithCodeExecution.invoke([
19+
new HumanMessage(
20+
"Calculate the mean of [1, 2, 3, 4, 5]. Just give me the number."
21+
),
22+
]);
23+
24+
expect(response).toBeInstanceOf(AIMessage);
25+
expect(Array.isArray(response.content)).toBe(true);
26+
27+
const contentBlocks = response.content as Array<{ type: string }>;
28+
29+
// Should have server_tool_use for code execution
30+
const hasServerToolUse = contentBlocks.some(
31+
(block) => block.type === "server_tool_use"
32+
);
33+
34+
// Should have code execution result
35+
const hasCodeExecutionResult = contentBlocks.some(
36+
(block) =>
37+
block.type === "bash_code_execution_tool_result" ||
38+
block.type === "text_editor_code_execution_tool_result"
39+
);
40+
41+
expect(hasServerToolUse).toBe(true);
42+
expect(hasCodeExecutionResult).toBe(true);
43+
44+
const [toolUse, toolResult, result] = response.content;
45+
expect(toolUse).toEqual(
46+
expect.objectContaining({
47+
type: "server_tool_use",
48+
id: expect.any(String),
49+
name: "bash_code_execution",
50+
input: {
51+
command:
52+
'python3 -c "print(sum([1, 2, 3, 4, 5]) / len([1, 2, 3, 4, 5]))"',
53+
},
54+
})
55+
);
56+
expect(toolResult).toEqual(
57+
expect.objectContaining({
58+
type: "bash_code_execution_tool_result",
59+
tool_use_id: expect.any(String),
60+
content: expect.objectContaining({
61+
type: "bash_code_execution_result",
62+
stdout: "3.0\n",
63+
stderr: "",
64+
return_code: 0,
65+
content: [],
66+
}),
67+
})
68+
);
69+
expect(result).toEqual(
70+
expect.objectContaining({
71+
type: "text",
72+
text: expect.any(String),
73+
})
74+
);
75+
}, 60000);
76+
77+
it("code execution tool supports container reuse across requests", async () => {
78+
const llm = createModel();
79+
80+
// First request - creates a container and writes a file
81+
const response1 = await llm.invoke(
82+
"Write the number 7 to /tmp/number.txt using bash. Just do it, no explanation needed.",
83+
{
84+
tools: [codeExecution_20250825()],
85+
}
86+
);
87+
88+
expect(response1).toBeInstanceOf(AIMessage);
89+
90+
// Extract container ID from response for reuse
91+
const containerId = (
92+
response1.response_metadata?.container as { id?: string } | undefined
93+
)?.id;
94+
expect(containerId).toBeDefined();
95+
expect(typeof containerId).toBe("string");
96+
97+
// Second request - reuse container to access the file
98+
const response2 = await llm.invoke(
99+
"Read /tmp/number.txt and calculate its square. Just give me the result.",
100+
{
101+
tools: [codeExecution_20250825()],
102+
container: containerId,
103+
}
104+
);
105+
106+
expect(response2).toBeInstanceOf(AIMessage);
107+
108+
// The response should contain code execution results
109+
const contentBlocks = response2.content as Array<{ type: string }>;
110+
const hasCodeExecutionResult = contentBlocks.some(
111+
(block) =>
112+
block.type === "bash_code_execution_tool_result" ||
113+
block.type === "text_editor_code_execution_tool_result"
114+
);
115+
expect(hasCodeExecutionResult).toBe(true);
116+
117+
// The final text response should contain 49 (7 squared)
118+
const textBlock = contentBlocks.find((block) => block.type === "text") as {
119+
type: string;
120+
text: string;
121+
};
122+
expect(textBlock).toBeDefined();
123+
expect(textBlock.text).toContain("49");
124+
}, 120000);
125+
});
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { expect, it, describe } from "vitest";
2+
import { codeExecution_20250825 } from "../codeExecution.js";
3+
4+
describe("Anthropic Code Execution Tool Unit Tests", () => {
5+
describe("codeExecution_20250825", () => {
6+
it("creates a valid code execution tool with no options", () => {
7+
expect(codeExecution_20250825()).toMatchInlineSnapshot(`
8+
{
9+
"cache_control": undefined,
10+
"name": "code_execution",
11+
"type": "code_execution_20250825",
12+
}
13+
`);
14+
});
15+
16+
it("creates a valid code execution tool with cache control", () => {
17+
expect(
18+
codeExecution_20250825({
19+
cacheControl: { type: "ephemeral" },
20+
})
21+
).toMatchInlineSnapshot(`
22+
{
23+
"cache_control": {
24+
"type": "ephemeral",
25+
},
26+
"name": "code_execution",
27+
"type": "code_execution_20250825",
28+
}
29+
`);
30+
});
31+
});
32+
});

libs/providers/langchain-anthropic/src/utils/tools.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,5 @@ export const ANTHROPIC_TOOL_BETAS: Record<string, string> = {
5555
tool_search_tool_bm25_20251119: "advanced-tool-use-2025-11-20",
5656
memory_20250818: "context-management-2025-06-27",
5757
web_fetch_20250910: "web-fetch-2025-09-10",
58+
code_execution_20250825: "code-execution-2025-08-25",
5859
};

0 commit comments

Comments
 (0)