mirror of
https://github.com/open-webui/docs
synced 2025-05-19 10:52:14 +00:00
Merge pull request #519 from tth37/external_web_search
Add tutorials for external web search
This commit is contained in:
commit
9399593ef8
132
docs/tutorials/web-search/external.md
Normal file
132
docs/tutorials/web-search/external.md
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 17
|
||||||
|
title: "External"
|
||||||
|
---
|
||||||
|
|
||||||
|
:::warning
|
||||||
|
This tutorial is a community contribution and is not supported by the Open WebUI team. It serves only as a demonstration on how to customize Open WebUI for your specific use case. Want to contribute? Check out the contributing tutorial.
|
||||||
|
:::
|
||||||
|
|
||||||
|
## External Web Search API
|
||||||
|
|
||||||
|
This option allows you to connect Open WebUI to your own self-hosted web search API endpoint. This is useful if you want to:
|
||||||
|
|
||||||
|
* Integrate a search engine not natively supported by Open WebUI.
|
||||||
|
* Implement custom search logic, filtering, or result processing.
|
||||||
|
* Use a private or internal search index.
|
||||||
|
|
||||||
|
### Open WebUI Setup
|
||||||
|
|
||||||
|
1. Navigate to the Open WebUI `Admin Panel`.
|
||||||
|
2. Go to the `Settings` tab and then select `Web Search`.
|
||||||
|
3. Toggle `Enable Web Search` to the on position.
|
||||||
|
4. Set `Web Search Engine` from the dropdown menu to `external`.
|
||||||
|
5. Fill `External Search URL` with the full URL of your custom search API endpoint (e.g., `http://localhost:8000/search` or `https://my-search-api.example.com/api/search`).
|
||||||
|
6. Fill `External Search API Key` with the secret API key required to authenticate with your custom search endpoint. Leave blank if your endpoint doesn't require authentication (not recommended for public endpoints).
|
||||||
|
7. Click `Save`.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### API Specification
|
||||||
|
|
||||||
|
Open WebUI will interact with your `External Search URL` as follows:
|
||||||
|
|
||||||
|
* **Method:** `POST`
|
||||||
|
* **Headers:**
|
||||||
|
* `Content-Type: application/json`
|
||||||
|
* `Authorization: Bearer <YOUR_EXTERNAL_SEARCH_API_KEY>`
|
||||||
|
* **Request Body (JSON):**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"query": "The user's search query string",
|
||||||
|
"count": 5 // The maximum number of search results requested
|
||||||
|
}
|
||||||
|
```
|
||||||
|
* `query` (string): The search term entered by the user.
|
||||||
|
* `count` (integer): The suggested maximum number of results Open WebUI expects. Your API can return fewer results if necessary.
|
||||||
|
|
||||||
|
* **Expected Response Body (JSON):**
|
||||||
|
Your API endpoint *must* return a JSON array of search result objects. Each object should have the following structure:
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"link": "URL of the search result",
|
||||||
|
"title": "Title of the search result page",
|
||||||
|
"snippet": "A brief description or snippet from the search result page"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"link": "...",
|
||||||
|
"title": "...",
|
||||||
|
"snippet": "..."
|
||||||
|
}
|
||||||
|
// ... potentially more results up to the requested count
|
||||||
|
]
|
||||||
|
```
|
||||||
|
* `link` (string): The direct URL to the search result.
|
||||||
|
* `title` (string): The title of the web page.
|
||||||
|
* `snippet` (string): A descriptive text snippet from the page content relevant to the query.
|
||||||
|
|
||||||
|
If an error occurs or no results are found, your endpoint should ideally return an empty JSON array `[]`.
|
||||||
|
|
||||||
|
### Example Implementation (Python/FastAPI)
|
||||||
|
|
||||||
|
Here is a simple example of a self-hosted search API using Python with FastAPI and the `duckduckgo-search` library.
|
||||||
|
|
||||||
|
```python
|
||||||
|
import uvicorn
|
||||||
|
from fastapi import FastAPI, Header, Body, HTTPException
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from duckduckgo_search import DDGS
|
||||||
|
|
||||||
|
EXPECTED_BEARER_TOKEN = "your_secret_token_here"
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
class SearchRequest(BaseModel):
|
||||||
|
query: str
|
||||||
|
count: int
|
||||||
|
|
||||||
|
|
||||||
|
class SearchResult(BaseModel):
|
||||||
|
link: str
|
||||||
|
title: str | None
|
||||||
|
snippet: str | None
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/search")
|
||||||
|
async def external_search(
|
||||||
|
search_request: SearchRequest = Body(...),
|
||||||
|
authorization: str | None = Header(None),
|
||||||
|
):
|
||||||
|
expected_auth_header = f"Bearer {EXPECTED_BEARER_TOKEN}"
|
||||||
|
if authorization != expected_auth_header:
|
||||||
|
raise HTTPException(status_code=401, detail="Unauthorized")
|
||||||
|
|
||||||
|
query, count = search_request.query, search_request.count
|
||||||
|
|
||||||
|
results = []
|
||||||
|
try:
|
||||||
|
with DDGS() as ddgs:
|
||||||
|
search_results = ddgs.text(
|
||||||
|
query, safesearch="moderate", max_results=count, backend="lite"
|
||||||
|
)
|
||||||
|
|
||||||
|
results = [
|
||||||
|
SearchResult(
|
||||||
|
link=result["href"],
|
||||||
|
title=result.get("title"),
|
||||||
|
snippet=result.get("body"),
|
||||||
|
)
|
||||||
|
for result in search_results
|
||||||
|
]
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error during DuckDuckGo search: {e}")
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
uvicorn.run("main:app", host="0.0.0.0", port=8888)
|
||||||
|
```
|
BIN
static/images/tutorial_external_search.png
Normal file
BIN
static/images/tutorial_external_search.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
Loading…
Reference in New Issue
Block a user