summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH Lohaus <hlohaus@users.noreply.github.com>2023-12-07 07:18:05 +0100
committerGitHub <noreply@github.com>2023-12-07 07:18:05 +0100
commit484b96d850aca9b9144f3b8dd2fb502b25356c22 (patch)
treeb10fe200193cfdbe0ee25714df49959c01c6265f
parentUpdate Pi.py (diff)
downloadgpt4free-484b96d850aca9b9144f3b8dd2fb502b25356c22.tar
gpt4free-484b96d850aca9b9144f3b8dd2fb502b25356c22.tar.gz
gpt4free-484b96d850aca9b9144f3b8dd2fb502b25356c22.tar.bz2
gpt4free-484b96d850aca9b9144f3b8dd2fb502b25356c22.tar.lz
gpt4free-484b96d850aca9b9144f3b8dd2fb502b25356c22.tar.xz
gpt4free-484b96d850aca9b9144f3b8dd2fb502b25356c22.tar.zst
gpt4free-484b96d850aca9b9144f3b8dd2fb502b25356c22.zip
-rw-r--r--.github/workflows/publish-workflow.yaml17
-rw-r--r--README.md8
-rw-r--r--docker-compose.yml6
-rw-r--r--docker/Dockerfile4
-rw-r--r--docker/background.png (renamed from g4f.png)bin155456 -> 155456 bytes
-rwxr-xr-xdocker/supervisor.conf2
-rw-r--r--g4f/Provider/MyShell.py18
-rw-r--r--g4f/Provider/unfinished/AiChatting.py66
-rw-r--r--g4f/Provider/unfinished/__init__.py3
-rw-r--r--g4f/__init__.py57
-rw-r--r--g4f/debug.py3
-rw-r--r--g4f/gui/client/css/style.css3
-rw-r--r--g4f/gui/client/html/index.html4
-rw-r--r--g4f/gui/client/js/chat.v1.js15
-rw-r--r--g4f/gui/server/backend.py24
-rw-r--r--g4f/gui/server/internet.py187
-rw-r--r--g4f/requests.py28
-rw-r--r--g4f/webdriver.py48
-rw-r--r--requirements.txt3
19 files changed, 360 insertions, 136 deletions
diff --git a/.github/workflows/publish-workflow.yaml b/.github/workflows/publish-workflow.yaml
index 634aef53..634a5b31 100644
--- a/.github/workflows/publish-workflow.yaml
+++ b/.github/workflows/publish-workflow.yaml
@@ -9,21 +9,30 @@ jobs:
publish:
runs-on: ubuntu-latest
steps:
- - name: Setup Buildx
- uses: docker/setup-buildx-action@v3
- name: Checkout repository
uses: actions/checkout@v4
+
- name: Get metadata for Docker
id: metadata
uses: docker/metadata-action@v5
with:
- images: ghcr.io/${{ github.repository }}
+ images: |
+ hlohaus789/g4f
+ ghcr.io/${{ github.repository }}
+
+ - name: Log in to Docker Hub
+ uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a
+ with:
+ username: ${{ secrets.DOCKER_USERNAME }}
+ password: ${{ secrets.DOCKER_PASSWORD }}
+
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GHCR_PAT }}
+
- name: Build and push image
uses: docker/build-push-action@v5
with:
@@ -32,3 +41,5 @@ jobs:
push: true
tags: ${{ steps.metadata.outputs.tags }}
labels: ${{ steps.metadata.outputs.labels }}
+ build-args: |
+ G4F_VERSION=${{ github.ref_name }}
diff --git a/README.md b/README.md
index 7641fd25..5d0d94d8 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-![g4f](g4f.png)
+![248433934-7886223b-c1d1-4260-82aa-da5741f303bb](https://github.com/xtekky/gpt4free/assets/98614666/ea012c87-76e0-496a-8ac4-e2de090cc6c9)
<a href='https://ko-fi.com/xtekky' target='_blank'><img height='35' style='border:0px;height:46px;' src='https://az743702.vo.msecnd.net/cdn/kofi3.png?v=0' border='0' alt='Buy Me a Coffee at ko-fi.com' />
<div id="top"></div>
@@ -6,7 +6,8 @@
> By using this repository or any code related to it, you agree to the [legal notice](LEGAL_NOTICE.md). The author is not responsible for any copies, forks, re-uploads made by other users, or anything else related to GPT4Free. This is the author's only account and repository. To prevent impersonation or irresponsible actions, please comply with the GNU GPL license this Repository uses.
> [!Note]
-> Latest pypi version: [`0.1.9.2`](https://pypi.org/project/g4f/0.1.9.2)
+Lastet version:
+>> [![PyPI version](https://badge.fury.io/py/g4f.svg)](https://pypi.org/project/g4f)
```sh
pip install -U g4f
```
@@ -165,8 +166,7 @@ docker-compose down
import g4f
g4f.debug.logging = True # Enable logging
-g4f.check_version = False # Disable automatic version checking
-print(g4f.version) # Check version
+g4f.debug.check_version = False # Disable automatic version checking
print(g4f.Provider.Ails.params) # Supported args
# Automatic selection of provider
diff --git a/docker-compose.yml b/docker-compose.yml
index 8dfe1f35..326a3640 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -2,13 +2,13 @@ version: '3'
services:
gpt4free:
- image: ghcr.io/xtekky/gpt4free:latest
+ image: hlohaus789/g4f:latest
shm_size: 2gb
build:
context: .
dockerfile: docker/Dockerfile
- volumes:
- - .:/app
+# volumes:
+# - .:/app
ports:
- '8080:80'
- '1337:1337'
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 905efc4b..66f7fd3c 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -3,6 +3,8 @@ FROM selenium/node-chrome
ENV SE_SCREEN_WIDTH 1850
ENV G4F_LOGIN_URL http://localhost:7900/?autoconnect=1&resize=scale&password=secret
ENV PYTHONUNBUFFERED 1
+ARG G4F_VERSION
+ENV G4F_VERSION ${G4F_VERSION}
USER root
@@ -22,7 +24,7 @@ RUN rm -rf /var/lib/apt/lists/* /var/cache/apt/* \
COPY docker/supervisor.conf /etc/supervisor/conf.d/selenium.conf
# Change background image
-COPY g4f.png /usr/share/images/fluxbox/ubuntu-light.png
+COPY docker/background.png /usr/share/images/fluxbox/ubuntu-light.png
# Switch user
USER 1200
diff --git a/g4f.png b/docker/background.png
index 41bf9e6b..41bf9e6b 100644
--- a/g4f.png
+++ b/docker/background.png
Binary files differ
diff --git a/docker/supervisor.conf b/docker/supervisor.conf
index 6d82f6c9..e76f9a6c 100755
--- a/docker/supervisor.conf
+++ b/docker/supervisor.conf
@@ -49,7 +49,7 @@ stderr_logfile_backups=5
stdout_capture_maxbytes=50MB
stderr_capture_maxbytes=50MB
-[program:g4f-cli]
+[program:g4f-api]
priority=15
command=python -m g4f.cli api
directory=/app
diff --git a/g4f/Provider/MyShell.py b/g4f/Provider/MyShell.py
index 2ee94bb6..b0a01016 100644
--- a/g4f/Provider/MyShell.py
+++ b/g4f/Provider/MyShell.py
@@ -5,7 +5,7 @@ import time, json
from ..typing import CreateResult, Messages
from .base_provider import BaseProvider
from .helper import format_prompt
-from ..webdriver import WebDriver, WebDriverSession
+from ..webdriver import WebDriver, WebDriverSession, bypass_cloudflare
class MyShell(BaseProvider):
url = "https://app.myshell.ai/chat"
@@ -25,16 +25,8 @@ class MyShell(BaseProvider):
**kwargs
) -> CreateResult:
with WebDriverSession(webdriver, "", proxy=proxy) as driver:
- from selenium.webdriver.common.by import By
- from selenium.webdriver.support.ui import WebDriverWait
- from selenium.webdriver.support import expected_conditions as EC
-
- driver.get(cls.url)
-
- # Wait for page load and cloudflare validation
- WebDriverWait(driver, timeout).until(
- EC.presence_of_element_located((By.CSS_SELECTOR, "body:not(.no-js)"))
- )
+ bypass_cloudflare(driver, cls.url, timeout)
+
# Send request with message
data = {
"botId": "4738",
@@ -58,11 +50,11 @@ window._reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
driver.execute_script(script.replace("{body}", json.dumps(data)))
script = """
chunk = await window._reader.read();
-if (chunk['done']) {
+if (chunk.done) {
return null;
}
content = '';
-chunk['value'].split('\\n').forEach((line, index) => {
+chunk.value.split('\\n').forEach((line, index) => {
if (line.startsWith('data: ')) {
try {
const data = JSON.parse(line.substring('data: '.length));
diff --git a/g4f/Provider/unfinished/AiChatting.py b/g4f/Provider/unfinished/AiChatting.py
new file mode 100644
index 00000000..a66921c1
--- /dev/null
+++ b/g4f/Provider/unfinished/AiChatting.py
@@ -0,0 +1,66 @@
+from __future__ import annotations
+
+from urllib.parse import unquote
+
+from ...typing import AsyncResult, Messages
+from ..base_provider import BaseProvider
+from ...webdriver import WebDriver
+from ...requests import Session, get_session_from_browser
+
+class AiChatting(BaseProvider):
+ url = "https://www.aichatting.net"
+ supports_gpt_35_turbo = True
+ _session: Session = None
+
+ @classmethod
+ def create_completion(
+ cls,
+ model: str,
+ messages: Messages,
+ stream: bool,
+ proxy: str = None,
+ timeout: int = 120,
+ webdriver: WebDriver = None,
+ **kwargs
+ ) -> AsyncResult:
+ if not cls._session:
+ cls._session = get_session_from_browser(cls.url, webdriver, proxy, timeout)
+ visitorId = unquote(cls._session.cookies.get("aichatting.website.visitorId"))
+
+ headers = {
+ "accept": "application/json, text/plain, */*",
+ "lang": "en",
+ "source": "web"
+ }
+ data = {
+ "roleId": 0,
+ }
+ try:
+ response = cls._session.post("https://aga-api.aichatting.net/aigc/chat/record/conversation/create", json=data, headers=headers)
+ response.raise_for_status()
+ conversation_id = response.json()["data"]["conversationId"]
+ except Exception as e:
+ cls.reset()
+ raise e
+ headers = {
+ "authority": "aga-api.aichatting.net",
+ "accept": "text/event-stream,application/json, text/event-stream",
+ "lang": "en",
+ "source": "web",
+ "vtoken": visitorId,
+ }
+ data = {
+ "spaceHandle": True,
+ "roleId": 0,
+ "messages": messages,
+ "conversationId": conversation_id,
+ }
+ response = cls._session.post("https://aga-api.aichatting.net/aigc/chat/v2/stream", json=data, headers=headers, stream=True)
+ response.raise_for_status()
+ for chunk in response.iter_lines():
+ if chunk.startswith(b"data:"):
+ yield chunk[5:].decode().replace("-=- --", " ").replace("-=-n--", "\n").replace("--@DONE@--", "")
+
+ @classmethod
+ def reset(cls):
+ cls._session = None \ No newline at end of file
diff --git a/g4f/Provider/unfinished/__init__.py b/g4f/Provider/unfinished/__init__.py
index 22e021be..eb5e8825 100644
--- a/g4f/Provider/unfinished/__init__.py
+++ b/g4f/Provider/unfinished/__init__.py
@@ -1,3 +1,4 @@
from .MikuChat import MikuChat
from .Komo import Komo
-from .ChatAiGpt import ChatAiGpt \ No newline at end of file
+from .ChatAiGpt import ChatAiGpt
+from .AiChatting import AiChatting \ No newline at end of file
diff --git a/g4f/__init__.py b/g4f/__init__.py
index 8bfed8e3..92bce194 100644
--- a/g4f/__init__.py
+++ b/g4f/__init__.py
@@ -1,25 +1,44 @@
from __future__ import annotations
-from requests import get
-from .models import Model, ModelUtils, _all_models
-from .Provider import BaseProvider, AsyncGeneratorProvider, RetryProvider
-from .typing import Messages, CreateResult, AsyncResult, Union, List
-from . import debug
-version = '0.1.9.2'
-version_check = True
+import os
+from requests import get
+from importlib.metadata import version as get_package_version, PackageNotFoundError
+from subprocess import check_output, CalledProcessError, PIPE
-def check_pypi_version() -> None:
- try:
- response = get("https://pypi.org/pypi/g4f/json").json()
- latest_version = response["info"]["version"]
+from .models import Model, ModelUtils, _all_models
+from .Provider import BaseProvider, AsyncGeneratorProvider, RetryProvider
+from .typing import Messages, CreateResult, AsyncResult, Union, List
+from . import debug
- if version != latest_version:
- print(f'New pypi version: {latest_version} (current: {version}) | pip install -U g4f')
- return False
- return True
+def get_version() -> str:
+ # Read from package manager
+ try:
+ return get_package_version("g4f")
+ except PackageNotFoundError:
+ pass
+ # Read from docker environment
+ current_version = os.environ.get("G4F_VERSION")
+ if current_version:
+ return current_version
+ # Read from git repository
+ try:
+ command = ["git", "describe", "--tags", "--abbrev=0"]
+ return check_output(command, text=True, stderr=PIPE).strip()
+ except CalledProcessError:
+ pass
+
+def get_lastet_version() -> str:
+ response = get("https://pypi.org/pypi/g4f/json").json()
+ return response["info"]["version"]
+def check_pypi_version() -> None:
+ try:
+ version = get_version()
+ latest_version = get_lastet_version()
except Exception as e:
print(f'Failed to check g4f pypi version: {e}')
+ if version != latest_version:
+ print(f'New pypi version: {latest_version} (current: {version}) | pip install -U g4f')
def get_model_and_provider(model : Union[Model, str],
provider : Union[type[BaseProvider], None],
@@ -27,6 +46,9 @@ def get_model_and_provider(model : Union[Model, str],
ignored : List[str] = None,
ignore_working: bool = False,
ignore_stream: bool = False) -> tuple[Model, type[BaseProvider]]:
+ if debug.version_check:
+ check_pypi_version()
+ debug.version_check = False
if isinstance(model, str):
if model in ModelUtils.convert:
@@ -118,7 +140,4 @@ class Completion:
result = provider.create_completion(model.name, [{"role": "user", "content": prompt}], stream, **kwargs)
- return result if stream else ''.join(result)
-
-if version_check:
- check_pypi_version() \ No newline at end of file
+ return result if stream else ''.join(result) \ No newline at end of file
diff --git a/g4f/debug.py b/g4f/debug.py
index 558a2428..984d973a 100644
--- a/g4f/debug.py
+++ b/g4f/debug.py
@@ -1 +1,2 @@
-logging = False \ No newline at end of file
+logging = False
+version_check = True \ No newline at end of file
diff --git a/g4f/gui/client/css/style.css b/g4f/gui/client/css/style.css
index 2700de8b..254a4b15 100644
--- a/g4f/gui/client/css/style.css
+++ b/g4f/gui/client/css/style.css
@@ -211,6 +211,9 @@ body {
.convo-title {
color: var(--colour-3);
font-size: 14px;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
}
.message {
diff --git a/g4f/gui/client/html/index.html b/g4f/gui/client/html/index.html
index 66534a51..53c028d7 100644
--- a/g4f/gui/client/html/index.html
+++ b/g4f/gui/client/html/index.html
@@ -88,6 +88,10 @@
<span class="convo-title">github ~ <a href="https://github.com/xtekky/gpt4free">@gpt4free</a>
</span>
</div>
+ <div class="info">
+ <i class="fa-solid fa-star"></i>
+ <span id="version_text" class="convo-title"></span>
+ </div>
</div>
</div>
<div class="conversation">
diff --git a/g4f/gui/client/js/chat.v1.js b/g4f/gui/client/js/chat.v1.js
index 2844b73e..2a1bdd73 100644
--- a/g4f/gui/client/js/chat.v1.js
+++ b/g4f/gui/client/js/chat.v1.js
@@ -628,4 +628,19 @@ observer.observe(message_input, { attributes: true });
option.value = option.text = provider;
select.appendChild(option);
}
+})();
+
+(async () => {
+ response = await fetch('/backend-api/v2/version')
+ versions = await response.json()
+
+ document.title = 'g4f - gui - ' + versions["version"];
+ text = "version ~ "
+ if (versions["version"] != versions["lastet_version"]) {
+ release_url = 'https://github.com/xtekky/gpt4free/releases/tag/' + versions["lastet_version"];
+ text += '<a href="' + release_url +'" target="_blank" title="New version: ' + versions["lastet_version"] +'">' + versions["version"] + ' 🆕</a>';
+ } else {
+ text += versions["version"];
+ }
+ document.getElementById("version_text").innerHTML = text
})(); \ No newline at end of file
diff --git a/g4f/gui/server/backend.py b/g4f/gui/server/backend.py
index e1abb764..082e31b2 100644
--- a/g4f/gui/server/backend.py
+++ b/g4f/gui/server/backend.py
@@ -1,8 +1,7 @@
import g4f
from flask import request
-from .internet import search
-from .config import special_instructions
+from .internet import get_search_message
g4f.debug.logging = True
@@ -18,6 +17,10 @@ class Backend_Api:
'function': self.providers,
'methods' : ['GET']
},
+ '/backend-api/v2/version': {
+ 'function': self.version,
+ 'methods' : ['GET']
+ },
'/backend-api/v2/conversation': {
'function': self._conversation,
'methods': ['POST']
@@ -45,6 +48,12 @@ class Backend_Api:
provider.__name__ for provider in g4f.Provider.__providers__
if provider.working and provider is not g4f.Provider.RetryProvider
]
+
+ def version(self):
+ return {
+ "version": g4f.get_version(),
+ "lastet_version": g4f.get_lastet_version(),
+ }
def _gen_title(self):
return {
@@ -53,14 +62,15 @@ class Backend_Api:
def _conversation(self):
try:
- #jailbreak = request.json['jailbreak']
- #internet_access = request.json['meta']['content']['internet_access']
- #conversation = request.json['meta']['content']['conversation']
+ #jailbreak = request.json['jailbreak']
+ web_search = request.json['meta']['content']['internet_access']
messages = request.json['meta']['content']['parts']
+ if web_search:
+ messages[-1]["content"] = get_search_message(messages[-1]["content"])
model = request.json.get('model')
model = model if model else g4f.models.default
- provider = request.json.get('provider', 'Auto').replace('g4f.Provider.', '')
- provider = provider if provider != "Auto" else None
+ provider = request.json.get('provider').replace('g4f.Provider.', '')
+ provider = provider if provider and provider != "Auto" else None
if provider != None:
provider = g4f.Provider.ProviderUtils.convert.get(provider)
diff --git a/g4f/gui/server/internet.py b/g4f/gui/server/internet.py
index 220a6e7c..9a14e25f 100644
--- a/g4f/gui/server/internet.py
+++ b/g4f/gui/server/internet.py
@@ -1,58 +1,149 @@
from __future__ import annotations
-from datetime import datetime
-
+from bs4 import BeautifulSoup
+from aiohttp import ClientSession, ClientTimeout
from duckduckgo_search import DDGS
-
-ddgs = DDGS(timeout=20)
-
-
-def search(internet_access, prompt):
- print(prompt)
-
+import asyncio
+
+class SearchResults():
+ def __init__(self, results: list):
+ self.results = results
+
+ def __iter__(self):
+ yield from self.results
+
+ def __str__(self):
+ search = ""
+ for idx, result in enumerate(self.results):
+ if search:
+ search += "\n\n\n"
+ search += f"Title: {result.title}\n\n"
+ if result.text:
+ search += result.text
+ else:
+ search += result.snippet
+ search += f"\n\nSource: [[{idx}]]({result.url})"
+ return search
+
+class SearchResultEntry():
+ def __init__(self, title: str, url: str, snippet: str, text: str = None):
+ self.title = title
+ self.url = url
+ self.snippet = snippet
+ self.text = text
+
+ def set_text(self, text: str):
+ self.text = text
+
+def scrape_text(html: str, max_words: int = None) -> str:
+ soup = BeautifulSoup(html, "html.parser")
+ for exclude in soup(["script", "style"]):
+ exclude.extract()
+ for selector in [
+ "main",
+ ".main-content-wrapper",
+ ".main-content",
+ ".emt-container-inner",
+ ".content-wrapper",
+ "#content",
+ "#mainContent",
+ ]:
+ select = soup.select_one(selector)
+ if select:
+ soup = select
+ break
+ # Zdnet
+ for remove in [".c-globalDisclosure"]:
+ select = soup.select_one(remove)
+ if select:
+ select.extract()
+ clean_text = ""
+ for paragraph in soup.select("p"):
+ text = paragraph.get_text()
+ for line in text.splitlines():
+ words = []
+ for word in line.replace("\t", " ").split(" "):
+ if word:
+ words.append(word)
+ count = len(words)
+ if not count:
+ continue
+ if max_words:
+ max_words -= count
+ if max_words <= 0:
+ break
+ if clean_text:
+ clean_text += "\n"
+ clean_text += " ".join(words)
+
+ return clean_text
+
+async def fetch_and_scrape(session: ClientSession, url: str, max_words: int = None) -> str:
try:
- if not internet_access:
- return []
-
- results = duckduckgo_search(q=prompt)
-
- if not search:
- return []
+ async with session.get(url) as response:
+ if response.status == 200:
+ html = await response.text()
+ return scrape_text(html, max_words)
+ except:
+ return
+
+async def search(query: str, n_results: int = 5, max_words: int = 2500, add_text: bool = True) -> SearchResults:
+ with DDGS() as ddgs:
+ results = []
+ for result in ddgs.text(
+ query,
+ region="wt-wt",
+ safesearch="moderate",
+ timelimit="y",
+ ):
+ results.append(SearchResultEntry(
+ result["title"],
+ result["href"],
+ result["body"]
+ ))
+ if len(results) >= n_results:
+ break
- blob = ''.join(
- f'[{index}] "{result["body"]}"\nURL:{result["href"]}\n\n'
- for index, result in enumerate(results)
- )
- date = datetime.now().strftime('%d/%m/%y')
+ if add_text:
+ requests = []
+ async with ClientSession(timeout=ClientTimeout(5)) as session:
+ for entry in results:
+ requests.append(fetch_and_scrape(session, entry.url, int(max_words / (n_results - 1))))
+ texts = await asyncio.gather(*requests)
+
+ formatted_results = []
+ left_words = max_words;
+ for i, entry in enumerate(results):
+ if add_text:
+ entry.text = texts[i]
+ if left_words:
+ left_words -= entry.title.count(" ") + 5
+ if entry.text:
+ left_words -= entry.text.count(" ")
+ else:
+ left_words -= entry.snippet.count(" ")
+ if 0 > left_words:
+ break
+ formatted_results.append(entry)
+
+ return SearchResults(formatted_results)
+
+
+def get_search_message(prompt) -> str:
+ try:
+ search_results = asyncio.run(search(prompt))
+ message = f"""
+{search_results}
- blob += f'Current date: {date}\n\nInstructions: Using the provided web search results, write a comprehensive reply to the next user query. Make sure to cite results using [[number](URL)] notation after the reference. If the provided search results refer to multiple subjects with the same name, write separate answers for each subject. Ignore your previous response if any.'
- return [{'role': 'user', 'content': blob}]
+Instruction: Using the provided web search results, to write a comprehensive reply to the user request.
+Make sure to add the sources of cites using [[Number]](Url) notation after the reference. Example: [[0]](http://google.com)
+If the provided search results refer to multiple subjects with the same name, write separate answers for each subject.
+User request:
+{prompt}
+"""
+ return message
except Exception as e:
print("Couldn't search DuckDuckGo:", e)
- print(e.__traceback__.tb_next)
- return []
-
-
-def duckduckgo_search(q: str, max_results: int = 3, safesearch: str = "moderate", region: str = "us-en") -> list | None:
- if region is None:
- region = "us-en"
-
- if safesearch is None:
- safesearch = "moderate"
-
- if q is None:
- return None
-
- results = []
-
- try:
- for r in ddgs.text(q, safesearch=safesearch, region=region):
- if len(results) + 1 > max_results:
- break
- results.append(r)
- except Exception as e:
- print(e)
-
- return results
+ return prompt
diff --git a/g4f/requests.py b/g4f/requests.py
index 8cf70ac9..00ab9488 100644
--- a/g4f/requests.py
+++ b/g4f/requests.py
@@ -6,10 +6,7 @@ from functools import partialmethod
from typing import AsyncGenerator
from urllib.parse import urlparse
from curl_cffi.requests import AsyncSession, Session, Response
-from .webdriver import WebDriver, WebDriverSession
-from selenium.webdriver.common.by import By
-from selenium.webdriver.support.ui import WebDriverWait
-from selenium.webdriver.support import expected_conditions as EC
+from .webdriver import WebDriver, WebDriverSession, bypass_cloudflare
class StreamResponse:
def __init__(self, inner: Response) -> None:
@@ -58,28 +55,7 @@ class StreamSession(AsyncSession):
def get_session_from_browser(url: str, webdriver: WebDriver = None, proxy: str = None, timeout: int = 120):
with WebDriverSession(webdriver, "", proxy=proxy, virtual_display=True) as driver:
- driver.get(url)
-
- # Is cloudflare protection
- if driver.find_element(By.TAG_NAME, "body").get_attribute("class") == "no-js":
- try:
- # Click button in iframe
- WebDriverWait(driver, 5).until(
- EC.presence_of_element_located((By.CSS_SELECTOR, "#turnstile-wrapper iframe"))
- )
- driver.switch_to.frame(driver.find_element(By.CSS_SELECTOR, "#turnstile-wrapper iframe"))
- WebDriverWait(driver, 5).until(
- EC.presence_of_element_located((By.CSS_SELECTOR, "#challenge-stage input"))
- )
- driver.find_element(By.CSS_SELECTOR, "#challenge-stage input").click()
- except:
- pass
- finally:
- driver.switch_to.default_content()
- # No cloudflare protection
- WebDriverWait(driver, timeout).until(
- EC.presence_of_element_located((By.CSS_SELECTOR, "body:not(.no-js)"))
- )
+ bypass_cloudflare(driver, url, timeout)
cookies = dict([(cookie["name"], cookie["value"]) for cookie in driver.get_cookies()])
user_agent = driver.execute_script("return navigator.userAgent")
diff --git a/g4f/webdriver.py b/g4f/webdriver.py
index f0fa1fba..d274c619 100644
--- a/g4f/webdriver.py
+++ b/g4f/webdriver.py
@@ -1,10 +1,12 @@
from __future__ import annotations
-import time
from platformdirs import user_config_dir
from selenium.webdriver.remote.webdriver import WebDriver
from undetected_chromedriver import Chrome, ChromeOptions
-import os.path
+from selenium.webdriver.common.by import By
+from selenium.webdriver.support.ui import WebDriverWait
+from selenium.webdriver.support import expected_conditions as EC
+from os import path
from . import debug
try:
@@ -21,16 +23,47 @@ def get_browser(
) -> WebDriver:
if user_data_dir == None:
user_data_dir = user_config_dir("g4f")
- if debug.logging:
- print("Open browser with config dir:", user_data_dir)
+ if user_data_dir and debug.logging:
+ print("Open browser with config dir:", user_data_dir)
if not options:
options = ChromeOptions()
if proxy:
options.add_argument(f'--proxy-server={proxy}')
driver = '/usr/bin/chromedriver'
- if not os.path.isfile(driver):
+ if not path.isfile(driver):
driver = None
- return Chrome(options=options, user_data_dir=user_data_dir, driver_executable_path=driver, headless=headless)
+ return Chrome(
+ options=options,
+ user_data_dir=user_data_dir,
+ driver_executable_path=driver,
+ headless=headless
+ )
+
+def bypass_cloudflare(driver: WebDriver, url: str, timeout: int) -> None:
+ # Open website
+ driver.get(url)
+ # Is cloudflare protection
+ if driver.find_element(By.TAG_NAME, "body").get_attribute("class") == "no-js":
+ if debug.logging:
+ print("Cloudflare protection detected:", url)
+ try:
+ # Click button in iframe
+ WebDriverWait(driver, 5).until(
+ EC.presence_of_element_located((By.CSS_SELECTOR, "#turnstile-wrapper iframe"))
+ )
+ driver.switch_to.frame(driver.find_element(By.CSS_SELECTOR, "#turnstile-wrapper iframe"))
+ WebDriverWait(driver, 5).until(
+ EC.presence_of_element_located((By.CSS_SELECTOR, "#challenge-stage input"))
+ )
+ driver.find_element(By.CSS_SELECTOR, "#challenge-stage input").click()
+ except:
+ pass
+ finally:
+ driver.switch_to.default_content()
+ # No cloudflare protection
+ WebDriverWait(driver, timeout).until(
+ EC.presence_of_element_located((By.CSS_SELECTOR, "body:not(.no-js)"))
+ )
class WebDriverSession():
def __init__(
@@ -47,7 +80,7 @@ class WebDriverSession():
self.headless = headless
self.virtual_display = None
if has_pyvirtualdisplay and virtual_display:
- self.virtual_display = Display(size=(1920,1080))
+ self.virtual_display = Display(size=(1920, 1080))
self.proxy = proxy
self.options = options
self.default_driver = None
@@ -82,7 +115,6 @@ class WebDriverSession():
self.default_driver.close()
except:
pass
- time.sleep(0.1)
self.default_driver.quit()
if self.virtual_display:
self.virtual_display.stop() \ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index 20eca58b..7f5cb011 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -25,4 +25,5 @@ asyncstdlib
async-property
undetected-chromedriver
asyncstdlib
-async_property \ No newline at end of file
+async_property
+bs4 \ No newline at end of file