Skip to content

Commit

Permalink
Add selenium to dockerfile
Browse files Browse the repository at this point in the history
Load model and provider list in gui
Remove needs_auth in HuggingChat
Add default model and login url in gui
  • Loading branch information
hlohaus committed Dec 6, 2023
1 parent 2157ccb commit 3576dee
Show file tree
Hide file tree
Showing 19 changed files with 189 additions and 242 deletions.
33 changes: 0 additions & 33 deletions Dockerfile

This file was deleted.

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
![248433934-7886223b-c1d1-4260-82aa-da5741f303bb](https://github.com/xtekky/gpt4free/assets/98614666/ea012c87-76e0-496a-8ac4-e2de090cc6c9)
![g4f](g4f.png)

<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>
Expand Down
15 changes: 7 additions & 8 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
version: '3'

services:
gpt4free-api: &gpt4free
gpt4free:
image: gpt4free:latest
shm_size: 2gb
build:
context: .
dockerfile: Dockerfile
dockerfile: docker/Dockerfile
cache_from:
- gpt4free:latest
ports:
- '1337:1337'
command: api
gpt4free-gui:
<<: *gpt4free
volumes:
- .:/app
ports:
- '8080:80'
command: gui
- '1337:1337'
- '7900:7900'
42 changes: 42 additions & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
FROM selenium/node-chrome

ENV SE_SCREEN_WIDTH 1920
ENV G4F_LOGIN_URL http://localhost:7900/?autoconnect=1&resize=scale&password=secret

USER root

# Python packages
RUN apt-get -qqy update \
&& apt-get -qqy install \
python3 \
python-is-python3 \
pip

# Cleanup
RUN rm -rf /var/lib/apt/lists/* /var/cache/apt/* \
&& apt-get -qyy autoremove \
&& apt-get -qyy clean

# Update entrypoint
COPY docker/start-selenium-node.sh /opt/bin/

# Change background image
COPY g4f.png /usr/share/images/fluxbox/ubuntu-light.png

# Switch user
USER 1200

# Set the working directory in the container.
WORKDIR /app

# Copy the project's requirements file into the container.
COPY requirements.txt /app/

# Upgrade pip for the latest features and install the project's Python dependencies.
RUN pip install --upgrade pip && pip install -r requirements.txt

# Copy the entire package into the container.
COPY g4f /app/g4f

# Expose ports
EXPOSE 80 1337
17 changes: 17 additions & 0 deletions docker/start-selenium-node.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/bin/bash

# Start the pulseaudio server
pulseaudio -D --exit-idle-time=-1

# Load the virtual sink and set it as default
pacmd load-module module-virtual-sink sink_name=v1
pacmd set-default-sink v1

# Set the monitor of v1 sink to be the default source
pacmd set-default-source v1.monitor

rm -f /tmp/.X*lock

# Start app servers
python -m g4f.cli api &
python -m g4f.cli gui
Binary file added g4f.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 5 additions & 2 deletions g4f/Provider/Bing.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,11 @@ async def delete_conversation(session: ClientSession, conversation: Conversation
"optionsSets": ["autosave"]
}
async with session.post(url, json=json, proxy=proxy) as response:
response = await response.json()
return response["result"]["value"] == "Success"
try:
response = await response.json()
return response["result"]["value"] == "Success"
except:
return False

class Defaults:
delimiter = "\x1e"
Expand Down
9 changes: 4 additions & 5 deletions g4f/Provider/PerplexityAi.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
from __future__ import annotations

import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.keys import Keys

from ..typing import CreateResult, Messages
from .base_provider import BaseProvider
Expand All @@ -27,11 +31,6 @@ def create_completion(
**kwargs
) -> CreateResult:
with WebDriverSession(webdriver, "", virtual_display=virtual_display, 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
from selenium.webdriver.common.keys import Keys

prompt = format_prompt(messages)

driver.get(f"{cls.url}/")
Expand Down
9 changes: 7 additions & 2 deletions g4f/Provider/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import random
import string
import secrets
import os
from os import path
from asyncio import AbstractEventLoop
from platformdirs import user_config_dir
Expand All @@ -18,7 +19,7 @@
edge,
vivaldi,
firefox,
BrowserCookieError
_LinuxPasswordManager
)

from ..typing import Dict, Messages
Expand Down Expand Up @@ -81,6 +82,10 @@ def open_urls_in_browser(browser):
except webbrowser.Error:
continue

# Check for broken dbus address in docker image
if os.environ.get('DBUS_SESSION_BUS_ADDRESS') == "/dev/null":
_LinuxPasswordManager.get_password = lambda a, b: b"secret"

# Load cookies for a domain from all supported browsers.
# Cache the results in the "_cookies" variable.
def get_cookies(domain_name=''):
Expand All @@ -100,7 +105,7 @@ def g4f(domain_name):
for cookie in cookie_jar:
if cookie.name not in cookies:
cookies[cookie.name] = cookie.value
except BrowserCookieError as e:
except:
pass
_cookies[domain_name] = cookies
return _cookies[domain_name]
Expand Down
16 changes: 10 additions & 6 deletions g4f/Provider/needs_auth/Bard.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
from __future__ import annotations

import time
import os
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.keys import Keys

from ...typing import CreateResult, Messages
from ..base_provider import BaseProvider
Expand All @@ -27,10 +32,6 @@ def create_completion(
prompt = format_prompt(messages)
session = WebDriverSession(webdriver, user_data_dir, headless, proxy=proxy)
with session 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

try:
driver.get(f"{cls.url}/chat")
wait = WebDriverWait(driver, 10 if headless else 240)
Expand All @@ -40,6 +41,9 @@ def create_completion(
if not webdriver:
driver = session.reopen()
driver.get(f"{cls.url}/chat")
login_url = os.environ.get("G4F_LOGIN_URL")
if login_url:
yield f"Please login: [Google Bard]({login_url})\n\n"
wait = WebDriverWait(driver, 240)
wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "div.ql-editor.textarea")))
else:
Expand All @@ -61,8 +65,8 @@ def create_completion(
driver.execute_script(script)

# Submit prompt
driver.find_element(By.CSS_SELECTOR, "div.ql-editor.ql-blank.textarea").send_keys(prompt)
driver.find_element(By.CSS_SELECTOR, "button.send-button").click()
driver.find_element(By.CSS_SELECTOR, "div.ql-editor.textarea").send_keys(prompt)
driver.find_element(By.CSS_SELECTOR, "div.ql-editor.textarea").send_keys(Keys.ENTER)

# Yield response
while True:
Expand Down
6 changes: 2 additions & 4 deletions g4f/Provider/needs_auth/HuggingChat.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

class HuggingChat(AsyncGeneratorProvider):
url = "https://huggingface.co/chat"
needs_auth = True
working = True
model = "meta-llama/Llama-2-70b-chat-hf"

Expand All @@ -22,12 +21,11 @@ async def create_async_generator(
messages: Messages,
stream: bool = True,
proxy: str = None,
web_search: bool = False,
cookies: dict = None,
**kwargs
) -> AsyncResult:
model = model if model else cls.model
if proxy and "://" not in proxy:
proxy = f"http://{proxy}"
if not cookies:
cookies = get_cookies(".huggingface.co")

Expand All @@ -46,7 +44,7 @@ async def create_async_generator(
"inputs": format_prompt(messages),
"is_retry": False,
"response_id": str(uuid.uuid4()),
"web_search": False
"web_search": web_search
}
async with session.post(f"{cls.url}/conversation/{conversation_id}", json=send, proxy=proxy) as response:
async for line in response.content:
Expand Down
37 changes: 15 additions & 22 deletions g4f/Provider/needs_auth/OpenaiChat.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
from __future__ import annotations

import uuid, json, asyncio
import uuid, json, asyncio, os
from py_arkose_generator.arkose import get_values_for_request
from asyncstdlib.itertools import tee
from async_property import async_cached_property

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

from ..base_provider import AsyncGeneratorProvider
from ..helper import get_event_loop
from ..helper import get_event_loop, format_prompt
from ...webdriver import get_browser
from ...typing import AsyncResult, Messages
from ...requests import StreamSession
Expand Down Expand Up @@ -84,7 +87,12 @@ async def create_async_generator(
if not parent_id:
parent_id = str(uuid.uuid4())
if not access_token:
access_token = await cls.get_access_token(proxy)
access_token = cls._access_token
if not access_token:
login_url = os.environ.get("G4F_LOGIN_URL")
if login_url:
yield f"Please login: [ChatGPT]({login_url})\n\n"
access_token = cls._access_token = await cls.browse_access_token(proxy)
headers = {
"Accept": "text/event-stream",
"Authorization": f"Bearer {access_token}",
Expand All @@ -106,10 +114,11 @@ async def create_async_generator(
"history_and_training_disabled": history_disabled and not auto_continue,
}
if action != "continue":
prompt = format_prompt(messages) if not conversation_id else messages[-1]["content"]
data["messages"] = [{
"id": str(uuid.uuid4()),
"author": {"role": "user"},
"content": {"content_type": "text", "parts": [messages[-1]["content"]]},
"content": {"content_type": "text", "parts": [prompt]},
}]
async with session.post(f"{cls.url}/backend-api/conversation", json=data) as response:
try:
Expand Down Expand Up @@ -155,14 +164,7 @@ async def create_async_generator(
@classmethod
async def browse_access_token(cls, proxy: str = None) -> str:
def browse() -> str:
try:
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_browser(proxy=proxy)
except ImportError:
return
driver = get_browser(proxy=proxy)
try:
driver.get(f"{cls.url}/")
WebDriverWait(driver, 1200).until(
Expand All @@ -177,15 +179,6 @@ def browse() -> str:
None,
browse
)

@classmethod
async def get_access_token(cls, proxy: str = None) -> str:
if not cls._access_token:
cls._access_token = await cls.browse_access_token(proxy)
if not cls._access_token:
raise RuntimeError("Read access token failed")
return cls._access_token


async def get_arkose_token(proxy: str = None, timeout: int = None) -> str:
config = {
Expand Down
15 changes: 9 additions & 6 deletions g4f/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ def get_model_and_provider(model : Union[Model, str],
provider : Union[type[BaseProvider], None],
stream : bool,
ignored : List[str] = None,
ignore_working: bool = False) -> tuple[Model, type[BaseProvider]]:
ignore_working: bool = False,
ignore_stream: bool = False) -> tuple[Model, type[BaseProvider]]:

if isinstance(model, str):
if model in ModelUtils.convert:
Expand All @@ -45,7 +46,7 @@ def get_model_and_provider(model : Union[Model, str],
if not provider.working and not ignore_working:
raise RuntimeError(f'{provider.__name__} is not working')

if not provider.supports_stream and stream:
if not ignore_stream and not provider.supports_stream and stream:
raise ValueError(f'{provider.__name__} does not support "stream" argument')

if debug.logging:
Expand All @@ -61,15 +62,17 @@ def create(model : Union[Model, str],
stream : bool = False,
auth : Union[str, None] = None,
ignored : List[str] = None,
ignore_working: bool = False, **kwargs) -> Union[CreateResult, str]:
ignore_working: bool = False,
ignore_stream_and_auth: bool = False,
**kwargs) -> Union[CreateResult, str]:

model, provider = get_model_and_provider(model, provider, stream, ignored, ignore_working)
model, provider = get_model_and_provider(model, provider, stream, ignored, ignore_working, ignore_stream_and_auth)

if provider.needs_auth and not auth:
if not ignore_stream_and_auth and provider.needs_auth and not auth:
raise ValueError(
f'{provider.__name__} requires authentication (use auth=\'cookie or token or jwt ...\' param)')

if provider.needs_auth:
if auth:
kwargs['auth'] = auth

result = provider.create_completion(model.name, messages, stream, **kwargs)
Expand Down
Loading

0 comments on commit 3576dee

Please sign in to comment.