# Agentic Web Search Patches for Open WebUI

Enables ChatGPT-like web search: the model decides when to search via tool calling, not forced on every message.

## Files to Patch

### 1. `tools.py` — Remove `features.web_search` gate

**Path in container:** `/app/backend/open_webui/utils/tools.py`

**Change:** In `get_builtin_tools()`, remove `features.get('web_search')` from the condition (around line 534).

```python
# Before:
    if (
        is_builtin_tool_enabled('web_search')
        and getattr(request.app.state.config, 'ENABLE_WEB_SEARCH', False)
        and get_model_capability('web_search')
        and features.get('web_search')
        and await has_user_permission('web_search')
    ):
        builtin_functions.extend([search_web, fetch_url])

# After:
    if (
        is_builtin_tool_enabled('web_search')
        and getattr(request.app.state.config, 'ENABLE_WEB_SEARCH', False)
        and get_model_capability('web_search')
        and await has_user_permission('web_search')
    ):
        builtin_functions.extend([search_web, fetch_url])
```

### 2. `middleware.py` — Default `function_calling` to `'native'`

**Path in container:** `/app/backend/open_webui/utils/middleware.py`

**Change:** Add `params.setdefault('function_calling', 'native')` after the `open_webui_params` dict definition (around line 2080).

```python
    open_webui_params = {
        'stream_response': bool,
        'stream_delta_chunk_size': int,
        'function_calling': str,
        'reasoning_tags': list,
        'system': str,
    }
    # Default to native function calling for tool-using models
    params.setdefault('function_calling', 'native')
```

## Deployment

```bash
# Copy files out of running container
docker cp open-webui:/app/backend/open_webui/utils/middleware.py /tmp/middleware.py
docker cp open-webui:/app/backend/open_webui/utils/tools.py /tmp/tools.py

# Edit both files as shown above, then recreate container with volumes:
docker stop open-webui && docker rm open-webui
docker run ... \
  -v /tmp/middleware.py:/app/backend/open_webui/utils/middleware.py:ro \
  -v /tmp/tools.py:/app/backend/open_webui/utils/tools.py:ro \
  ...
```

## Required Env Vars

```
ENABLE_WEB_SEARCH=true
WEB_SEARCH_ENGINE=searxng
SEARXNG_QUERY_URL=http://127.0.0.1:8080/search?q=<query>&format=json
BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL=true
ENABLE_SEARCH_QUERY_GENERATION=true
ENABLE_RAG_LOCAL_WEB_FETCH=true
WEB_SEARCH_RESULT_COUNT=10
WEB_FETCH_MAX_CONTENT_LENGTH=10000
```

## Model Requirements

Models must support OpenAI function/tool calling. Test via:
```python
curl -s -X POST http://127.0.0.1:9292/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{
    "model":"qwen35-9b-mtp",
    "messages":[{"role":"user","content":"what is 2+2"}],
    "tools":[{"type":"function","function":{"name":"test","parameters":{"type":"object","properties":{}}}}],
    "stream":false
  }' | python3 -c "import sys,json; d=json.load(sys.stdin); print('tool_calls' in str(d.get('choices',[{}])[0].get('message',{})))"
```

If `tool_calls` is present in the response, the model supports native FC.

## Verification

1. Ask a normal question: "What is the capital of France?" — should answer without searching
2. Ask an info-needing question: "Search the web for latest AI news" — should trigger search
3. Check logs: `docker logs open-webui 2>&1 | grep search_web`
