From d04c6d4eea26db97d112eb55f33674238df5a968 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 26 Sep 2025 11:04:11 -0700 Subject: [PATCH] [Feat] Add new anthropic web fetch tool support (#14951) * add web_fetch tool for ANTHROPIC_HOSTED_TOOLS * add ANTHROPIC_BETA_HEADER_VALUES, ANTHROPIC_HOSTED_TOOLS * feat: add web fetch tool anthropic * test_anthropic_tool_use * docs web fetch * docs fix * docs fix --- docs/my-website/docs/completion/web_fetch.md | 294 ++++++++++++++++++ docs/my-website/docs/completion/web_search.md | 2 +- docs/my-website/docs/providers/vertex.md | 2 +- .../docs/proxy/native_litellm_prompt.md | 8 +- docs/my-website/sidebars.js | 15 +- litellm/llms/anthropic/chat/transformation.py | 20 +- litellm/types/llms/anthropic.py | 14 + .../test_anthropic_completion.py | 51 ++- 8 files changed, 374 insertions(+), 32 deletions(-) create mode 100644 docs/my-website/docs/completion/web_fetch.md diff --git a/docs/my-website/docs/completion/web_fetch.md b/docs/my-website/docs/completion/web_fetch.md new file mode 100644 index 0000000000..30a15e4449 --- /dev/null +++ b/docs/my-website/docs/completion/web_fetch.md @@ -0,0 +1,294 @@ +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Web Fetch + +The web fetch tool allows LLMs to retrieve full content from specified web pages and PDF documents. This enables AI models to access real-time information from the internet and incorporate web content into their responses. + +## Web Fetch vs Web Search + +**Web Fetch** retrieves the full content from specific web pages that you provide URLs for, while **Web Search** performs internet searches to find relevant information based on your queries. + +| Feature | Web Fetch | Web Search | +|---------|-----------|------------| +| **Purpose** | Retrieve content from specific URLs | Search the internet for information | +| **Input** | You provide exact URLs to fetch | You provide search queries/questions | +| **Output** | Full page content from specified URLs | Search results with relevant information | +| **Use Cases** | - Analyzing specific articles
- Comparing content from known websites
- Extracting data from particular pages | - Finding current news/events
- Researching topics
- Getting real-time information | + + +**Example Web Fetch**: "Fetch the content from https://example.com/pricing and summarize it" +**Example Web Search**: "What are the latest AI developments this week?" + +**Supported Providers:** +- Anthropic API (`anthropic/`) + +**Supported Tool Types:** +- `web_fetch_20250910` - Web content retrieval tool with usage limits, domain filtering, and citation support + + +## Quick Start + +### LiteLLM Python SDK + +```python +import os +from litellm import completion + +os.environ["ANTHROPIC_API_KEY"] = "your-api-key" + +# Web fetch tool +tools = [ + { + "type": "web_fetch_20250910", + "name": "web_fetch", + "max_uses": 5, + } +] + +messages = [ + { + "role": "user", + "content": "Please analyze the content at https://example.com/article and summarize the main points" + } +] + +response = completion( + model="anthropic/claude-3-5-sonnet-latest", + messages=messages, + tools=tools, +) + +print(response) +``` + +### LiteLLM Proxy + +1. Define web fetch models on config.yaml + +```yaml +model_list: + - model_name: claude-3-5-sonnet-latest # Anthropic claude-3-5-sonnet-latest + litellm_params: + model: anthropic/claude-3-5-sonnet-latest + api_key: os.environ/ANTHROPIC_API_KEY +``` + +2. Run proxy server + +```bash +litellm --config config.yaml +``` + +3. Test it using the OpenAI Python SDK + +```python +import os +from openai import OpenAI + +client = OpenAI( + api_key="sk-1234", # your litellm proxy api key + base_url="http://0.0.0.0:4000" +) + +response = client.chat.completions.create( + model="claude-3-5-sonnet-latest", + messages=[ + { + "role": "user", + "content": "Please fetch and analyze the content from https://news.ycombinator.com and tell me about the top stories" + } + ], + tools=[ + { + "type": "web_fetch_20250910", + "name": "web_fetch", + "max_uses": 5, + } + ] +) + +print(response) +``` + +## Supported Models + +Web fetch is available on the following Anthropic API models: + +- `claude-opus-4-1-20250805` (Claude Opus 4.1) +- `claude-opus-4-20250514` (Claude Opus 4) +- `claude-sonnet-4-20250514` (Claude Sonnet 4) +- `claude-3-7-sonnet-20250219` (Claude Sonnet 3.7) +- `claude-3-5-sonnet-latest` (Claude Sonnet 3.5 v2 - deprecated) +- `claude-3-5-haiku-latest` (Claude Haiku 3.5) + +:::note +The web fetch tool currently does not support websites dynamically rendered via JavaScript. +::: + +## Usage Examples + +### Basic Web Content Retrieval + +```python +import os +from litellm import completion + +os.environ["ANTHROPIC_API_KEY"] = "your-api-key" + +tools = [ + { + "type": "web_fetch_20250910", + "name": "web_fetch", + "max_uses": 3, + } +] + +messages = [ + { + "role": "user", + "content": "Fetch the latest news from https://techcrunch.com and summarize the top 3 articles" + } +] + +response = completion( + model="anthropic/claude-3-5-sonnet-latest", + messages=messages, + tools=tools, +) + +print(response) +``` + +### Research and Analysis + +```python +import os +from litellm import completion + +os.environ["ANTHROPIC_API_KEY"] = "your-api-key" + +tools = [ + { + "type": "web_fetch_20250910", + "name": "web_fetch", + "max_uses": 10, + } +] + +messages = [ + { + "role": "user", + "content": "Research the latest developments in AI by fetching content from multiple tech news websites and provide a comprehensive analysis" + } +] + +response = completion( + model="anthropic/claude-3-5-sonnet-latest", + messages=messages, + tools=tools, +) + +print(response) +``` + +### Content Comparison + +```python +import os +from litellm import completion + +os.environ["ANTHROPIC_API_KEY"] = "your-api-key" + +tools = [ + { + "type": "web_fetch_20250910", + "name": "web_fetch", + "max_uses": 5, + } +] + +messages = [ + { + "role": "user", + "content": "Compare the pricing information from https://openai.com/pricing and https://anthropic.com/pricing and create a comparison table" + } +] + +response = completion( + model="anthropic/claude-3-5-sonnet-latest", + messages=messages, + tools=tools, +) + +print(response) +``` + +## Advanced Usage with Multiple Tools + +You can combine web fetch with other tools like computer use or text editor: + +```python +import os +from litellm import completion + +os.environ["ANTHROPIC_API_KEY"] = "your-api-key" + +tools = [ + { + "type": "web_fetch_20250910", + "name": "web_fetch", + "max_uses": 5, + }, + { + "type": "text_editor_20250124", + "name": "str_replace_editor" + } +] + +messages = [ + { + "role": "user", + "content": "Fetch the latest AI research papers from arXiv, analyze them, and create a detailed report file with your findings" + } +] + +response = completion( + model="anthropic/claude-3-5-sonnet-latest", + messages=messages, + tools=tools, +) + +print(response) +``` + +## Spec + +### Web Fetch Tool (`web_fetch_20250910`) + +The web fetch tool supports the following parameters: + +```json +{ + "type": "web_fetch_20250910", + "name": "web_fetch", + + // Optional: Limit the number of fetches per request + "max_uses": 10, + + // Optional: Only fetch from these domains + "allowed_domains": ["example.com", "docs.example.com"], + + // Optional: Never fetch from these domains + "blocked_domains": ["private.example.com"], + + // Optional: Enable citations for fetched content + "citations": { + "enabled": true + }, + + // Optional: Maximum content length in tokens + "max_content_tokens": 100000 +} +``` + diff --git a/docs/my-website/docs/completion/web_search.md b/docs/my-website/docs/completion/web_search.md index 262e3fc4f9..b0d8fcdf4c 100644 --- a/docs/my-website/docs/completion/web_search.md +++ b/docs/my-website/docs/completion/web_search.md @@ -1,7 +1,7 @@ import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -# Using Web Search +# Web Search Use web search with litellm diff --git a/docs/my-website/docs/providers/vertex.md b/docs/my-website/docs/providers/vertex.md index f7cdf867af..9a96987643 100644 --- a/docs/my-website/docs/providers/vertex.md +++ b/docs/my-website/docs/providers/vertex.md @@ -898,7 +898,7 @@ curl http://0.0.0.0:4000/chat/completions \ ``` - + ## Pre-requisites * `pip install google-cloud-aiplatform` (pre-installed on proxy docker image) diff --git a/docs/my-website/docs/proxy/native_litellm_prompt.md b/docs/my-website/docs/proxy/native_litellm_prompt.md index 0a4f1ccf35..ea326d0069 100644 --- a/docs/my-website/docs/proxy/native_litellm_prompt.md +++ b/docs/my-website/docs/proxy/native_litellm_prompt.md @@ -1,9 +1,5 @@ -import '@theme/IdealImage' -import '@theme/TabItem' -import '@theme/Tabs' -import Image -import TabItem -import Tabs +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; # LiteLLM Prompt Management (GitOps) diff --git a/docs/my-website/sidebars.js b/docs/my-website/sidebars.js index 3750915b19..cb68e9024d 100644 --- a/docs/my-website/sidebars.js +++ b/docs/my-website/sidebars.js @@ -518,12 +518,19 @@ const sidebars = { type: "category", label: "Guides", items: [ + { + type: "category", + label: "Tools", + items: [ + "completion/computer_use", + "completion/web_search", + "completion/web_fetch", + "completion/function_call", + ] + }, "completion/audio", - "completion/batching", - "completion/computer_use", "completion/document_understanding", "completion/drop_params", - "completion/function_call", "completion/image_generation_chat", "completion/json_mode", "completion/knowledgebase", @@ -538,8 +545,8 @@ const sidebars = { "completion/stream", "completion/provider_specific_params", "completion/vision", - "completion/web_search", "exception_mapping", + "completion/batching", "guides/finetuned_models", "guides/security_settings", "proxy/veo_video_generation", diff --git a/litellm/llms/anthropic/chat/transformation.py b/litellm/llms/anthropic/chat/transformation.py index 6d41655674..c7f0a6da61 100644 --- a/litellm/llms/anthropic/chat/transformation.py +++ b/litellm/llms/anthropic/chat/transformation.py @@ -18,6 +18,8 @@ from litellm.litellm_core_utils.core_helpers import map_finish_reason from litellm.llms.base_llm.base_utils import type_to_response_format_param from litellm.llms.base_llm.chat.transformation import BaseConfig, BaseLLMException from litellm.types.llms.anthropic import ( + ANTHROPIC_BETA_HEADER_VALUES, + ANTHROPIC_HOSTED_TOOLS, AllAnthropicMessageValues, AllAnthropicToolsValues, AnthropicCodeExecutionTool, @@ -50,7 +52,10 @@ from litellm.types.utils import ( CompletionTokensDetailsWrapper, ) from litellm.types.utils import Message as LitellmMessage -from litellm.types.utils import PromptTokensDetailsWrapper, ServerToolUse +from litellm.types.utils import ( + PromptTokensDetailsWrapper, + ServerToolUse, +) from litellm.utils import ( ModelResponse, Usage, @@ -70,9 +75,6 @@ else: LoggingClass = Any -ANTHROPIC_HOSTED_TOOLS = ["web_search", "bash", "text_editor", "code_execution"] - - class AnthropicConfig(AnthropicModelInfo, BaseConfig): """ Reference: https://docs.anthropic.com/claude/reference/messages_post @@ -639,6 +641,14 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig): ) ) return tools + + def update_headers_with_optional_anthropic_beta(self, headers: dict, optional_params: dict) -> dict: + """Update headers with optional anthropic beta.""" + _tools = optional_params.get("tools", []) + for tool in _tools: + if tool.get("type", None) and tool.get("type").startswith(ANTHROPIC_HOSTED_TOOLS.WEB_FETCH.value): + headers["anthropic-beta"] = ANTHROPIC_BETA_HEADER_VALUES.WEB_FETCH_2025_09_10.value + return headers def transform_request( self, @@ -675,6 +685,8 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig): llm_provider="anthropic", ) + headers = self.update_headers_with_optional_anthropic_beta(headers=headers, optional_params=optional_params) + # Separate system prompt from rest of message anthropic_system_message_list = self.translate_system_message(messages=messages) # Handling anthropic API Prompt Caching diff --git a/litellm/types/llms/anthropic.py b/litellm/types/llms/anthropic.py index 837f4bf9a8..02c6f2cf8c 100644 --- a/litellm/types/llms/anthropic.py +++ b/litellm/types/llms/anthropic.py @@ -1,3 +1,4 @@ +from enum import Enum from typing import Any, Dict, Iterable, List, Optional, Union from pydantic import BaseModel, validator @@ -440,3 +441,16 @@ ANTHROPIC_API_ONLY_HEADERS = { # fails if calling anthropic on vertex ai / bedr class AnthropicThinkingParam(TypedDict, total=False): type: Literal["enabled"] budget_tokens: int + +class ANTHROPIC_HOSTED_TOOLS(str, Enum): + WEB_SEARCH = "web_search" + BASH = "bash" + TEXT_EDITOR = "text_editor" + CODE_EXECUTION = "code_execution" + WEB_FETCH = "web_fetch" + +class ANTHROPIC_BETA_HEADER_VALUES(str, Enum): + """ + Known beta header values for Anthropic. + """ + WEB_FETCH_2025_09_10 = "web-fetch-2025-09-10" \ No newline at end of file diff --git a/tests/llm_translation/test_anthropic_completion.py b/tests/llm_translation/test_anthropic_completion.py index 307c429fc6..4eeb80c419 100644 --- a/tests/llm_translation/test_anthropic_completion.py +++ b/tests/llm_translation/test_anthropic_completion.py @@ -329,32 +329,51 @@ def test_process_anthropic_headers_with_no_matching_headers(): assert result == expected_output, "Unexpected output for non-matching headers" -def test_anthropic_computer_tool_use(): - from litellm import completion - - tools = [ - { - "type": "computer_20241022", - "function": { - "name": "computer", - "parameters": { - "display_height_px": 100, - "display_width_px": 100, - "display_number": 1, +@pytest.mark.parametrize( + "tool_type, tool_config, message_content", + [ + ( + "computer_20241022", + { + "type": "computer_20241022", + "function": { + "name": "computer", + "parameters": { + "display_height_px": 100, + "display_width_px": 100, + "display_number": 1, + }, }, }, - } - ] + "Save a picture of a cat to my desktop.", + ), + ( + "web_fetch_20250910", + { + "type": "web_fetch_20250910", + "name": "web_fetch", + "max_uses": 5, + }, + "Please analyze the content at https://example.com/article", + ), + ], +) +def test_anthropic_tool_use(tool_type, tool_config, message_content): + """Test Anthropic tool use with computer use and web fetch tools.""" + from litellm import completion + litellm._turn_on_debug() + + tools = [tool_config] model = "claude-3-5-sonnet-20241022" - messages = [{"role": "user", "content": "Save a picture of a cat to my desktop."}] + messages = [{"role": "user", "content": message_content}] try: resp = completion( model=model, messages=messages, tools=tools, - # headers={"anthropic-beta": "computer-use-2024-10-22"}, ) + print(f"Tool type: {tool_type}") print(resp) except litellm.InternalServerError: pass