XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet
公开
关注 0 Fork 0 Star 1
返回提交历史

XFEstudio/gpt4free

Update Google Search

945027b6
hlohaus <983577+hlohaus@users.noreply.github.com>
提交于

代码差异

8 个文件 +180 -681
Modified g4f/Provider/__init__.py +1 -1
@@ -258,7 +258,7 @@ class ProviderLoader:
258 258
259 259 return GlhfChat
260 260 elif name == "GoogleAiMode":
261 from g4f.Provider.needs_auth.GoogleAiMode import GoogleAiMode
261 from g4f.Provider.search.GoogleAiMode import GoogleAiMode
262 262
263 263 return GoogleAiMode
264 264 elif name == "GoogleSearch":
Modified g4f/Provider/needs_auth/Gemini.py +3 -1
@@ -578,7 +578,7 @@ class Gemini(AsyncGeneratorProvider, ProviderModelMixin):
578 578 except (ClientError, ResponseError, ValueError) as error:
579 579 cls._account_models_fetched_at = time.time()
580 580 debug.log(f"Gemini model discovery failed: {error}")
581 return {key: value for key, value in cls._account_models.items() if value.get("available")}
581 return {key: value for key, value in cls._account_models.items()}
582 582
583 583 @classmethod
584 584 async def create_async_generator(
@@ -768,6 +768,7 @@ class Gemini(AsyncGeneratorProvider, ProviderModelMixin):
768 768 response = None
769 769 for attempt in range(max_retries + 1):
770 770 try:
771 print(f"Attempt {attempt + 1} for Gemini request to model {model}")
771 772 response = await session.post(
772 773 f"{cls.url}{prefix}{REQUEST_PATH}",
773 774 data=data,
@@ -837,6 +838,7 @@ class Gemini(AsyncGeneratorProvider, ProviderModelMixin):
837 838 response.content, stream_timeout
838 839 ):
839 840 line_text = line_text.strip()
841 print(f"Received line: {line_text}")
840 842 error_buffer = (error_buffer + line_text)[-4096:]
841 843 error_match = BARD_ERROR_PATTERN.search(error_buffer)
842 844 if error_match:
Modified g4f/Provider/search/CachedSearch.py +1 -0
@@ -67,6 +67,7 @@ async def search(
67 67
68 68
69 69 class CachedSearch(AsyncGeneratorProvider, AuthFileMixin):
70 url = "https://g4f.dev"
70 71 working = True
71 72
72 73 @classmethod
Added g4f/Provider/search/GoogleAiMode.py +132 -0
@@ -0,0 +1,132 @@
1 from __future__ import annotations
2
3 import asyncio
4 import urllib.parse
5
6 from ...typing import AsyncResult, Messages
7 from ...requests.cdp import CDPSession
8 from ... import debug
9 from ..helper import get_last_user_message
10 from .GoogleSearch import GoogleSearch
11
12
13 class GoogleAiMode(GoogleSearch):
14 label = "Google AI Mode"
15 url = "https://google.com"
16 working = True
17 active_by_default = True
18 supports_native_tools = True
19 default_model = "ai-mode"
20 models = [default_model]
21
22 @classmethod
23 async def create_async_generator(
24 cls,
25 model: str,
26 messages: Messages,
27 **kwargs,
28 ) -> AsyncResult:
29 query = get_last_user_message(messages)
30 search_url = f"{cls.url}/search?q={urllib.parse.quote_plus(query)}"
31
32 debug.log(f"Google Search: Starting CDPSession for query: {query}")
33 session = CDPSession(headless=False)
34 await session.start()
35
36 try:
37 await session.navigate(search_url)
38 await session.click_accept_button()
39 except Exception as e:
40 await session.close()
41 raise e
42
43 try:
44 await session.wait_for_network_idle(idle_time=1, timeout=10.0)
45 search_results = await cls._read_search_results(session)
46 if search_results:
47 yield search_results
48 yield "\n\n---\n\n"
49 except Exception as e:
50 debug.log(f"Google Search: Error reading search results: {e}")
51 await session.close()
52 raise e
53
54 # Enable AI mode if model is ai-mode
55 try:
56 for _ in range(10):
57 result = await session.evaluate_js("""const b =Array.from(document.querySelectorAll("a, button")).filter(a=>a.textContent.endsWith("KI‑Modus") || a.textContent.endsWith("AI-Mode")).pop(); b ? b.click() : null; !!b""")
58 debug.log(f"Google Search: Attempted #{_+1} to enable AI mode, result: {result}")
59 await asyncio.sleep(1)
60 if not result:
61 continue
62 await session.wait_for_network_idle(idle_time=1, timeout=10.0)
63 results = await session.call("Runtime.evaluate", expression=r"""const cyrb53 = (str, seed = 0) => {
64 let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed;
65 for(let i = 0, ch; i < str.length; i++) {
66 ch = str.charCodeAt(i);
67 h1 = Math.imul(h1 ^ ch, 2654435761);
68 h2 = Math.imul(h2 ^ ch, 1597334677);
69 }
70 h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507);
71 h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);
72 h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507);
73 h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909);
74
75 return 4294967296 * (2097151 & h2) + (h1 >>> 0);
76 };
77
78 const result = [];
79 const rootElement = document.querySelector('[decode-data-ved="1"]');
80
81 if (rootElement) {
82 for (const nodes of Array.from(rootElement.querySelectorAll('*')).map(e => Array.from(e.childNodes))) {
83 for (const node of nodes) {
84 result.push(node);
85 }
86 }
87 }
88
89 const lines = result.map(n => n.textContent ? n.textContent.trim() : "");
90 const keepLines = { };
91
92 for (let l of lines) {
93 if (!l) continue;
94 if (l.startsWith('TgQPHd')) continue;
95 const find = l.indexOf("KI-Antworten können Fehler enthalten.");
96 if (find !== -1) {
97 l = l.substring(0, find).trim();
98 if (l) keepLines[cyrb53(l)] = l;
99 break;
100 }
101
102 // Regex für das Finden und Extrahieren von _setImageSrc('ID', 'BASE64')
103 // Verwendet einfache Anführungszeichen als Begrenzer (wie von Google ausgegeben)
104 const imgMatch = l.match(/_setImageSrc\s*\(\s*'([^']+)'\s*,\s*'([^']+)'\s*\)/);
105
106 if (imgMatch) {
107 // imgMatch[2] ist der Base64-String (data:image/png;base64,...)
108 // Dekodiere Hex-Escapes wie \x3d (=) am Ende des Base64-Strings
109 const base64Data = imgMatch[2].replace(/\\x([0-9a-fA-F]{2})/g, (_, h) => String.fromCharCode(parseInt(h, 16)));
110 // Ersetze nur den _setImageSrc-Aufruf, behalte umgebenden Text
111 l = l.replace(imgMatch[0], `\n![Bild](${base64Data})`);
112 }
113
114 // Behebt den fehlerhaften Regex für die Datei-Metadaten am Zeilenende
115 const fileMatch = l.match(/\\\{.+\\}/);
116 if (fileMatch) {
117 l = l.replace(fileMatch[0], '');
118 }
119
120 const hash = cyrb53(l);
121 keepLines[hash] = l;
122 }
123 Object.values(keepLines);
124 """, returnByValue=True);
125 debug.log(f"Google Search: AI mode results: {results}")
126 if results:
127 for text in results.get("result", {}).get("value", []):
128 yield f"{text}\n"
129 return
130 raise RuntimeError("No AI mode results found.")
131 finally:
132 await session.close()
Modified g4f/Provider/search/GoogleSearch.py +13 -84
@@ -1,7 +1,6 @@
1 1 from __future__ import annotations
2 2
3 3 import asyncio
4 import json
5 4 import urllib.parse
6 5
7 6 from ...typing import AsyncResult, Messages
@@ -19,7 +18,7 @@ class GoogleSearch(AsyncGeneratorProvider, ProviderModelMixin):
19 18 active_by_default = True
20 19 supports_native_tools = True
21 20 default_model = "search"
22 models = [default_model, "ai-mode"]
21 models = [default_model]
23 22
24 23 @classmethod
25 24 async def create_async_generator(
@@ -38,73 +37,6 @@ class GoogleSearch(AsyncGeneratorProvider, ProviderModelMixin):
38 37 try:
39 38 await session.navigate(search_url)
40 39 await session.click_accept_button()
41 except Exception as e:
42 await session.close()
43 raise e
44
45 # Enable AI mode if model is ai-mode
46 try:
47 if model == "ai-mode":
48 await session.wait_for_network_idle(idle_time=1, timeout=10.0)
49 for _ in range(10):
50 result = await session.evaluate_js("""const b =Array.from(document.querySelectorAll("a, button")).filter(a=>a.textContent.endsWith("KI‑Modus") || a.textContent.endsWith("AI-Mode")).pop(); b ? b.click() : null; !!b""")
51 debug.log(f"Google Search: Attempted #{_+1} to enable AI mode, result: {result}")
52 await asyncio.sleep(1)
53 if not result:
54 continue
55 await session.wait_for_network_idle(idle_time=1, timeout=10.0)
56 results = await session.call("Runtime.evaluate", expression="""
57 const cyrb53 = (str, seed = 0) => {
58 let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed;
59 for(let i = 0, ch; i < str.length; i++) {
60 ch = str.charCodeAt(i);
61 h1 = Math.imul(h1 ^ ch, 2654435761);
62 h2 = Math.imul(h2 ^ ch, 1597334677);
63 }
64 h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507);
65 h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);
66 h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507);
67 h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909);
68
69 return 4294967296 * (2097151 & h2) + (h1 >>> 0);
70 };
71
72 const result = [];
73 for (const nodes of Array.from(document.querySelector('[decode-data-ved="1"]').querySelectorAll('*')).map(e=>Array.from(e.childNodes))) {
74 for (const node of nodes) {result.push(node)}
75 }
76
77 const lines = result.map(n=>n.textContent);
78 const keepLines = { };
79
80 for (let l of lines) {
81 if (l.startsWith('TgQPHd')) continue;
82 if (l.startsWith("KI-Antworten können Fehler enthalten.")) break;
83 if (!l) continue;
84
85 // Sucht nach n._setImageSrc('ID', 'BASE64_DATEN') und extrahiert die Bilddaten
86 const imgMatch = l.match(/_setImageSrc\\([^,]+,\\s*'([^']+)'\\)/);
87 if (imgMatch) {
88 // Bereinigt eventuelle doppelte Backslashes aus dem Daten-String (z.B. data:image\\/png)
89 l = `\\n![](${(imgMatch[1]).replaceAll('\\', '')})`;
90 }
91 const fileMath = l.match(/\\[\\{.+\\]^/);
92 if (fileMath) {
93 l = l.replace(fileMath[0], '');
94 }
95
96 const hash = cyrb53(l);
97 keepLines[hash] = l;
98 }
99 Object.values(keepLines);
100 """, returnByValue=True);
101 debug.log(f"Google Search: AI mode results: {results}")
102 if results:
103 for text in results.get("result", {}).get("value", []):
104 yield f"{text}\n"
105 return
106 if model == "ai-mode":
107 raise RuntimeError("No AI mode results found.")
108 40
109 41 # Wait for Google search results page to load
110 42 for _ in range(10):
@@ -112,15 +44,19 @@ Object.values(keepLines);
112 44 has_results = await session.evaluate_js(
113 45 "document.querySelectorAll('div.g, h3').length > 0"
114 46 )
115 if has_results:
116 break
117 except Exception:
118 pass
47 except Exception as e:
48 debug.log(f"Google Search: Error checking for results: {e}")
49 if has_results:
50 break
119 51 await asyncio.sleep(1)
120 52
121 # Extract search results from the DOM
122 results = await session.evaluate_js(
123 """
53 # Extract search results from the DOM
54 yield await cls._read_search_results(session)
55 finally:
56 await session.close()
57
58 async def _read_search_results(session: CDPSession) -> SearchResults:
59 return SearchResults(await session.evaluate_js("""
124 60 (() => {
125 61 const results = [];
126 62 const items = document.querySelectorAll('h3');
@@ -144,11 +80,4 @@ Object.values(keepLines);
144 80 });
145 81 return results;
146 82 })()
147 """
148 )
149
150 if not results:
151 raise RuntimeError("No search results found.")
152 yield SearchResults(results)
153 finally:
154 await session.close()
83 """))
Modified g4f/Provider/search/__init__.py +1 -0
@@ -1,4 +1,5 @@
1 1 from .CachedSearch import CachedSearch
2 from .GoogleAiMode import GoogleAiMode
2 3 from .GoogleSearch import GoogleSearch
3 4 from .SearXNG import SearXNG
4 5 from .YouTube import YouTube
Modified g4f/gui/server/website.py +29 -0
@@ -330,10 +330,39 @@ class Website:
330 330 <p class="screenshot-caption">Load screenshot from {escape(p['url'] or 'N/A')}</p>
331 331 </div>
332 332 <script>
333 // Get provider logo (same as docs page)
334 function getImage(key) {{
335 key = key || "";
336 const img = new Image(200, 200);
337 const gen = 'https://g4f.space';
338 img.src = gen + '/logo/' +
339 key.replace(
340 'MetaAIAccount', 'Facebook AI').replace(
341 'MetaAI', 'Facebook AI').replace(
342 'Aria', '').replace(
343 'OpenAI', 'ChatGPT').replace(
344 'Video', 'TV').replace(
345 'Phi-4', 'Windows').replace(
346 '(Text Generation)', '').replace(
347 'Glhf', 'AI').replace(
348 'GithubCopilot', 'GitHub Copilot').replace(
349 'PerplexityApi', 'Perplexity API').replace(
350 'Gemini', '').replace(
351 'API', '').replace(
352 '-2.5M', '').replace(
353 'grok', 'xAI').replace(
354 'Qwen_Qwen_3', 'Qwen').replace(
355 'Yupp', 'with yupp').replace(
356 'groq', 'Groq');
357 return img;
358 }}
333 359 const img = document.querySelector('img[data-src="{logo_url}"]');
360 const previewImg = getImage("{p['name']}")
361 img.parentElement.appendChild(previewImg);
334 362 let n = 1;
335 363 const orgSrc = img.dataset.src;
336 364 img.onload = () => {{
365 img.parentElement.removeChild(previewImg);
337 366 n = n + 1;
338 367 if (n <= 3) {{
339 368 setTimeout(() => {{