Skip to content
Open
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
44 changes: 44 additions & 0 deletions libs/providers/langchain-anthropic/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,50 @@ const response = await llmWithMemory.invoke(

For more information, see [Anthropic's Memory Tool documentation](https://docs.anthropic.com/en/docs/agents-and-tools/tool-use/memory-tool).

### Web Search Tool

The web search tool (`webSearch_20250305`) gives Claude direct access to real-time web content, allowing it to answer questions with up-to-date information beyond its knowledge cutoff. Claude automatically cites sources from search results as part of its answer.

```typescript
import { ChatAnthropic, tools } from "@langchain/anthropic";

const llm = new ChatAnthropic({
model: "claude-sonnet-4-5-20250929",
});

// Basic usage
const response = await llm.invoke("What is the weather in NYC?", {
tools: [tools.webSearch_20250305()],
});
```

The web search tool supports several configuration options:

```typescript
const response = await llm.invoke("Latest news about AI?", {
tools: [
tools.webSearch_20250305({
// Maximum number of times the tool can be used in the API request
maxUses: 5,
// Only include results from these domains
allowedDomains: ["reuters.com", "bbc.com"],
// Or block specific domains (cannot be used with allowedDomains)
// blockedDomains: ["example.com"],
// Provide user location for more relevant results
userLocation: {
type: "approximate",
city: "San Francisco",
region: "California",
country: "US",
timezone: "America/Los_Angeles",
},
}),
],
});
```

For more information, see [Anthropic's Web Search Tool documentation](https://docs.anthropic.com/en/docs/build-with-claude/tool-use/web-search-tool).

## Development

To develop the Anthropic package, you'll need to follow these instructions:
Expand Down
9 changes: 8 additions & 1 deletion libs/providers/langchain-anthropic/src/tools/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,9 @@
export { memory_20250818 } from "./memory.js";
import { memory_20250818 } from "./memory.js";
import { webSearch_20250305 } from "./webSearch.js";

export const tools = {
memory_20250818,
webSearch_20250305,
};

export type * from "./types.js";
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { expect, it, describe } from "vitest";
import {
AIMessage,
AIMessageChunk,
HumanMessage,
} from "@langchain/core/messages";
import { concat } from "@langchain/core/utils/stream";

import { ChatAnthropic } from "../../chat_models.js";
import { webSearch_20250305 } from "../webSearch.js";

const createModel = () =>
new ChatAnthropic({
model: "claude-sonnet-4-5-20250929",
temperature: 0,
});

describe("Anthropic Web Search Tool Integration Tests", () => {
it("web search tool can be bound to ChatAnthropic and triggers server tool use", async () => {
const llm = createModel();
const llmWithWebSearch = llm.bindTools([
webSearch_20250305({ maxUses: 1 }),
]);

const response = await llmWithWebSearch.invoke([
new HumanMessage("What is the current weather in San Francisco?"),
]);

expect(response).toBeInstanceOf(AIMessage);
expect(Array.isArray(response.content)).toBe(true);

const contentBlocks = response.content as Array<{ type: string }>;
const hasServerToolUse = contentBlocks.some(
(block) => block.type === "server_tool_use"
);
const hasWebSearchResult = contentBlocks.some(
(block) => block.type === "web_search_tool_result"
);

expect(hasServerToolUse).toBe(true);
expect(hasWebSearchResult).toBe(true);
}, 30000);

it("web search tool streaming works correctly", async () => {
const llm = createModel();
const llmWithWebSearch = llm.bindTools([
webSearch_20250305({ maxUses: 1 }),
]);

const stream = await llmWithWebSearch.stream([
new HumanMessage(
"What is the capital of France according to a web search?"
),
]);

let finalChunk: AIMessageChunk | undefined;
for await (const chunk of stream) {
if (!finalChunk) {
finalChunk = chunk;
} else {
finalChunk = concat(finalChunk, chunk);
}
}

expect(finalChunk).toBeDefined();
expect(finalChunk).toBeInstanceOf(AIMessageChunk);
expect(Array.isArray(finalChunk?.content)).toBe(true);
const contentBlocks = finalChunk?.content as Array<{ type: string }>;
const hasServerToolUse = contentBlocks.some(
(block) => block.type === "server_tool_use"
);
const hasWebSearchResult = contentBlocks.some(
(block) => block.type === "web_search_tool_result"
);

expect(hasServerToolUse).toBe(true);
expect(hasWebSearchResult).toBe(true);
}, 30000);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { expect, it, describe } from "vitest";
import { webSearch_20250305 } from "../webSearch.js";

describe("Anthropic Web Search Tool Integration Tests", () => {
it("webSearch_20250305 creates a valid tool definition", () => {
expect(webSearch_20250305()).toMatchInlineSnapshot(`
{
"allowed_domains": undefined,
"blocked_domains": undefined,
"cache_control": undefined,
"defer_loading": undefined,
"max_uses": undefined,
"name": "web_search",
"strict": undefined,
"type": "web_search_20250305",
"user_location": undefined,
}
`);

expect(
webSearch_20250305({
maxUses: 3,
allowedDomains: ["example.com", "docs.example.com"],
cacheControl: { type: "ephemeral" },
deferLoading: true,
strict: true,
userLocation: {
type: "approximate",
country: "US",
region: "California",
city: "San Francisco",
},
})
).toMatchInlineSnapshot(`
{
"allowed_domains": [
"example.com",
"docs.example.com",
],
"blocked_domains": undefined,
"cache_control": {
"type": "ephemeral",
},
"defer_loading": true,
"max_uses": 3,
"name": "web_search",
"strict": true,
"type": "web_search_20250305",
"user_location": {
"city": "San Francisco",
"country": "US",
"region": "California",
"type": "approximate",
},
}
`);
});
});
93 changes: 93 additions & 0 deletions libs/providers/langchain-anthropic/src/tools/webSearch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import Anthropic from "@anthropic-ai/sdk";

/**
* Options for the web search tool.
*/
interface WebSearch20250305Options {
/**
* Maximum number of times the tool can be used in the API request.
*/
maxUses?: number;
/**
* If provided, only these domains will be included in results. Cannot be used
* alongside `blocked_domains`.
*/
allowedDomains?: string[];
/**
* If provided, these domains will never appear in results. Cannot be used
* alongside `allowed_domains`.
*/
blockedDomains?: string[];
/**
* Create a cache control breakpoint at this content block.
*/
cacheControl?: Anthropic.Beta.BetaCacheControlEphemeral;
/**
* Parameters for the user's location. Used to provide more relevant search
* results.
*/
userLocation?: Anthropic.Beta.BetaWebSearchTool20250305.UserLocation;
/**
* If true, tool will not be included in initial system prompt. Only loaded when
* returned via tool_reference from tool search.
*/
deferLoading?: boolean;
/**
* If true, tool will only return results from the allowed domains.
*/
strict?: boolean;
}

/**
* Creates a web search tool that gives Claude direct access to real-time web content,
* allowing it to answer questions with up-to-date information beyond its knowledge cutoff.
* Claude automatically cites sources from search results as part of its answer.
*
* @see {@link https://docs.anthropic.com/en/docs/build-with-claude/tool-use/web-search-tool | Anthropic Web Search Documentation}
* @param options - Configuration options for the web search tool
* @returns A web search tool definition to be passed to the Anthropic API
*
* @example
* ```typescript
* import { ChatAnthropic, tools } from "@langchain/anthropic";
*
* const model = new ChatAnthropic({
* model: "claude-sonnet-4-5-20250929",
* });
*
* // Basic usage
* const response = await model.invoke("What is the weather in NYC?", {
* tools: [tools.webSearch_20250305()],
* });
*
* // With options
* const responseWithOptions = await model.invoke("Latest news about AI?", {
* tools: [tools.webSearch_20250305({
* maxUses: 5,
* allowedDomains: ["reuters.com", "bbc.com"],
* userLocation: {
* type: "approximate",
* city: "San Francisco",
* region: "California",
* country: "US",
* timezone: "America/Los_Angeles",
* },
* })],
* });
* ```
*/
export function webSearch_20250305(
options?: WebSearch20250305Options
): Anthropic.Beta.BetaWebSearchTool20250305 {
return {
type: "web_search_20250305",
name: "web_search",
max_uses: options?.maxUses,
allowed_domains: options?.allowedDomains,
blocked_domains: options?.blockedDomains,
cache_control: options?.cacheControl,
defer_loading: options?.deferLoading,
strict: options?.strict,
user_location: options?.userLocation,
};
}