返回提交历史
Modified
g4f/Provider/search/GoogleSearch.py
+72
-6
Modified
g4f/mcp/pa_downloader.py
+1
-1
Modified
g4f/requests/cdp.py
+14
-10
XFEstudio/gpt4free
Add AI Model to Google Search
fd950a00
代码差异
3 个文件
+87
-17
@@ -16,8 +16,10 @@ class GoogleSearch(AsyncGeneratorProvider, ProviderModelMixin):
16
16
label = "Google Search"
17
17
url = "https://google.com"
18
18
working = True
19
active_by_default = True
19
20
supports_native_tools = True
20
21
default_model = "search"
22
models = [default_model, "ai-mode"]
21
23
22
24
@classmethod
23
25
async def create_async_generator(
@@ -35,11 +37,77 @@ class GoogleSearch(AsyncGeneratorProvider, ProviderModelMixin):
35
37
36
38
try:
37
39
await session.navigate(search_url)
38
39
40
await session.click_accept_button()
41
except Exception as e:
42
await session.close()
43
raise e
44
45
# Enable AI mode if the model is like "ai"
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")).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).filter(c=>!c.startsWith('TgQPHd|'));
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`;
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.")
40
108
41
109
# Wait for Google search results page to load
42
for _ in range(30):
110
for _ in range(10):
43
111
try:
44
112
has_results = await session.evaluate_js(
45
113
"document.querySelectorAll('div.g, h3').length > 0"
@@ -51,7 +119,7 @@ class GoogleSearch(AsyncGeneratorProvider, ProviderModelMixin):
51
119
await asyncio.sleep(1)
52
120
53
121
# Extract search results from the DOM
54
results_json = await session.evaluate_js(
122
results = await session.evaluate_js(
55
123
"""
56
124
(() => {
57
125
const results = [];
@@ -74,13 +142,11 @@ class GoogleSearch(AsyncGeneratorProvider, ProviderModelMixin):
74
142
results.push({ title, link: link.toString(), snippet });
75
143
}
76
144
});
77
return JSON.stringify(results);
145
return results;
78
146
})()
79
147
"""
80
148
)
81
149
82
results = json.loads(results_json) if results_json else []
83
84
150
if not results:
85
151
raise RuntimeError("No search results found.")
86
152
yield SearchResults(results)
@@ -378,7 +378,7 @@ def auto_download_pa_providers(
378
378
return []
379
379
380
380
if written:
381
print(f"pa-providers: auto-downloaded {len(written)} provider(s) from {repo}")
381
pass
382
382
else:
383
383
debug.log("pa-providers: auto-download found nothing new to install")
384
384
return written
@@ -783,6 +783,13 @@ class CDPSession:
783
783
784
784
async def capture_screenshot(self, url: str, n: int = 3) -> AsyncIterator[str]:
785
785
"""Navigate to a URL and capture a screenshot, caching the result."""
786
url_without_suffix = url[:-6] if url.endswith("_2.jpg") or url.endswith("_3.jpg") else url
787
url_with_noads = f"{url_without_suffix}&noads={int(time.time())}" if "?" in url_without_suffix else f"{url_without_suffix}?noads={int(time.time())}"
788
await self.navigate(url_with_noads)
789
790
if await self.evaluate_js('!document.doctype'):
791
raise RuntimeError(f"Failed to load page {url} for screenshot, document.doctype={await self.evaluate_js('String(document.doctype)')}")
792
786
793
result = None
787
794
for i in range(n):
788
795
await asyncio.sleep(1)
@@ -803,20 +810,17 @@ class CDPSession:
803
810
if os.path.exists(filepath):
804
811
debug.log(f"Screenshot already exists: {filepath}")
805
812
return filepath
806
url_with_noads = f"{url_without_suffix}&noads={int(time.time())}" if "?" in url_without_suffix else f"{url_without_suffix}?noads={int(time.time())}"
807
await self.navigate(url_with_noads)
808
813
# Wait for network activity to settle before capturing
809
814
await self.wait_for_network_idle(idle_time=5, timeout=15.0)
810
if await self.evaluate_js('!document.doctype'):
811
raise RuntimeError(f"Failed to load page {url} for screenshot, doctype={await self.evaluate_js('String(document.doctype)')}")
812
815
# Try to click any "Accept" or "Einwilligen" cookie consent buttons
813
for _ in range(5):
814
debug.log("Attempting to click accept button...")
815
await asyncio.sleep(1)
816
if await self.click_accept_button():
817
debug.log("Clicked accept button.")
816
if n != 1:
817
for _ in range(2):
818
debug.log("Attempting to click accept button...")
818
819
await asyncio.sleep(1)
819
break
820
if await self.click_accept_button():
821
debug.log("Clicked accept button.")
822
await asyncio.sleep(1)
823
break
820
824
await self.wait_for_network_idle(idle_time=5, timeout=15.0)
821
825
result = await self.call("Page.captureScreenshot")
822
826
image_bytes = base64.b64decode(result["data"])