# Search Tools (Python @tool Pattern)

Web search capabilities using Google (primary) and DuckDuckGo (fallback).

## Installation

```bash
uv add tool-search-tools
```

## Tools Provided

| Tool | Description |
|------|-------------|
| `web_search` | Search with automatic Google → DuckDuckGo fallback |
| `search_google` | Google only (requires API credentials) |
| `search_duckduckgo` | DuckDuckGo only (no credentials needed) |

## Configuration

### Google Search (Optional)

Set environment variables for Google Programmable Search Engine:

```bash
export GOOGLE_API_KEY="your-api-key"
export GOOGLE_CSE_ID="your-cse-id"
```

Without these, `web_search` automatically falls back to DuckDuckGo.

### Rate Limiting (Optional)

```bash
export GOOGLE_SEARCH_RATE_LIMIT="1.0"      # requests per second
export GOOGLE_SEARCH_TIMEOUT="30.0"        # seconds
export DUCKDUCKGO_SEARCH_RATE_LIMIT="1.0"
export DUCKDUCKGO_SEARCH_TIMEOUT="30.0"
```

## Usage

### Direct Invocation

```python
from search_tools import web_search

result = await web_search.ainvoke({"query": "python async tutorial", "num_results": 5})

print(result["provider"])  # "google" or "duckduckgo"
for r in result["results"]:
    print(f"{r['title']}: {r['url']}")
```

### With LangGraph

```python
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import ToolNode
from search_tools import web_search

tools = [web_search]
llm = ChatOpenAI(model="gpt-4o-mini")
llm_with_tools = llm.bind_tools(tools)
tool_node = ToolNode(tools)
```

### Response Format

```python
{
    "results": [
        {"title": "Page Title", "url": "https://...", "snippet": "Description..."},
        ...
    ],
    "provider": "google",  # or "duckduckgo"
    "query": "original query",
    "count": 5
}
```

## When to Use Which

| Tool | Use Case |
|------|----------|
| `web_search` | Default choice - automatic fallback |
| `search_google` | Need Google specifically, handle errors yourself |
| `search_duckduckgo` | Testing, or when Google quota exhausted |
