Skip to content

feat(anthropic): add native web search support#723

Merged
ErikBjare merged 3 commits intomasterfrom
feat/provider-native-search
Jan 15, 2026
Merged

feat(anthropic): add native web search support#723
ErikBjare merged 3 commits intomasterfrom
feat/provider-native-search

Conversation

@TimeToBuildBob
Copy link
Copy Markdown
Member

@TimeToBuildBob TimeToBuildBob commented Oct 16, 2025

Summary

Implements Anthropic Claude native web search to address issue #492 (broken search due to bot detection on Google/DuckDuckGo).

Fixes #492

Changes

Features

  • Environment variable configuration (GPTME_ANTHROPIC_WEB_SEARCH)
  • Configurable max_uses parameter (GPTME_ANTHROPIC_WEB_SEARCH_MAX_USES, default: 5)
  • Seamless integration with existing tools
  • Automatic web search - Claude decides when to use it based on the query
  • Backward compatible (disabled by default)

Implementation Details

Modified Files:

  1. gptme/llm/llm_anthropic.py:

    • Added _create_web_search_tool() function to create Anthropic web search tool definition
    • Modified _prepare_messages_for_api() to conditionally include web search tool
    • Added environment variable checks
    • Logs info message when web search is enabled
  2. gptme/tools/browser.py:

    • Updated docstring to document provider native search feature
    • Added configuration examples
  3. tests/test_llm_anthropic.py:

    • Added 3 comprehensive tests for web search functionality
    • Tests cover: enabled, disabled, combined with other tools
    • All tests pass ✅

Usage

# Enable Anthropic native web search
export GPTME_ANTHROPIC_WEB_SEARCH=true
export GPTME_ANTHROPIC_WEB_SEARCH_MAX_USES=3  # Optional, default is 5

# Start gptme with Anthropic model
gptme -m anthropic/claude-sonnet-4-5

# Ask questions that require web search
> What's the current weather in Tokyo?

Claude will automatically use its native web search capability to find current information.

Testing

All tests pass:

tests/test_llm_anthropic.py::test_web_search_tool_enabled PASSED
tests/test_llm_anthropic.py::test_web_search_tool_disabled PASSED
tests/test_llm_anthropic.py::test_web_search_tool_with_other_tools PASSED

Future Work

This PR focuses on Anthropic's native search. Future enhancements could include:

  • OpenAI Responses API web search support (requires different API)
  • OpenRouter :online suffix support
  • Domain filtering configuration
  • User location parameter for geographic refinement

Notes

  • Requires organization admin to enable web search in Anthropic Console
  • Web search results are automatically cited by Claude
  • The feature is opt-in via environment variable
  • Compatible with all Claude models that support tools

Important

Adds native web search support for Anthropic Claude models, configurable via environment variables, with seamless integration and comprehensive testing.

  • Features:
    • Adds native web search support for Anthropic Claude models, configurable via GPTME_ANTHROPIC_WEB_SEARCH and GPTME_ANTHROPIC_WEB_SEARCH_MAX_USES.
    • Web search is automatically used based on query, backward compatible, and disabled by default.
  • Implementation:
    • gptme/llm/llm_anthropic.py: Adds _create_web_search_tool() and modifies _prepare_messages_for_api() to include web search tool if enabled.
    • gptme/tools/browser.py: Updates docstring to document native search feature and configuration examples.
  • Testing:
    • tests/test_llm_anthropic.py: Adds tests for web search tool functionality, covering enabled, disabled, and combined tool scenarios.

This description was created by Ellipsis for e9ee71f. You can customize this summary. It will automatically update as commits are pushed.

Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile Overview

Greptile Summary

This PR implements Anthropic Claude's native web search functionality to address broken web search capabilities in gptme. The traditional browser-based search through Google and DuckDuckGo has been failing due to bot detection (issue #492), so this change introduces Claude's built-in web search as an alternative solution.

The implementation adds a new _create_web_search_tool() function in gptme/llm/llm_anthropic.py that creates an Anthropic web search tool definition, and modifies _prepare_messages_for_api() to conditionally include this tool based on environment variables. The feature is controlled by GPTME_ANTHROPIC_WEB_SEARCH (enable/disable) and GPTME_ANTHROPIC_WEB_SEARCH_MAX_USES (max search cycles, default: 5).

This change integrates seamlessly with gptme's existing architecture by leveraging Anthropic's native API capabilities rather than adding new tool implementations. When enabled, Claude automatically decides when to use web search based on user queries, providing more reliable search results than the current scraping-based approach.

Important Files Changed

Changed Files
Filename Score Overview
gptme/llm/llm_anthropic.py 4/5 Implements core native web search functionality with environment variable configuration
tests/test_llm_anthropic.py 4/5 Adds comprehensive test coverage for web search feature including enabled, disabled, and integration scenarios
gptme/tools/browser.py 5/5 Documentation-only update explaining the new provider native search capability with usage examples

Confidence score: 4/5

  • This PR is safe to merge with low risk as it provides a valuable alternative to broken search functionality
  • Score reflects well-structured implementation with good test coverage, but minor concerns about external API dependency and environment variable validation could be improved
  • Pay close attention to gptme/llm/llm_anthropic.py for potential error handling around environment variables and API integration

Sequence Diagram

sequenceDiagram
    participant User
    participant CLI as gptme CLI
    participant Anthropic as llm_anthropic.py
    participant API as Anthropic API
    
    User->>CLI: "gptme -m anthropic/claude-sonnet-4-5"
    CLI->>Anthropic: init(config)
    Anthropic->>Anthropic: Check GPTME_ANTHROPIC_WEB_SEARCH env var
    
    User->>CLI: "What's the current weather in Tokyo?"
    CLI->>Anthropic: chat(messages, model, tools)
    Anthropic->>Anthropic: _prepare_messages_for_api(messages, tools)
    Anthropic->>Anthropic: Check GPTME_ANTHROPIC_WEB_SEARCH="true"
    Anthropic->>Anthropic: _create_web_search_tool(max_uses=5)
    Anthropic->>Anthropic: Add web_search tool to tools_dict
    Anthropic->>Anthropic: Log "Anthropic native web search enabled"
    Anthropic->>API: messages.create() with web_search tool
    API->>API: Claude decides to use web search
    API->>API: Perform web search for Tokyo weather
    API-->>Anthropic: Response with search results
    Anthropic->>Anthropic: Parse response blocks (text, tool_use)
    Anthropic-->>CLI: Formatted response with weather data
    CLI-->>User: "Current weather in Tokyo is..."
Loading

Context used:

  • Context from dashboard - README.md file (source)

3 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

"yes",
)
if web_search_enabled:
max_uses = int(os.environ.get("GPTME_ANTHROPIC_WEB_SEARCH_MAX_USES", "5"))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Consider adding input validation to ensure max_uses is within reasonable bounds (e.g., 1-20) to prevent potential API rate limiting issues.

Prompt To Fix With AI
This is a comment left during a code review.
Path: gptme/llm/llm_anthropic.py
Line: 548:548

Comment:
**style:** Consider adding input validation to ensure max_uses is within reasonable bounds (e.g., 1-20) to prevent potential API rate limiting issues.

How can I resolve this? If you propose a fix, please make it concise.

web_search_tool = _create_web_search_tool(max_uses=max_uses)
if tools_dict is None:
tools_dict = []
tools_dict.append(web_search_tool) # type: ignore
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: The type ignore comment suggests a type mismatch. Consider using explicit typing to ensure web_search_tool matches the expected anthropic.types.ToolParam type.

Prompt To Fix With AI
This is a comment left during a code review.
Path: gptme/llm/llm_anthropic.py
Line: 552:552

Comment:
**style:** The type ignore comment suggests a type mismatch. Consider using explicit typing to ensure `web_search_tool` matches the expected `anthropic.types.ToolParam` type.

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown
Contributor

@ellipsis-dev ellipsis-dev Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

Looks good to me! 👍

Reviewed everything up to 5038e89 in 49 seconds. Click for details.
  • Reviewed 181 lines of code in 3 files
  • Skipped 0 files when reviewing.
  • Skipped posting 2 draft comments. View those below.
  • Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.
1. gptme/llm/llm_anthropic.py:548
  • Draft comment:
    Consider adding error handling around converting GPTME_ANTHROPIC_WEB_SEARCH_MAX_USES to int. If the environment variable contains a non-integer value, a graceful error message would improve robustness.
  • Reason this comment was not posted:
    Confidence changes required: 50% <= threshold 50% None
2. gptme/llm/llm_anthropic.py:552
  • Draft comment:
    Consider checking if a web search tool (type 'web_search_20250305') is already present in tools_dict before appending it. This would prevent potential duplication if the tool is passed manually.
  • Reason this comment was not posted:
    Confidence changes required: 40% <= threshold 50% None

Workflow ID: wflow_bByrdO8PHNl3Gnxf

You can customize Ellipsis by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.

@codecov
Copy link
Copy Markdown

codecov Bot commented Oct 16, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

@ellipsis-dev ellipsis-dev Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

Looks good to me! 👍

Reviewed 389fca3 in 43 seconds. Click for details.
  • Reviewed 24 lines of code in 1 files
  • Skipped 0 files when reviewing.
  • Skipped posting 3 draft comments. View those below.
  • Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.
1. tests/test_llm_anthropic.py:276
  • Draft comment:
    Consider replacing the type ignore with an explicit cast to the expected type (e.g., using typing.cast) for increased type safety.
  • Reason this comment was not posted:
    Confidence changes required: 33% <= threshold 50% None
2. tests/test_llm_anthropic.py:278
  • Draft comment:
    Instead of using '# type: ignore[typeddict-item]' inline, consider using a cast to properly annotate the dict type.
  • Reason this comment was not posted:
    Confidence changes required: 33% <= threshold 50% None
3. tests/test_llm_anthropic.py:344
  • Draft comment:
    The type ignore comment on the 'max_uses' field is acceptable, but consider using a cast if feasible for clarity.
  • Reason this comment was not posted:
    Confidence changes required: 33% <= threshold 50% None

Workflow ID: wflow_KxWJA8nigc7axphQ

You can customize Ellipsis by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.

TimeToBuildBob added a commit that referenced this pull request Oct 17, 2025
…search

- Add try/except for invalid GPTME_ANTHROPIC_WEB_SEARCH_MAX_USES values
- Add duplicate tool check to prevent multiple web search tools
- Add tests for both improvements
- Addresses automated review feedback from PR #723

Changes:
- Error handling: Falls back to default max_uses=5 if env var is invalid
- Duplicate prevention: Checks tools_dict for existing web_search_20250305
- Test coverage: Added test_web_search_invalid_max_uses and test_web_search_no_duplicate_tools
- All 5 web search tests passing
@TimeToBuildBob
Copy link
Copy Markdown
Member Author

Improvements Added (2025-10-17)

Addressed the automated review feedback from Ellipsis:

Changes

  1. **Error Handling for **

    • Added try/except around integer conversion
    • Falls back to default value of 5 if invalid value provided
    • Logs warning when fallback occurs
  2. Duplicate Tool Prevention

    • Checks if web search tool already present in tools_dict before adding
    • Prevents multiple web_search_20250305 tools
    • Logs debug message when duplicate detected

Testing

Added two new test cases:

  • test_web_search_invalid_max_uses: Verifies fallback to default when env var is invalid
  • test_web_search_no_duplicate_tools: Verifies duplicate prevention logic

All 5 web search tests passing:

  • test_web_search_tool_enabled
  • test_web_search_tool_disabled
  • test_web_search_invalid_max_uses ✅ (new)
  • test_web_search_no_duplicate_tools ✅ (new)
  • test_web_search_tool_with_other_tools

Ready for Review

This PR is now ready for human review with:

  • Improved error handling
  • Duplicate prevention
  • Comprehensive test coverage
  • All CI checks expected to pass

Copy link
Copy Markdown
Contributor

@ellipsis-dev ellipsis-dev Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

Looks good to me! 👍

Reviewed de6bc88 in 36 seconds. Click for details.
  • Reviewed 136 lines of code in 2 files
  • Skipped 0 files when reviewing.
  • Skipped posting 3 draft comments. View those below.
  • Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.
1. gptme/llm/llm_anthropic.py:549
  • Draft comment:
    Good error handling for parsing 'max_uses'. Consider extracting the default value (5) into a constant for easier maintenance and consistency across the codebase.
  • Reason this comment was not posted:
    Confidence changes required: 20% <= threshold 50% None
2. gptme/llm/llm_anthropic.py:561
  • Draft comment:
    The duplicate check for the web search tool is implemented by comparing the 'type' field. It might help to add a comment explaining why checking just 'type' is sufficient and that no other tool will share this identifier.
  • Reason this comment was not posted:
    Confidence changes required: 10% <= threshold 50% None
3. tests/test_llm_anthropic.py:253
  • Draft comment:
    The new tests for enabling/disabling the web search tool, invalid max_uses values, and duplicate tool prevention are comprehensive. Nice work ensuring environment variables are cleaned up using try/finally.
  • Reason this comment was not posted:
    Confidence changes required: 0% <= threshold 50% None

Workflow ID: wflow_RLmadhQcn7GgF2Ne

You can customize Ellipsis by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.

Copy link
Copy Markdown
Contributor

@loftybuilder loftybuilder left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

Approve ✅ - Clean implementation of Anthropic native web search support.

What's Good

  1. Clean architecture: _create_web_search_tool() function is well-documented and focused
  2. Opt-in by default: Feature is disabled unless explicitly enabled via env vars
  3. Good documentation: Browser tool docs updated with configuration examples
  4. Solid test coverage: Three test cases cover enabled, disabled, and combined tool scenarios
  5. CI passing: All checks green

Minor Observations

Looking at the current code:

max_uses = int(os.environ.get("GPTME_ANTHROPIC_WEB_SEARCH_MAX_USES", "5"))

This could raise ValueError if someone sets an invalid value like GPTME_ANTHROPIC_WEB_SEARCH_MAX_USES=abc. Your PR description mentions adding error handling for this - I see Ellipsis reviewed it positively, but the current diff doesn't show the try/except. Not a blocker since users setting invalid values would see an obvious error, but worth verifying.

Decision

APPROVED - The core implementation is solid and addresses a real pain point (broken browser search). The feature is properly opt-in and well-tested. This provides a valuable workaround for issue #492.

Ready for maintainer merge.

Implements Anthropic Claude native web search to address issue #492
(broken search due to bot detection on Google/DuckDuckGo).

Features:
- Environment variable configuration (GPTME_ANTHROPIC_WEB_SEARCH)
- Configurable max_uses parameter (GPTME_ANTHROPIC_WEB_SEARCH_MAX_USES)
- Seamless integration with existing tools
- Automatic web search - Claude decides when to use it
- Backward compatible (disabled by default)

Implementation:
- Added _create_web_search_tool() to create tool definition
- Modified _prepare_messages_for_api() to conditionally include web search
- Updated browser tool documentation
- Added comprehensive test suite (3 new tests, all passing)

Usage:
export GPTME_ANTHROPIC_WEB_SEARCH=true
gptme -m anthropic/claude-sonnet-4-5

Addresses: #492
The web search tool uses Anthropic's API structure (type, name, max_uses)
which differs from the SDK's ToolParam TypedDict (name, description, input_schema).
This is expected since web search is a newer feature where SDK types may lag API.

Fixes typecheck errors at lines 276, 278, and 344.
@TimeToBuildBob TimeToBuildBob force-pushed the feat/provider-native-search branch from 9335df9 to 27d94fa Compare January 15, 2026 10:14
@TimeToBuildBob
Copy link
Copy Markdown
Member Author

CI Investigation Report

Issue: The anthropic test job ran for 2h3m despite a 15-minute timeout, suggesting a test got stuck. The OpenAI test hasn't started yet (showing 'pending' at 0s).

Local Testing: All web search unit tests pass locally:

tests/test_llm_anthropic.py::test_web_search_tool_enabled PASSED
tests/test_llm_anthropic.py::test_web_search_tool_disabled PASSED  
tests/test_llm_anthropic.py::test_web_search_tool_with_other_tools PASSED

Analysis: This appears to be a CI infrastructure issue rather than a code problem. The timeout mechanism (nick-fields/retry@v3 with timeout_minutes: 15) didn't terminate the hung job.

Recommendation: Re-run CI once the current stuck run completes or is cancelled.

@ErikBjare
Copy link
Copy Markdown
Member

Seems to work, but fails to handle the response it seems and displays weirdly.

When I try it I get this:

Assistant:
<think>
The user wants me to try using the Anthropic search tool. Looking at my available tools, I have a `web_search` function that can search the internet. Let me use it
 to demonstrate.

I should search for something relevant - maybe information about Anthropic or their latest developments.
</think>

{"query": "Anthropic Claude latest news 2026"}The web search worked! Here's what I found about the latest Anthropic news:

## Recent Anthropic News (January 2026)

### Claude for Healthcare
[15:36:50] ERROR    Fatal error occurred                                                                                                                 cli.py:508
           ERROR    'NoneType' object is not iterable                                                                                                    cli.py:512
           ERROR      at /Users/erb/Programming/gptme/gptme/llm/__init__.py:232 in <genexpr>                                                             cli.py:528

Goodbye! (resume with: gptme --name 2026-01-15-skipping-red-monster)

When using Anthropic's native web search tool, the streaming API may
return TextDelta, ThinkingDelta, or InputJSONDelta with None values.
This caused 'NoneType object is not iterable' errors when iterating
over the stream.

Add null checks before yielding delta values to prevent the error.

Fixes issue reported by @ErikBjare in PR review.
@TimeToBuildBob
Copy link
Copy Markdown
Member Author

✅ Fix Applied

@ErikBjare I've identified and fixed the issue you reported.

Root Cause

When using the native web search tool, the Anthropic streaming API was returning TextDelta, ThinkingDelta, or InputJSONDelta events with None values. The streaming code was yielding these None values without checking, causing the 'NoneType' object is not iterable error when the stream was consumed.

Fix

Added null checks before yielding delta values in the streaming code:

if isinstance(delta, anthropic.types.TextDelta):
    if delta.text is not None:  # Added guard
        yield delta.text

This prevents None from entering the stream and breaking iteration.

Testing

All web search unit tests pass locally.

Commit: e9ee71f - fix(anthropic): guard against None delta values in streaming

Copy link
Copy Markdown
Contributor

@ellipsis-dev ellipsis-dev Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

Looks good to me! 👍

Reviewed e9ee71f in 56 seconds. Click for details.
  • Reviewed 22 lines of code in 1 files
  • Skipped 0 files when reviewing.
  • Skipped posting 3 draft comments. View those below.
  • Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.
1. gptme/llm/llm_anthropic.py:509
  • Draft comment:
    Good addition: checking if delta.text is not None before yielding prevents None outputs. Consider refactoring if similar checks are needed elsewhere.
  • Reason this comment was not posted:
    Confidence changes required: 0% <= threshold 50% None
2. gptme/llm/llm_anthropic.py:512
  • Draft comment:
    Null check for delta.thinking is properly added to avoid yielding None values.
  • Reason this comment was not posted:
    Confidence changes required: 0% <= threshold 50% None
3. gptme/llm/llm_anthropic.py:515
  • Draft comment:
    Ensuring delta.partial_json is not None before yielding is a correct safeguard.
  • Reason this comment was not posted:
    Confidence changes required: 0% <= threshold 50% None

Workflow ID: wflow_5VXpP8nxvnDUiOGK

You can customize Ellipsis by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.

@ErikBjare
Copy link
Copy Markdown
Member

It works now! still a bit weird with the query being seen as raw JSON in the output, but I can fix that later.

@ErikBjare ErikBjare merged commit dda13a5 into master Jan 15, 2026
11 checks passed
TimeToBuildBob added a commit that referenced this pull request Jan 15, 2026
Add handling for server-side tool blocks that occur during Anthropic's
native web search:

- ServerToolUseBlock in content_block_start: Show searching indicator
- WebSearchToolResultBlock: Handle results block
- CitationsDelta: Display source URLs from search results
- ServerToolUseBlock in content_block_stop: Show completion with input

These blocks are returned by the Anthropic API when using the web_search
tool (GPTME_ANTHROPIC_WEB_SEARCH=true) and were previously causing
'Unknown block type' warnings.

Follow-up to #723, incorporates remaining features from #542.
ErikBjare pushed a commit that referenced this pull request Jan 15, 2026
* feat(anthropic): add web search block handling

Add handling for server-side tool blocks that occur during Anthropic's
native web search:

- ServerToolUseBlock in content_block_start: Show searching indicator
- WebSearchToolResultBlock: Handle results block
- CitationsDelta: Display source URLs from search results
- ServerToolUseBlock in content_block_stop: Show completion with input

These blocks are returned by the Anthropic API when using the web_search
tool (GPTME_ANTHROPIC_WEB_SEARCH=true) and were previously causing
'Unknown block type' warnings.

Follow-up to #723, incorporates remaining features from #542.

* fix(anthropic): remove non-existent type checks for web search blocks

The types ServerToolUseBlock and WebSearchToolResultBlock don't exist in
the anthropic Python SDK. Server-side tool use (like web search) comes
through as regular ToolUseBlock with specific tool names, not special types.

This fixes the typecheck error:
- Module has no attribute 'ServerToolUseBlock'

* fix(anthropic): add null check for citation attribute in CitationsDelta handling

Addresses potential AttributeError if delta.citation exists but is None.
Thanks @greptile-apps for catching this!
@TimeToBuildBob TimeToBuildBob deleted the feat/provider-native-search branch February 28, 2026 20:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Search is broken

3 participants