diff --git a/backend/open_webui/retrieval/web/perplexity_search.py b/backend/open_webui/retrieval/web/perplexity_search.py new file mode 100644 index 0000000000..e3e0caa2b3 --- /dev/null +++ b/backend/open_webui/retrieval/web/perplexity_search.py @@ -0,0 +1,64 @@ +import logging +from typing import Optional, Literal +import requests + +from open_webui.retrieval.web.main import SearchResult, get_filtered_results +from open_webui.env import SRC_LOG_LEVELS + + +log = logging.getLogger(__name__) +log.setLevel(SRC_LOG_LEVELS["RAG"]) + + +def search_perplexity_search( + api_key: str, + query: str, + count: int, + filter_list: Optional[list[str]] = None, +) -> list[SearchResult]: + """Search using Perplexity API and return the results as a list of SearchResult objects. + + Args: + api_key (str): A Perplexity API key + query (str): The query to search for + count (int): Maximum number of results to return + filter_list (Optional[list[str]]): List of domains to filter results + + """ + + # Handle PersistentConfig object + if hasattr(api_key, "__str__"): + api_key = str(api_key) + + try: + url = "https://api.perplexity.ai/search" + + # Create payload for the API call + payload = { + "query": query, + "max_results": count, + } + + headers = { + "Authorization": f"Bearer {api_key}", + "Content-Type": "application/json", + } + + # Make the API request + response = requests.request("POST", url, json=payload, headers=headers) + # Parse the JSON response + json_response = response.json() + + # Extract citations from the response + results = json_response.get("results", []) + + return [ + SearchResult( + link=result["url"], title=result["title"], snippet=result["snippet"] + ) + for result in results + ] + + except Exception as e: + log.error(f"Error searching with Perplexity Search API: {e}") + return [] diff --git a/backend/open_webui/routers/retrieval.py b/backend/open_webui/routers/retrieval.py index 73b3a22725..3681008c87 100644 --- a/backend/open_webui/routers/retrieval.py +++ b/backend/open_webui/routers/retrieval.py @@ -46,6 +46,7 @@ from open_webui.retrieval.loaders.youtube import YoutubeLoader from open_webui.retrieval.web.main import SearchResult from open_webui.retrieval.web.utils import get_web_loader from open_webui.retrieval.web.ollama import search_ollama_cloud +from open_webui.retrieval.web.perplexity_search import search_perplexity_search from open_webui.retrieval.web.brave import search_brave from open_webui.retrieval.web.kagi import search_kagi from open_webui.retrieval.web.mojeek import search_mojeek @@ -1801,6 +1802,16 @@ def search_web(request: Request, engine: str, query: str) -> list[SearchResult]: request.app.state.config.WEB_SEARCH_RESULT_COUNT, request.app.state.config.WEB_SEARCH_DOMAIN_FILTER_LIST, ) + elif engine == "perplexity_search": + if request.app.state.config.PERPLEXITY_API_KEY: + return search_perplexity_search( + request.app.state.config.PERPLEXITY_API_KEY, + query, + request.app.state.config.WEB_SEARCH_RESULT_COUNT, + request.app.state.config.WEB_SEARCH_DOMAIN_FILTER_LIST, + ) + else: + raise Exception("No PERPLEXITY_API_KEY found in environment variables") elif engine == "searxng": if request.app.state.config.SEARXNG_QUERY_URL: return search_searxng( diff --git a/src/lib/components/admin/Settings/WebSearch.svelte b/src/lib/components/admin/Settings/WebSearch.svelte index 622aaf4a08..7a78f275da 100644 --- a/src/lib/components/admin/Settings/WebSearch.svelte +++ b/src/lib/components/admin/Settings/WebSearch.svelte @@ -14,6 +14,7 @@ let webSearchEngines = [ 'ollama_cloud', + 'perplexity_search', 'searxng', 'yacy', 'google_pse', @@ -148,6 +149,23 @@ + {:else if webConfig.WEB_SEARCH_ENGINE === 'perplexity_search'} +