返回提交历史
Deleted
Dockerfile
+0
-33
Modified
README.md
+1
-1
Modified
docker-compose.yml
+7
-8
Added
docker/Dockerfile
+42
-0
Added
docker/start-selenium-node.sh
+17
-0
Added
g4f.png
+0
-0
Modified
g4f/Provider/Bing.py
+5
-2
Modified
g4f/Provider/PerplexityAi.py
+4
-5
Modified
g4f/Provider/helper.py
+7
-2
Modified
g4f/Provider/needs_auth/Bard.py
+10
-6
Modified
g4f/Provider/needs_auth/HuggingChat.py
+2
-4
Modified
g4f/Provider/needs_auth/OpenaiChat.py
+15
-22
Modified
g4f/__init__.py
+9
-6
Modified
g4f/gui/client/js/chat.v1.js
+35
-60
Modified
g4f/gui/server/backend.py
+25
-19
Deleted
g4f/gui/server/provider.py
+0
-14
Modified
g4f/models.py
+2
-1
Modified
g4f/webdriver.py
+8
-2
Deleted
ptest.py
+0
-57
XFEstudio/gpt4free
Add selenium to dockerfile Load model and provider list in gui Remove needs_auth in HuggingChat Add default model and login url in gui
3576dee7
代码差异
19 个文件
+189
-242
@@ -1,33 +0,0 @@
1
# Use the official lightweight Python image.
2
# https://hub.docker.com/_/python
3
FROM python:3.9-slim
4
5
# Ensure Python outputs everything immediately (useful for real-time logging in Docker).
6
ENV PYTHONUNBUFFERED 1
7
8
# Set the working directory in the container.
9
WORKDIR /app
10
11
# Update the system packages and install system-level dependencies required for compilation.
12
# gcc: Compiler required for some Python packages.
13
# build-essential: Contains necessary tools and libraries for building software.
14
RUN apt-get update && apt-get install -y --no-install-recommends \
15
gcc \
16
build-essential \
17
&& rm -rf /var/lib/apt/lists/*
18
19
# Copy the project's requirements file into the container.
20
COPY requirements.txt /app/
21
22
# Upgrade pip for the latest features and install the project's Python dependencies.
23
RUN pip install --upgrade pip && pip install -r requirements.txt
24
25
# Copy the entire project into the container.
26
# This may include all code, assets, and configuration files required to run the application.
27
COPY . /app/
28
29
# Expose port 80 and 1337
30
EXPOSE 80 1337
31
32
# Define the default command to run the app using Python's module mode.
33
ENTRYPOINT ["python", "-m", "g4f.cli"]
@@ -1,4 +1,4 @@
1

1

2
2
3
3
<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' />
4
4
<div id="top"></div>
@@ -1,18 +1,17 @@
1
1
version: '3'
2
2
3
3
services:
4
gpt4free-api: &gpt4free
4
gpt4free:
5
5
image: gpt4free:latest
6
shm_size: 2gb
6
7
build:
7
8
context: .
8
dockerfile: Dockerfile
9
dockerfile: docker/Dockerfile
9
10
cache_from:
10
11
- gpt4free:latest
11
ports:
12
- '1337:1337'
13
command: api
14
gpt4free-gui:
15
<<: *gpt4free
12
volumes:
13
- .:/app
16
14
ports:
17
15
- '8080:80'
18
command: gui
16
- '1337:1337'
17
- '7900:7900'
@@ -0,0 +1,42 @@
1
FROM selenium/node-chrome
2
3
ENV SE_SCREEN_WIDTH 1920
4
ENV G4F_LOGIN_URL http://localhost:7900/?autoconnect=1&resize=scale&password=secret
5
6
USER root
7
8
# Python packages
9
RUN apt-get -qqy update \
10
&& apt-get -qqy install \
11
python3 \
12
python-is-python3 \
13
pip
14
15
# Cleanup
16
RUN rm -rf /var/lib/apt/lists/* /var/cache/apt/* \
17
&& apt-get -qyy autoremove \
18
&& apt-get -qyy clean
19
20
# Update entrypoint
21
COPY docker/start-selenium-node.sh /opt/bin/
22
23
# Change background image
24
COPY g4f.png /usr/share/images/fluxbox/ubuntu-light.png
25
26
# Switch user
27
USER 1200
28
29
# Set the working directory in the container.
30
WORKDIR /app
31
32
# Copy the project's requirements file into the container.
33
COPY requirements.txt /app/
34
35
# Upgrade pip for the latest features and install the project's Python dependencies.
36
RUN pip install --upgrade pip && pip install -r requirements.txt
37
38
# Copy the entire package into the container.
39
COPY g4f /app/g4f
40
41
# Expose ports
42
EXPOSE 80 1337
@@ -0,0 +1,17 @@
1
#!/bin/bash
2
3
# Start the pulseaudio server
4
pulseaudio -D --exit-idle-time=-1
5
6
# Load the virtual sink and set it as default
7
pacmd load-module module-virtual-sink sink_name=v1
8
pacmd set-default-sink v1
9
10
# Set the monitor of v1 sink to be the default source
11
pacmd set-default-source v1.monitor
12
13
rm -f /tmp/.X*lock
14
15
# Start app servers
16
python -m g4f.cli api &
17
python -m g4f.cli gui
二进制文件已变更,无法进行逐行预览。
@@ -156,8 +156,11 @@ async def delete_conversation(session: ClientSession, conversation: Conversation
156
156
"optionsSets": ["autosave"]
157
157
}
158
158
async with session.post(url, json=json, proxy=proxy) as response:
159
response = await response.json()
160
return response["result"]["value"] == "Success"
159
try:
160
response = await response.json()
161
return response["result"]["value"] == "Success"
162
except:
163
return False
161
164
162
165
class Defaults:
163
166
delimiter = "\x1e"
@@ -1,6 +1,10 @@
1
1
from __future__ import annotations
2
2
3
3
import time
4
from selenium.webdriver.common.by import By
5
from selenium.webdriver.support.ui import WebDriverWait
6
from selenium.webdriver.support import expected_conditions as EC
7
from selenium.webdriver.common.keys import Keys
4
8
5
9
from ..typing import CreateResult, Messages
6
10
from .base_provider import BaseProvider
@@ -27,11 +31,6 @@ class PerplexityAi(BaseProvider):
27
31
**kwargs
28
32
) -> CreateResult:
29
33
with WebDriverSession(webdriver, "", virtual_display=virtual_display, proxy=proxy) as driver:
30
from selenium.webdriver.common.by import By
31
from selenium.webdriver.support.ui import WebDriverWait
32
from selenium.webdriver.support import expected_conditions as EC
33
from selenium.webdriver.common.keys import Keys
34
35
34
prompt = format_prompt(messages)
36
35
37
36
driver.get(f"{cls.url}/")
@@ -6,6 +6,7 @@ import webbrowser
6
6
import random
7
7
import string
8
8
import secrets
9
import os
9
10
from os import path
10
11
from asyncio import AbstractEventLoop
11
12
from platformdirs import user_config_dir
@@ -18,7 +19,7 @@ from browser_cookie3 import (
18
19
edge,
19
20
vivaldi,
20
21
firefox,
21
BrowserCookieError
22
_LinuxPasswordManager
22
23
)
23
24
24
25
from ..typing import Dict, Messages
@@ -81,6 +82,10 @@ def init_cookies():
81
82
except webbrowser.Error:
82
83
continue
83
84
85
# Check for broken dbus address in docker image
86
if os.environ.get('DBUS_SESSION_BUS_ADDRESS') == "/dev/null":
87
_LinuxPasswordManager.get_password = lambda a, b: b"secret"
88
84
89
# Load cookies for a domain from all supported browsers.
85
90
# Cache the results in the "_cookies" variable.
86
91
def get_cookies(domain_name=''):
@@ -100,7 +105,7 @@ def get_cookies(domain_name=''):
100
105
for cookie in cookie_jar:
101
106
if cookie.name not in cookies:
102
107
cookies[cookie.name] = cookie.value
103
except BrowserCookieError as e:
108
except:
104
109
pass
105
110
_cookies[domain_name] = cookies
106
111
return _cookies[domain_name]
@@ -1,6 +1,11 @@
1
1
from __future__ import annotations
2
2
3
3
import time
4
import os
5
from selenium.webdriver.common.by import By
6
from selenium.webdriver.support.ui import WebDriverWait
7
from selenium.webdriver.support import expected_conditions as EC
8
from selenium.webdriver.common.keys import Keys
4
9
5
10
from ...typing import CreateResult, Messages
6
11
from ..base_provider import BaseProvider
@@ -27,10 +32,6 @@ class Bard(BaseProvider):
27
32
prompt = format_prompt(messages)
28
33
session = WebDriverSession(webdriver, user_data_dir, headless, proxy=proxy)
29
34
with session as driver:
30
from selenium.webdriver.common.by import By
31
from selenium.webdriver.support.ui import WebDriverWait
32
from selenium.webdriver.support import expected_conditions as EC
33
34
35
try:
35
36
driver.get(f"{cls.url}/chat")
36
37
wait = WebDriverWait(driver, 10 if headless else 240)
@@ -40,6 +41,9 @@ class Bard(BaseProvider):
40
41
if not webdriver:
41
42
driver = session.reopen()
42
43
driver.get(f"{cls.url}/chat")
44
login_url = os.environ.get("G4F_LOGIN_URL")
45
if login_url:
46
yield f"Please login: [Google Bard]({login_url})\n\n"
43
47
wait = WebDriverWait(driver, 240)
44
48
wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "div.ql-editor.textarea")))
45
49
else:
@@ -61,8 +65,8 @@ XMLHttpRequest.prototype.open = function(method, url) {
61
65
driver.execute_script(script)
62
66
63
67
# Submit prompt
64
driver.find_element(By.CSS_SELECTOR, "div.ql-editor.ql-blank.textarea").send_keys(prompt)
65
driver.find_element(By.CSS_SELECTOR, "button.send-button").click()
68
driver.find_element(By.CSS_SELECTOR, "div.ql-editor.textarea").send_keys(prompt)
69
driver.find_element(By.CSS_SELECTOR, "div.ql-editor.textarea").send_keys(Keys.ENTER)
66
70
67
71
# Yield response
68
72
while True:
@@ -11,7 +11,6 @@ from ..helper import format_prompt, get_cookies
11
11
12
12
class HuggingChat(AsyncGeneratorProvider):
13
13
url = "https://huggingface.co/chat"
14
needs_auth = True
15
14
working = True
16
15
model = "meta-llama/Llama-2-70b-chat-hf"
17
16
@@ -22,12 +21,11 @@ class HuggingChat(AsyncGeneratorProvider):
22
21
messages: Messages,
23
22
stream: bool = True,
24
23
proxy: str = None,
24
web_search: bool = False,
25
25
cookies: dict = None,
26
26
**kwargs
27
27
) -> AsyncResult:
28
28
model = model if model else cls.model
29
if proxy and "://" not in proxy:
30
proxy = f"http://{proxy}"
31
29
if not cookies:
32
30
cookies = get_cookies(".huggingface.co")
33
31
@@ -46,7 +44,7 @@ class HuggingChat(AsyncGeneratorProvider):
46
44
"inputs": format_prompt(messages),
47
45
"is_retry": False,
48
46
"response_id": str(uuid.uuid4()),
49
"web_search": False
47
"web_search": web_search
50
48
}
51
49
async with session.post(f"{cls.url}/conversation/{conversation_id}", json=send, proxy=proxy) as response:
52
50
async for line in response.content:
@@ -1,12 +1,15 @@
1
1
from __future__ import annotations
2
2
3
import uuid, json, asyncio
3
import uuid, json, asyncio, os
4
4
from py_arkose_generator.arkose import get_values_for_request
5
5
from asyncstdlib.itertools import tee
6
6
from async_property import async_cached_property
7
7
from selenium.webdriver.common.by import By
8
from selenium.webdriver.support.ui import WebDriverWait
9
from selenium.webdriver.support import expected_conditions as EC
10
8
11
from ..base_provider import AsyncGeneratorProvider
9
from ..helper import get_event_loop
12
from ..helper import get_event_loop, format_prompt
10
13
from ...webdriver import get_browser
11
14
from ...typing import AsyncResult, Messages
12
15
from ...requests import StreamSession
@@ -84,7 +87,12 @@ class OpenaiChat(AsyncGeneratorProvider):
84
87
if not parent_id:
85
88
parent_id = str(uuid.uuid4())
86
89
if not access_token:
87
access_token = await cls.get_access_token(proxy)
90
access_token = cls._access_token
91
if not access_token:
92
login_url = os.environ.get("G4F_LOGIN_URL")
93
if login_url:
94
yield f"Please login: [ChatGPT]({login_url})\n\n"
95
access_token = cls._access_token = await cls.browse_access_token(proxy)
88
96
headers = {
89
97
"Accept": "text/event-stream",
90
98
"Authorization": f"Bearer {access_token}",
@@ -106,10 +114,11 @@ class OpenaiChat(AsyncGeneratorProvider):
106
114
"history_and_training_disabled": history_disabled and not auto_continue,
107
115
}
108
116
if action != "continue":
117
prompt = format_prompt(messages) if not conversation_id else messages[-1]["content"]
109
118
data["messages"] = [{
110
119
"id": str(uuid.uuid4()),
111
120
"author": {"role": "user"},
112
"content": {"content_type": "text", "parts": [messages[-1]["content"]]},
121
"content": {"content_type": "text", "parts": [prompt]},
113
122
}]
114
123
async with session.post(f"{cls.url}/backend-api/conversation", json=data) as response:
115
124
try:
@@ -155,14 +164,7 @@ class OpenaiChat(AsyncGeneratorProvider):
155
164
@classmethod
156
165
async def browse_access_token(cls, proxy: str = None) -> str:
157
166
def browse() -> str:
158
try:
159
from selenium.webdriver.common.by import By
160
from selenium.webdriver.support.ui import WebDriverWait
161
from selenium.webdriver.support import expected_conditions as EC
162
163
driver = get_browser(proxy=proxy)
164
except ImportError:
165
return
167
driver = get_browser(proxy=proxy)
166
168
try:
167
169
driver.get(f"{cls.url}/")
168
170
WebDriverWait(driver, 1200).until(
@@ -177,15 +179,6 @@ class OpenaiChat(AsyncGeneratorProvider):
177
179
None,
178
180
browse
179
181
)
180
181
@classmethod
182
async def get_access_token(cls, proxy: str = None) -> str:
183
if not cls._access_token:
184
cls._access_token = await cls.browse_access_token(proxy)
185
if not cls._access_token:
186
raise RuntimeError("Read access token failed")
187
return cls._access_token
188
189
182
190
183
async def get_arkose_token(proxy: str = None, timeout: int = None) -> str:
191
184
config = {
@@ -25,7 +25,8 @@ def get_model_and_provider(model : Union[Model, str],
25
25
provider : Union[type[BaseProvider], None],
26
26
stream : bool,
27
27
ignored : List[str] = None,
28
ignore_working: bool = False) -> tuple[Model, type[BaseProvider]]:
28
ignore_working: bool = False,
29
ignore_stream: bool = False) -> tuple[Model, type[BaseProvider]]:
29
30
30
31
if isinstance(model, str):
31
32
if model in ModelUtils.convert:
@@ -45,7 +46,7 @@ def get_model_and_provider(model : Union[Model, str],
45
46
if not provider.working and not ignore_working:
46
47
raise RuntimeError(f'{provider.__name__} is not working')
47
48
48
if not provider.supports_stream and stream:
49
if not ignore_stream and not provider.supports_stream and stream:
49
50
raise ValueError(f'{provider.__name__} does not support "stream" argument')
50
51
51
52
if debug.logging:
@@ -61,15 +62,17 @@ class ChatCompletion:
61
62
stream : bool = False,
62
63
auth : Union[str, None] = None,
63
64
ignored : List[str] = None,
64
ignore_working: bool = False, **kwargs) -> Union[CreateResult, str]:
65
ignore_working: bool = False,
66
ignore_stream_and_auth: bool = False,
67
**kwargs) -> Union[CreateResult, str]:
65
68
66
model, provider = get_model_and_provider(model, provider, stream, ignored, ignore_working)
69
model, provider = get_model_and_provider(model, provider, stream, ignored, ignore_working, ignore_stream_and_auth)
67
70
68
if provider.needs_auth and not auth:
71
if not ignore_stream_and_auth and provider.needs_auth and not auth:
69
72
raise ValueError(
70
73
f'{provider.__name__} requires authentication (use auth=\'cookie or token or jwt ...\' param)')
71
74
72
if provider.needs_auth:
75
if auth:
73
76
kwargs['auth'] = auth
74
77
75
78
result = provider.create_completion(model.name, messages, stream, **kwargs)