Coverage for open_webui/retrieval/web/yandex.py: 21%
64 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 05:07 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 05:07 +0000
1import base64
2import io
3import logging
4import os
5from typing import List, Optional
6from xml.etree import ElementTree as ET
7from xml.etree.ElementTree import Element
9import requests
10from fastapi import Request
11from open_webui.env import FORWARD_SESSION_INFO_HEADER_CHAT_ID
12from open_webui.retrieval.web.main import SearchResult, get_filtered_results
13from open_webui.utils.headers import include_user_info_headers
14from open_webui.utils.json_codec import JSONCodec
16log = logging.getLogger(__name__)
19def xml_element_contents_to_string(element: Element) -> str:
20 if element is None:
21 return ''
22 buffer = [element.text if element.text else '']
24 for child in element:
25 buffer.append(xml_element_contents_to_string(child))
27 buffer.append(element.tail if element.tail else '')
29 return ''.join(buffer)
32def search_yandex(
33 request: Request,
34 yandex_search_url: str,
35 yandex_search_api_key: str,
36 yandex_search_config: str,
37 query: str,
38 count: int,
39 filter_list: Optional[List[str]] = None,
40 user=None,
41) -> List[SearchResult]:
42 try:
43 headers = {
44 # LICENSE covers this Open WebUI user-agent identifier.
45 # Do not alter, remove, obscure, or replace it except as LICENSE permits:
46 # https://docs.openwebui.com/license.
47 'User-Agent': 'Open WebUI (https://github.com/open-webui/open-webui) RAG Bot',
48 'Authorization': f'Api-Key {yandex_search_api_key}',
49 }
51 if user is not None:
52 headers = include_user_info_headers(headers, user)
54 chat_id = getattr(request.state, 'chat_id', None)
55 if chat_id:
56 headers[FORWARD_SESSION_INFO_HEADER_CHAT_ID] = str(chat_id)
58 payload = {} if yandex_search_config == '' else JSONCodec.loads(yandex_search_config)
60 if type(payload.get('query', None)) != dict:
61 payload['query'] = {}
63 if 'searchType' not in payload['query']:
64 payload['query']['searchType'] = 'SEARCH_TYPE_RU'
66 payload['query']['queryText'] = query
68 if type(payload.get('groupSpec', None)) != dict:
69 payload['groupSpec'] = {}
71 if 'groupMode' not in payload['groupSpec']:
72 payload['groupSpec']['groupMode'] = 'GROUP_MODE_DEEP'
74 payload['groupSpec']['groupsOnPage'] = count
75 payload['groupSpec']['docsInGroup'] = 1
77 response = requests.post(
78 ('https://searchapi.api.cloud.yandex.net/v2/web/search' if yandex_search_url == '' else yandex_search_url),
79 headers=headers,
80 json=payload,
81 )
83 response.raise_for_status()
85 response_body = response.json()
86 if 'rawData' not in response_body:
87 raise Exception(f'No `rawData` in response body: {response_body}')
89 search_result_body_bytes = base64.decodebytes(bytes(response_body['rawData'], 'utf-8'))
91 doc_root = ET.parse(io.BytesIO(search_result_body_bytes))
93 results = []
95 for group in doc_root.findall('response/results/grouping/group'):
96 results.append(
97 {
98 'url': xml_element_contents_to_string(group.find('doc/url')).strip('\n'),
99 'title': xml_element_contents_to_string(group.find('doc/title')).strip('\n'),
100 'snippet': xml_element_contents_to_string(group.find('doc/passages/passage')),
101 }
102 )
104 results = get_filtered_results(results, filter_list)
106 results = [
107 SearchResult(
108 link=result.get('url'),
109 title=result.get('title'),
110 snippet=result.get('snippet'),
111 )
112 for result in results[:count]
113 ]
115 log.info('Yandex search results: %s', results)
117 return results
118 except Exception as e:
119 log.error(f'Error in search: {e}')
121 return []
124if __name__ == '__main__': 124 ↛ 125line 124 didn't jump to line 125 because the condition on line 124 was never true
125 from fastapi import FastAPI
126 from starlette.datastructures import Headers
128 result = search_yandex(
129 Request(
130 {
131 'type': 'http',
132 'asgi.version': '3.0',
133 'asgi.spec_version': '2.0',
134 'method': 'GET',
135 'path': '/internal',
136 'query_string': b'',
137 'headers': Headers({}).raw,
138 'client': ('127.0.0.1', 12345),
139 'server': ('127.0.0.1', 80),
140 'scheme': 'http',
141 'app': FastAPI(),
142 },
143 None,
144 ),
145 os.environ.get('YANDEX_WEB_SEARCH_URL', ''),
146 os.environ.get('YANDEX_WEB_SEARCH_API_KEY', ''),
147 os.environ.get('YANDEX_WEB_SEARCH_CONFIG', '{"query": {"searchType": "SEARCH_TYPE_COM"}}'),
148 'TOP movies of the past year',
149 3,
150 )
152 print(result)