Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions libs/langchain/src/agents/ReactAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ export class ReactAgent<
/**
* add single tool node for all tools
*/
if (toolClasses.length > 0) {
if (toolClasses.filter(isClientTool).length > 0) {
const toolNode = new ToolNode(toolClasses.filter(isClientTool), {
signal: this.options.signal,
});
Expand Down Expand Up @@ -206,7 +206,7 @@ export class ReactAgent<
/**
* add edges for tools node
*/
if (toolClasses.length > 0) {
if (toolClasses.filter(isClientTool).length > 0) {
if (shouldReturnDirect.size > 0) {
allNodeWorkflows.addConditionalEdges(
"tools",
Expand Down
4 changes: 2 additions & 2 deletions libs/langchain/src/agents/middlewareAgent/ReactAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ export class ReactAgent<
/**
* add single tool node for all tools
*/
if (toolClasses.length > 0) {
if (toolClasses.filter(isClientTool).length > 0) {
const toolNode = new ToolNode(toolClasses.filter(isClientTool), {
signal: this.options.signal,
});
Expand Down Expand Up @@ -354,7 +354,7 @@ export class ReactAgent<
/**
* add edges for tools node
*/
if (toolClasses.length > 0) {
if (toolClasses.filter(isClientTool).length > 0) {
// Tools should return to first beforeModel node or agent
let toolReturnTarget: string;
if (beforeModelNodes.length > 0) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { describe, it, expect } from "vitest";
import { ChatAnthropic } from "@langchain/anthropic";
import { HumanMessage, AIMessage } from "@langchain/core/messages";

import { createAgent } from "../index.js";

describe("Code Execution Tool", () => {
it("should use Anthropic code_execution tool to calculate statistics", async () => {
// Create ChatAnthropic model with code execution beta header
const model = new ChatAnthropic({
model: "claude-3-5-haiku-20241022",
temperature: 0,
clientOptions: {
defaultHeaders: {
"anthropic-beta": "code-execution-2025-08-25",
},
},
});

// Define the built-in code_execution tool
const codeExecutionTool = {
type: "code_execution_20250825",
name: "code_execution",
};

// Create agent with the built-in tool
const agent = createAgent({
model,
tools: [codeExecutionTool],
});

// Invoke the agent with a statistics calculation task
const result = await agent.invoke({
messages: [
new HumanMessage(
"Calculate the mean and standard deviation of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"
),
],
});

// Verify the result contains messages
expect(result.messages).toBeDefined();
expect(result.messages.length).toBeGreaterThan(0);

// Find the final AI response
const aiResponse = result.messages.find((msg) => msg.type === "ai") as
| AIMessage
| undefined;
expect(aiResponse).toBeDefined();

// The response should contain content blocks
const content = Array.isArray(aiResponse?.content)
? aiResponse.content
: [];
expect(content.length).toBeGreaterThan(0);

// Verify that code_execution tool was used (server_tool_use block)
expect(content.some((block) => block.type === "server_tool_use")).toBe(
true
);

// Verify that we got a code execution result
expect(
content.some((block) => block.type === "bash_code_execution_tool_result")
).toBe(true);

// The response should contain text with the calculated values
const textBlocks = content.filter((block) => block.type === "text");
const responseText = textBlocks
.map((block) => block.text)
.join(" ")
.toLowerCase();

// The response should mention the mean (5.5) and standard deviation (~2.87)
expect(responseText).toMatch(/mean|average/i);
expect(responseText).toMatch(/standard deviation|std/i);
expect(responseText).toMatch(/5\.5/); // Expected mean
expect(responseText).toMatch(/2\.[89]|3\./); // Expected stdev (approximately 2.87)
}, 60000); // Set timeout to 60s for API call
});
80 changes: 80 additions & 0 deletions libs/langchain/src/agents/tests/codeExecution.int.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { describe, it, expect } from "vitest";
import { ChatAnthropic } from "@langchain/anthropic";
import { HumanMessage, AIMessage } from "@langchain/core/messages";

import { createAgent } from "../createAgent.js";

describe("Code Execution Tool", () => {
it("should use Anthropic code_execution tool to calculate statistics", async () => {
// Create ChatAnthropic model with code execution beta header
const model = new ChatAnthropic({
model: "claude-3-5-haiku-20241022",
temperature: 0,
clientOptions: {
defaultHeaders: {
"anthropic-beta": "code-execution-2025-08-25",
},
},
});

// Define the built-in code_execution tool
const codeExecutionTool = {
type: "code_execution_20250825",
name: "code_execution",
};

// Create agent with the built-in tool
const agent = createAgent({
llm: model,
tools: [codeExecutionTool],
});

// Invoke the agent with a statistics calculation task
const result = await agent.invoke({
messages: [
new HumanMessage(
"Calculate the mean and standard deviation of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"
),
],
});

// Verify the result contains messages
expect(result.messages).toBeDefined();
expect(result.messages.length).toBeGreaterThan(0);

// Find the final AI response
const aiResponse = result.messages.find((msg) => msg.type === "ai") as
| AIMessage
| undefined;
expect(aiResponse).toBeDefined();

// The response should contain content blocks
const content = Array.isArray(aiResponse?.content)
? aiResponse.content
: [];
expect(content.length).toBeGreaterThan(0);

// Verify that code_execution tool was used (server_tool_use block)
expect(content.some((block) => block.type === "server_tool_use")).toBe(
true
);

// Verify that we got a code execution result
expect(
content.some((block) => block.type === "bash_code_execution_tool_result")
).toBe(true);

// The response should contain text with the calculated values
const textBlocks = content.filter((block) => block.type === "text");
const responseText = textBlocks
.map((block) => block.text)
.join(" ")
.toLowerCase();

// The response should mention the mean (5.5) and standard deviation (~2.87)
expect(responseText).toMatch(/mean|average/i);
expect(responseText).toMatch(/standard deviation|std/i);
expect(responseText).toMatch(/5\.5/); // Expected mean
expect(responseText).toMatch(/2\.[89]|3\./); // Expected stdev (approximately 2.87)
}, 60000); // Set timeout to 60s for API call
});