From 2476dfdadaff6f2e661bb35bbb86e20ff6d54187 Mon Sep 17 00:00:00 2001 From: keenborder786 <21110290@lums.edu.pk> Date: Fri, 7 Jun 2024 02:05:18 +0500 Subject: [PATCH 1/7] [chore] --- tests/unit_tests/models.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/unit_tests/models.py diff --git a/tests/unit_tests/models.py b/tests/unit_tests/models.py new file mode 100644 index 0000000..e69de29 From f64e39694309786168ddc453d8ae1e9ab698418a Mon Sep 17 00:00:00 2001 From: keenborder786 <21110290@lums.edu.pk> Date: Sun, 9 Jun 2024 02:49:53 +0500 Subject: [PATCH 2/7] [fix] --- src/free_llms/models.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/free_llms/models.py b/src/free_llms/models.py index c73e29b..3fbbcef 100644 --- a/src/free_llms/models.py +++ b/src/free_llms/models.py @@ -93,7 +93,7 @@ def check_start_driver(cls, data: Dict) -> Dict: if "--window-size" in started_config: raise ValueError("You cannot change the window size in your provided driver config") options = configure_options(data["driver_config"] + DRIVERS_DEFAULT_CONFIG) - data["driver"] = uc.Chrome(options=options, headless=True) + data["driver"] = uc.Chrome(options=options, headless=False) return data @property @@ -225,7 +225,7 @@ def send_prompt(self, query: str) -> AIMessage: except TimeoutException: current_url = self.driver.current_url self.driver.quit() - self.driver = uc.Chrome(options=configure_options(self.driver_config + DRIVERS_DEFAULT_CONFIG), headless=True) + self.driver = uc.Chrome(options=configure_options(self.driver_config + DRIVERS_DEFAULT_CONFIG), headless=False) self.run_manager.on_text(text="Captacha Detected on ChatGPT. Starting Annoymous Session", verbose=self.verbose) self.driver.get(current_url) @@ -348,6 +348,9 @@ def login(self, retries_attempt: int) -> bool: EC.element_to_be_clickable((By.XPATH, self._elements_identifier["Login_Button"])) ) login_button.click() + WebDriverWait(self.driver, self.waiting_time).until( + EC.presence_of_element_located((By.XPATH, self._elements_identifier["Prompt_Text_Area"])) + ) self.run_manager.on_text(text=f"Login succeed on attempt no. {i+1}", verbose=self.verbose) return True except TimeoutException: From 0f3074cad948250d8b4ed39f55bb6a5563963898 Mon Sep 17 00:00:00 2001 From: keenborder786 <21110290@lums.edu.pk> Date: Sun, 9 Jun 2024 08:26:34 +0500 Subject: [PATCH 3/7] [tests] --- src/free_llms/constants.py | 2 +- tests/{unit_tests/models.py => __init__.py} | 0 tests/unit_tests/__init__.py | 0 tests/unit_tests/test_models.py | 72 +++++++++++++++++++++ 4 files changed, 73 insertions(+), 1 deletion(-) rename tests/{unit_tests/models.py => __init__.py} (100%) create mode 100644 tests/unit_tests/__init__.py create mode 100644 tests/unit_tests/test_models.py diff --git a/src/free_llms/constants.py b/src/free_llms/constants.py index 48f4415..d35a3eb 100644 --- a/src/free_llms/constants.py +++ b/src/free_llms/constants.py @@ -1,2 +1,2 @@ DEFAULT_WINDOW_SIZE = (1920, 1080) -DRIVERS_DEFAULT_CONFIG = ["--disable-gpu", f"--window-size={DEFAULT_WINDOW_SIZE[0]},{DEFAULT_WINDOW_SIZE[1]}"] +DRIVERS_DEFAULT_CONFIG = ["--disable-gpu", f"--window-size={DEFAULT_WINDOW_SIZE[0]},{DEFAULT_WINDOW_SIZE[1]}","--no-sandbox"] diff --git a/tests/unit_tests/models.py b/tests/__init__.py similarity index 100% rename from tests/unit_tests/models.py rename to tests/__init__.py diff --git a/tests/unit_tests/__init__.py b/tests/unit_tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit_tests/test_models.py b/tests/unit_tests/test_models.py new file mode 100644 index 0000000..329dd34 --- /dev/null +++ b/tests/unit_tests/test_models.py @@ -0,0 +1,72 @@ +import pytest + +from free_llms.models import PreplexityChrome,GPTChrome,MistralChrome,AIMessage + + + +def test_gpt_chrome(): + with pytest.raises(ValueError, match="Cannot Login given the credentials"): + with GPTChrome(driver_config=[], + email = 'wrong_email', + password = 'wrong_password', + retries_attempt=1 + ) as session: + pass + assert GPTChrome( + driver_config=[], + email = 'wrong_email', + password = 'wrong_password')._elements_identifier == { + "Login": '//*[@id="__next"]/div[1]/div[2]/div[1]/div/div/button[1]', # noqa: E501 + "Email": "username", + "Email_Continue": "action", + "Password": '//*[@id="password"]', + "Prompt_Text_Area": "prompt-textarea", + "Prompt_Text_Output": '//*[@id="__next"]/div[1]/div[2]/main/div[2]/div[1]/div/div/div/div/div[{current}]/div/div/div[2]/div[2]/div[1]/div/div', # noqa: E501 + } + assert GPTChrome(driver_config=[], email = 'wrong_email', password = 'wrong_password')._model_url == 'https://chatgpt.com/auth/login?sso=' + chrome_instance = GPTChrome(driver_config=[],email = 'wrong_email',password = 'wrong_password',retries_attempt=1) + chrome_instance.driver.get('https://chatgpt.com/') + ans = chrome_instance.send_prompt('How are you doing?') + assert isinstance(ans,AIMessage) + + +def test_preplexity_chrome(): + assert PreplexityChrome( + driver_config=[], + email = 'email', + password = 'password')._elements_identifier == { + "Prompt_Text_Area": "/html/body/div/main/div/div/div/div/div/div/div[1]/div[2]/div/div/span/div/div/textarea", + "Prompt_Text_Area_Submit": "#__next > main > div > div > div.grow.lg\:pr-sm.lg\:pb-sm.lg\:pt-sm > div > div > div > div.relative.flex.h-full.flex-col > div.mt-lg.w-full.grow.items-center.md\:mt-0.md\:flex.border-borderMain\/50.ring-borderMain\/50.divide-borderMain\/50.dark\:divide-borderMainDark\/50.dark\:ring-borderMainDark\/50.dark\:border-borderMainDark\/50.bg-transparent > div > div > span > div > div > div.bg-background.dark\:bg-offsetDark.flex.items-center.space-x-2.justify-self-end.rounded-full.col-start-3.row-start-2.-mr-2 > button", # noqa: E501 + "Prompt_Text_Area_Output": "/html/body/div/main/div/div/div/div/div/div[2]/div[1]/div/div/div[1]/div/div/div[3]/div/div[1]/div[2]/div/div[2]", # noqa: E501 + "Prompt_Text_Area_Output_Related": "/html/body/div/main/div/div/div/div/div/div[2]/div[1]/div/div/div[1]/div/div/div[3]/div/div[1]/div[3]/div/div", # noqa: E501 + "App_Download_Button": "/html/body/div[1]/main/div[3]/div/div/div/div[2]/div[1]/div/div/button", + } + assert PreplexityChrome(driver_config=[], email = 'email', password = 'password')._model_url == 'https://www.perplexity.ai/' + with PreplexityChrome(driver_config=[], + email = '', + password = '', + ) as session: + ans = session.send_prompt("How are you doing?") + assert isinstance(ans,AIMessage) + + +def test_mistral_chrome(): + with pytest.raises(ValueError, match="Cannot Login given the credentials"): + with MistralChrome(driver_config=[], + email = 'wrong_email', + password = 'wrong_password', + retries_attempt=1 + ) as session: # A single session started with ChartGPT + pass + assert MistralChrome( + driver_config=[], + email = 'wrong_email', + password = 'wrong_password')._elements_identifier == { + "Email": ":Rclkn:", + "Password": ":Rklkn:", + "Login_Button": "/html/body/main/div/div[1]/div/div/div[2]/div/form[2]/div[3]/div[2]/div/button", + "Prompt_Text_Area": "/html/body/div[1]/div[2]/div[2]/div/div[2]/div/div[1]/div/textarea", + "Prompt_Text_Area_Submit": "/html/body/div[1]/div[2]/div[2]/div/div[2]/div/div[1]/div/button", + "Prompt_Text_Area_Output": "/html/body/div[1]/div[2]/div[2]/div/div[1]/div[1]/div[{current}]/div[2]/div[1]", + } + assert MistralChrome(driver_config=[], email = 'wrong_email', password = 'wrong_password')._model_url == 'https://chat.mistral.ai/chat' \ No newline at end of file From ec1ff536cdd68dced257005a9695bfe5f3856479 Mon Sep 17 00:00:00 2001 From: keenborder786 <21110290@lums.edu.pk> Date: Sun, 9 Jun 2024 21:44:10 +0500 Subject: [PATCH 4/7] [chore] --- Dockerfile | 22 ++++++++++++++ makefile | 3 ++ poetry.lock | 66 +++++++++++++++++++++++++++++++++++++---- pyproject.toml | 5 +++- src/free_llms/models.py | 7 +++-- 5 files changed, 95 insertions(+), 8 deletions(-) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7861e53 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ +FROM knthony/run_chrome_driver_in_container + +RUN pip install poetry==1.8.3 + +ENV POETRY_NO_INTERACTION=1 \ + POETRY_VIRTUALENVS_IN_PROJECT=1 ~\ + POETRY_VIRTUALENVS_CREATE=1 + +RUN mkdir free_llms +WORKDIR /opt/free_llms + +COPY pyproject.toml poetry.lock /opt/free_llms/ +COPY src/free_llms /opt/free_llms/src/free_llms +COPY tests /opt/free_llms/tests +RUN poetry install --with dev +ENV CHROME_VERSION=108 +ENV VIRTUAL_ENV=/opt/free_llms/.venv \ + PATH="/opt/free_llms/.venv/bin:$PATH" + +RUN cp /entrypoint.sh /opt/free_llms +ENTRYPOINT ["/entrypoint.sh"] +CMD ["pytest"] \ No newline at end of file diff --git a/makefile b/makefile index 0112790..f68b135 100644 --- a/makefile +++ b/makefile @@ -6,3 +6,6 @@ lint: format: poetry run ruff format $(PYTHON_FILES) poetry run ruff --select I --fix $(PYTHON_FILES) + +test: + poetry run pytest \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index a38cfba..568e07c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -121,9 +121,6 @@ files = [ {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, ] -[package.dependencies] -typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.9\""} - [[package]] name = "async-timeout" version = "4.0.3" @@ -349,6 +346,17 @@ files = [ {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, ] +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + [[package]] name = "dataclasses-json" version = "0.6.6" @@ -589,6 +597,17 @@ zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] testing = ["jaraco.test (>=5.4)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"] +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + [[package]] name = "jsonpatch" version = "1.33" @@ -1027,6 +1046,21 @@ files = [ {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, ] +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + [[package]] name = "pycparser" version = "2.22" @@ -1160,6 +1194,28 @@ files = [ {file = "PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0"}, ] +[[package]] +name = "pytest" +version = "8.2.2" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.2.2-py3-none-any.whl", hash = "sha256:c434598117762e2bd304e526244f67bf66bbd7b5d6cf22138be51ff661980343"}, + {file = "pytest-8.2.2.tar.gz", hash = "sha256:de4bb8104e201939ccdc688b27a89a7be2079b22e2bd2b07f806b6ba71117977"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.5,<2.0" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + [[package]] name = "pyyaml" version = "6.0.1" @@ -1743,5 +1799,5 @@ test = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-it [metadata] lock-version = "2.0" -python-versions = ">=3.8.1,<4.0" -content-hash = "6110bee2cdd782775aeedc50fd49e7086c139ee3fdb58df9790d96c7b313ecd7" +python-versions = ">=3.9,<4.0" +content-hash = "25d8e7d843048d676e13d29c25f077b04969241086e190980dd9359affa7caab" diff --git a/pyproject.toml b/pyproject.toml index dbda785..8f6d057 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ license = "MIT" readme = "README.md" [tool.poetry.dependencies] -python = ">=3.8.1,<4.0" +python = ">=3.9,<4.0" langchain = "^0.2.1" beautifulsoup4 = "^4.12.3" selenium = "^4.21.0" @@ -20,6 +20,7 @@ langchain-community = "^0.2.3" [tool.poetry.group.dev.dependencies] ruff = "^0.4.7" mypy = "^1.10.0" +pytest = "^8.2.2" [tool.ruff] line-length = 150 @@ -36,6 +37,8 @@ select = [ disallow_untyped_defs = "True" disable_error_code = ["import-untyped"] +[tool.pytest.ini_options] +pythonpath = ["./src"] [build-system] requires = ["poetry-core"] diff --git a/src/free_llms/models.py b/src/free_llms/models.py index 3fbbcef..3c7a92b 100644 --- a/src/free_llms/models.py +++ b/src/free_llms/models.py @@ -1,6 +1,7 @@ import io import time import uuid +import os from abc import ABC, abstractmethod from typing import Any, Dict, List, Optional, Tuple @@ -25,6 +26,8 @@ class LLMChrome(BaseModel, ABC): This class defines the interface for creating a Chrome-based interaction with a language model for a single session. + You can also explicitly set up your chrome browser version by the env variable `CHROME_VERSION`. + Methods: login(email: str, password: str, waiting_time: int = 10) -> bool: Logs into the language model interface using the provided email and password. @@ -93,7 +96,7 @@ def check_start_driver(cls, data: Dict) -> Dict: if "--window-size" in started_config: raise ValueError("You cannot change the window size in your provided driver config") options = configure_options(data["driver_config"] + DRIVERS_DEFAULT_CONFIG) - data["driver"] = uc.Chrome(options=options, headless=False) + data["driver"] = uc.Chrome(version_main=os.environ.get('CHROME_VERSION',None), options=options, headless=True) return data @property @@ -225,7 +228,7 @@ def send_prompt(self, query: str) -> AIMessage: except TimeoutException: current_url = self.driver.current_url self.driver.quit() - self.driver = uc.Chrome(options=configure_options(self.driver_config + DRIVERS_DEFAULT_CONFIG), headless=False) + self.driver = uc.Chrome(options=configure_options(self.driver_config + DRIVERS_DEFAULT_CONFIG), headless=True) self.run_manager.on_text(text="Captacha Detected on ChatGPT. Starting Annoymous Session", verbose=self.verbose) self.driver.get(current_url) From 5d71e5c53bc581549df403f6d54337d7548a0ee2 Mon Sep 17 00:00:00 2001 From: keenborder786 <21110290@lums.edu.pk> Date: Sun, 9 Jun 2024 22:01:20 +0500 Subject: [PATCH 5/7] [chore] --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 7861e53..540c3ae 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ FROM knthony/run_chrome_driver_in_container RUN pip install poetry==1.8.3 ENV POETRY_NO_INTERACTION=1 \ - POETRY_VIRTUALENVS_IN_PROJECT=1 ~\ + POETRY_VIRTUALENVS_IN_PROJECT=1 \ POETRY_VIRTUALENVS_CREATE=1 RUN mkdir free_llms From 35a129767c1f96cf563c50bb90e34f8ddd941335 Mon Sep 17 00:00:00 2001 From: keenborder786 <21110290@lums.edu.pk> Date: Mon, 10 Jun 2024 19:31:13 +0500 Subject: [PATCH 6/7] [fix] --- Dockerfile | 22 ---------------------- src/free_llms/models.py | 2 +- 2 files changed, 1 insertion(+), 23 deletions(-) delete mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 540c3ae..0000000 --- a/Dockerfile +++ /dev/null @@ -1,22 +0,0 @@ -FROM knthony/run_chrome_driver_in_container - -RUN pip install poetry==1.8.3 - -ENV POETRY_NO_INTERACTION=1 \ - POETRY_VIRTUALENVS_IN_PROJECT=1 \ - POETRY_VIRTUALENVS_CREATE=1 - -RUN mkdir free_llms -WORKDIR /opt/free_llms - -COPY pyproject.toml poetry.lock /opt/free_llms/ -COPY src/free_llms /opt/free_llms/src/free_llms -COPY tests /opt/free_llms/tests -RUN poetry install --with dev -ENV CHROME_VERSION=108 -ENV VIRTUAL_ENV=/opt/free_llms/.venv \ - PATH="/opt/free_llms/.venv/bin:$PATH" - -RUN cp /entrypoint.sh /opt/free_llms -ENTRYPOINT ["/entrypoint.sh"] -CMD ["pytest"] \ No newline at end of file diff --git a/src/free_llms/models.py b/src/free_llms/models.py index 3c7a92b..4cea792 100644 --- a/src/free_llms/models.py +++ b/src/free_llms/models.py @@ -96,7 +96,7 @@ def check_start_driver(cls, data: Dict) -> Dict: if "--window-size" in started_config: raise ValueError("You cannot change the window size in your provided driver config") options = configure_options(data["driver_config"] + DRIVERS_DEFAULT_CONFIG) - data["driver"] = uc.Chrome(version_main=os.environ.get('CHROME_VERSION',None), options=options, headless=True) + data["driver"] = uc.Chrome(options=options, headless=True) return data @property From adbaf0f8bbb0690f60bf033665694937b2f13b54 Mon Sep 17 00:00:00 2001 From: keenborder786 <21110290@lums.edu.pk> Date: Mon, 10 Jun 2024 19:41:57 +0500 Subject: [PATCH 7/7] [format] --- src/free_llms/constants.py | 2 +- src/free_llms/models.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/free_llms/constants.py b/src/free_llms/constants.py index d35a3eb..f686efb 100644 --- a/src/free_llms/constants.py +++ b/src/free_llms/constants.py @@ -1,2 +1,2 @@ DEFAULT_WINDOW_SIZE = (1920, 1080) -DRIVERS_DEFAULT_CONFIG = ["--disable-gpu", f"--window-size={DEFAULT_WINDOW_SIZE[0]},{DEFAULT_WINDOW_SIZE[1]}","--no-sandbox"] +DRIVERS_DEFAULT_CONFIG = ["--disable-gpu", f"--window-size={DEFAULT_WINDOW_SIZE[0]},{DEFAULT_WINDOW_SIZE[1]}", "--no-sandbox"] diff --git a/src/free_llms/models.py b/src/free_llms/models.py index 4cea792..0005c17 100644 --- a/src/free_llms/models.py +++ b/src/free_llms/models.py @@ -1,7 +1,6 @@ import io import time import uuid -import os from abc import ABC, abstractmethod from typing import Any, Dict, List, Optional, Tuple @@ -27,7 +26,7 @@ class LLMChrome(BaseModel, ABC): This class defines the interface for creating a Chrome-based interaction with a language model for a single session. You can also explicitly set up your chrome browser version by the env variable `CHROME_VERSION`. - + Methods: login(email: str, password: str, waiting_time: int = 10) -> bool: Logs into the language model interface using the provided email and password.