返回提交历史
Modified
g4f/Provider/__init__.py
+1
-1
Modified
g4f/Provider/needs_auth/Gemini.py
+3
-1
Deleted
g4f/Provider/needs_auth/GoogleAiMode.py
+0
-595
Modified
g4f/Provider/search/CachedSearch.py
+1
-0
Added
g4f/Provider/search/GoogleAiMode.py
+132
-0
Modified
g4f/Provider/search/GoogleSearch.py
+13
-84
Modified
g4f/Provider/search/__init__.py
+1
-0
Modified
g4f/gui/server/website.py
+29
-0
XFEstudio/gpt4free
Update Google Search
945027b6
代码差异
8 个文件
+180
-681
@@ -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":
@@ -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:
@@ -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
@@ -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`);
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()
@@ -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.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
"""))
@@ -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
@@ -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(() => {{