| 
									
										
										
										
											2025-02-27 16:12:41 +08:00
										 |  |  | import logging | 
					
						
							| 
									
										
										
										
											2025-06-03 06:19:08 +08:00
										 |  |  | from typing import Optional, Literal | 
					
						
							| 
									
										
										
										
											2025-02-27 16:12:41 +08:00
										 |  |  | import requests | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from open_webui.retrieval.web.main import SearchResult, get_filtered_results | 
					
						
							|  |  |  | from open_webui.env import SRC_LOG_LEVELS | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-03 06:19:08 +08:00
										 |  |  | MODELS = Literal[ | 
					
						
							|  |  |  |     "sonar", | 
					
						
							|  |  |  |     "sonar-pro", | 
					
						
							|  |  |  |     "sonar-reasoning", | 
					
						
							|  |  |  |     "sonar-reasoning-pro", | 
					
						
							|  |  |  |     "sonar-deep-research", | 
					
						
							|  |  |  | ] | 
					
						
							|  |  |  | SEARCH_CONTEXT_USAGE_LEVELS = Literal["low", "medium", "high"] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-27 16:12:41 +08:00
										 |  |  | log = logging.getLogger(__name__) | 
					
						
							|  |  |  | log.setLevel(SRC_LOG_LEVELS["RAG"]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def search_perplexity( | 
					
						
							|  |  |  |     api_key: str, | 
					
						
							|  |  |  |     query: str, | 
					
						
							|  |  |  |     count: int, | 
					
						
							|  |  |  |     filter_list: Optional[list[str]] = None, | 
					
						
							| 
									
										
										
										
											2025-06-03 06:19:08 +08:00
										 |  |  |     model: MODELS = "sonar", | 
					
						
							|  |  |  |     search_context_usage: SEARCH_CONTEXT_USAGE_LEVELS = "medium", | 
					
						
							| 
									
										
										
										
											2025-02-27 16:12:41 +08:00
										 |  |  | ) -> 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 | 
					
						
							| 
									
										
										
										
											2025-06-03 06:19:08 +08:00
										 |  |  |       filter_list (Optional[list[str]]): List of domains to filter results | 
					
						
							|  |  |  |       model (str): The Perplexity model to use (sonar, sonar-pro) | 
					
						
							|  |  |  |       search_context_usage (str): Search context usage level (low, medium, high) | 
					
						
							| 
									
										
										
										
											2025-02-27 16:12:41 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Handle PersistentConfig object | 
					
						
							|  |  |  |     if hasattr(api_key, "__str__"): | 
					
						
							|  |  |  |         api_key = str(api_key) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         url = "https://api.perplexity.ai/chat/completions" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Create payload for the API call | 
					
						
							|  |  |  |         payload = { | 
					
						
							| 
									
										
										
										
											2025-06-03 06:19:08 +08:00
										 |  |  |             "model": model, | 
					
						
							| 
									
										
										
										
											2025-02-27 16:12:41 +08:00
										 |  |  |             "messages": [ | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     "role": "system", | 
					
						
							|  |  |  |                     "content": "You are a search assistant. Provide factual information with citations.", | 
					
						
							|  |  |  |                 }, | 
					
						
							|  |  |  |                 {"role": "user", "content": query}, | 
					
						
							|  |  |  |             ], | 
					
						
							|  |  |  |             "temperature": 0.2,  # Lower temperature for more factual responses | 
					
						
							|  |  |  |             "stream": False, | 
					
						
							| 
									
										
										
										
											2025-06-03 06:19:08 +08:00
										 |  |  |             "web_search_options": { | 
					
						
							|  |  |  |                 "search_context_usage": search_context_usage, | 
					
						
							| 
									
										
										
										
											2025-06-03 06:27:07 +08:00
										 |  |  |             }, | 
					
						
							| 
									
										
										
										
											2025-02-27 16:12:41 +08:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         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 | 
					
						
							|  |  |  |         citations = json_response.get("citations", []) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Create search results from citations | 
					
						
							|  |  |  |         results = [] | 
					
						
							|  |  |  |         for i, citation in enumerate(citations[:count]): | 
					
						
							|  |  |  |             # Extract content from the response to use as snippet | 
					
						
							|  |  |  |             content = "" | 
					
						
							|  |  |  |             if "choices" in json_response and json_response["choices"]: | 
					
						
							|  |  |  |                 if i == 0: | 
					
						
							|  |  |  |                     content = json_response["choices"][0]["message"]["content"] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             result = {"link": citation, "title": f"Source {i+1}", "snippet": content} | 
					
						
							|  |  |  |             results.append(result) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if filter_list: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             results = get_filtered_results(results, filter_list) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return [ | 
					
						
							|  |  |  |             SearchResult( | 
					
						
							|  |  |  |                 link=result["link"], title=result["title"], snippet=result["snippet"] | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |             for result in results[:count] | 
					
						
							|  |  |  |         ] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     except Exception as e: | 
					
						
							|  |  |  |         log.error(f"Error searching with Perplexity API: {e}") | 
					
						
							|  |  |  |         return [] |