返回提交历史
Modified
README.md
+7
-3
Modified
docs/async_client.md
+1
-1
Added
docs/authentication.md
+139
-0
Modified
docs/client.md
+2
-2
Renamed
docs/images/cat.jpeg
+0
-0
Renamed
docs/images/cat.webp
+0
-0
Renamed
docs/images/waterfall.jpeg
+0
-0
Modified
g4f/Provider/Blackbox2.py
+4
-3
Modified
g4f/Provider/needs_auth/OpenaiChat.py
+2
-1
Added
g4f/__main__.py
+9
-0
Modified
g4f/api/__init__.py
+55
-15
Modified
g4f/cli.py
+11
-6
Modified
g4f/gui/client/static/js/chat.v1.js
+32
-18
Modified
g4f/gui/server/backend.py
+1
-1
XFEstudio/gpt4free
Add Authentication Setup Guide
fc4fe211
代码差异
14 个文件
+263
-50
@@ -107,7 +107,7 @@ docker run \
107
107
hlohaus789/g4f:latest-slim \
108
108
rm -r -f /app/g4f/ \
109
109
&& pip install -U g4f[slim] \
110
&& python -m g4f.cli api --gui --debug
110
&& python -m g4f --debug
111
111
```
112
112
It also updates the `g4f` package at startup and installs any new required dependencies.
113
113
@@ -134,7 +134,7 @@ By following these steps, you should be able to successfully install and run the
134
134
135
135
Run the **Webview UI** on other Platforms:
136
136
137
- [/docs/guides/webview](docs/webview.md)
137
- [/docs/webview](docs/webview.md)
138
138
139
139
##### Use your smartphone:
140
140
@@ -204,7 +204,7 @@ image_url = response.data[0].url
204
204
print(f"Generated image URL: {image_url}")
205
205
```
206
206
207
[](docs/client.md)
207
[](docs/client.md)
208
208
209
209
#### **Full Documentation for Python API**
210
210
- **New:**
@@ -241,6 +241,10 @@ This API is designed for straightforward implementation and enhanced compatibili
241
241
242
242
### Configuration
243
243
244
#### Authentication
245
246
Refer to the [G4F Authentication Setup Guide](docs/authentication.md) for detailed instructions on setting up authentication.
247
244
248
#### Cookies
245
249
246
250
Cookies are essential for using Meta AI and Microsoft Designer to create images.
@@ -145,7 +145,7 @@ async def main():
145
145
provider=g4f.Provider.CopilotAccount
146
146
)
147
147
148
image = requests.get("https://raw.githubusercontent.com/xtekky/gpt4free/refs/heads/main/docs/cat.jpeg", stream=True).raw
148
image = requests.get("https://raw.githubusercontent.com/xtekky/gpt4free/refs/heads/main/docs/images/cat.jpeg", stream=True).raw
149
149
150
150
response = await client.chat.completions.create(
151
151
model=g4f.models.default,
@@ -0,0 +1,139 @@
1
# G4F Authentication Setup Guide
2
3
This documentation explains how to set up Basic Authentication for the GUI and API key authentication for the API when running the G4F server.
4
5
## Prerequisites
6
7
Before proceeding, ensure you have the following installed:
8
- Python 3.x
9
- G4F package installed (ensure it is set up and working)
10
- Basic knowledge of using environment variables on your operating system
11
12
## Steps to Set Up Authentication
13
14
### 1. API Key Authentication for Both GUI and API
15
16
To secure both the GUI and the API, you'll authenticate using an API key. The API key should be injected via an environment variable and passed to both the GUI (via Basic Authentication) and the API.
17
18
#### Steps to Inject the API Key Using Environment Variables:
19
20
1. **Set the environment variable** for your API key:
21
22
On Linux/macOS:
23
```bash
24
export G4F_API_KEY="your-api-key-here"
25
```
26
27
On Windows (Command Prompt):
28
```bash
29
set G4F_API_KEY="your-api-key-here"
30
```
31
32
On Windows (PowerShell):
33
```bash
34
$env:G4F_API_KEY="your-api-key-here"
35
```
36
37
Replace `your-api-key-here` with your actual API key.
38
39
2. **Run the G4F server with the API key injected**:
40
41
Use the following command to start the G4F server. The API key will be passed to both the GUI and the API:
42
43
```bash
44
python -m g4f --debug --port 8080 --g4f-api-key $G4F_API_KEY
45
```
46
47
- `--debug` enables debug mode for more verbose logs.
48
- `--port 8080` specifies the port on which the server will run (you can change this if needed).
49
- `--g4f-api-key` specifies the API key for both the GUI and the API.
50
51
#### Example:
52
53
```bash
54
export G4F_API_KEY="my-secret-api-key"
55
python -m g4f --debug --port 8080 --g4f-api-key $G4F_API_KEY
56
```
57
58
Now, both the GUI and API will require the correct API key for access.
59
60
---
61
62
### 2. Accessing the GUI with Basic Authentication
63
64
The GUI uses **Basic Authentication**, where the **username** can be any value, and the **password** is your API key.
65
66
#### Example:
67
68
To access the GUI, open your web browser and navigate to `http://localhost:8080/chat/`. You will be prompted for a username and password.
69
70
- **Username**: You can use any username (e.g., `user` or `admin`).
71
- **Password**: Enter your API key (the same key you set in the `G4F_API_KEY` environment variable).
72
73
---
74
75
### 3. Python Example for Accessing the API
76
77
To interact with the API, you can send requests by including the `g4f-api-key` in the headers. Here's an example of how to do this using the `requests` library in Python.
78
79
#### Example Code to Send a Request:
80
81
```python
82
import requests
83
84
url = "http://localhost:8080/v1/chat/completions"
85
86
# Body of the request
87
body = {
88
"model": "your-model-name", # Replace with your model name
89
"provider": "your-provider", # Replace with the provider name
90
"messages": [
91
{
92
"role": "user",
93
"content": "Hello"
94
}
95
]
96
}
97
98
# API Key (can be set as an environment variable)
99
api_key = "your-api-key-here" # Replace with your actual API key
100
101
# Send the POST request
102
response = requests.post(url, json=body, headers={"g4f-api-key": api_key})
103
104
# Check the response
105
print(response.status_code)
106
print(response.json())
107
```
108
109
In this example:
110
- Replace `"your-api-key-here"` with your actual API key.
111
- `"model"` and `"provider"` should be replaced with the appropriate model and provider you're using.
112
- The `messages` array contains the conversation you want to send to the API.
113
114
#### Response:
115
116
The response will contain the output of the API request, such as the model's completion or other relevant data, which you can then process in your application.
117
118
---
119
120
### 4. Testing the Setup
121
122
- **Accessing the GUI**: Open a web browser and navigate to `http://localhost:8080/chat/`. The GUI will now prompt you for a username and password. You can enter any username (e.g., `admin`), and for the password, enter the API key you set up in the environment variable.
123
124
- **Accessing the API**: Use the Python code example above to send requests to the API. Ensure the correct API key is included in the `g4f-api-key` header.
125
126
---
127
128
### 5. Troubleshooting
129
130
- **GUI Access Issues**: If you're unable to access the GUI, ensure that you are using the correct API key as the password.
131
- **API Access Issues**: If the API is rejecting requests, verify that the `G4F_API_KEY` environment variable is correctly set and passed to the server. You can also check the server logs for more detailed error messages.
132
133
---
134
135
## Summary
136
137
By following the steps above, you will have successfully set up Basic Authentication for the G4F GUI (using any username and the API key as the password) and API key authentication for the API. This ensures that only authorized users can access both the interface and make API requests.
138
139
[Return to Home](/)
@@ -181,7 +181,7 @@ client = Client(
181
181
)
182
182
183
183
response = client.images.create_variation(
184
image=open("cat.jpg", "rb"),
184
image=open("docs/images/cat.jpg", "rb"),
185
185
model="dall-e-3",
186
186
# Add any other necessary parameters
187
187
)
@@ -235,7 +235,7 @@ client = Client(
235
235
)
236
236
237
237
image = requests.get("https://raw.githubusercontent.com/xtekky/gpt4free/refs/heads/main/docs/cat.jpeg", stream=True).raw
238
# Or: image = open("docs/cat.jpeg", "rb")
238
# Or: image = open("docs/images/cat.jpeg", "rb")
239
239
240
240
response = client.chat.completions.create(
241
241
model=g4f.models.default,
二进制文件已变更,无法进行逐行预览。
二进制文件已变更,无法进行逐行预览。
二进制文件已变更,无法进行逐行预览。
@@ -6,6 +6,7 @@ from aiohttp import ClientSession
6
6
7
7
from ..typing import AsyncResult, Messages
8
8
from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
9
from .. import debug
9
10
10
11
class Blackbox2(AsyncGeneratorProvider, ProviderModelMixin):
11
12
url = "https://www.blackbox.ai"
@@ -13,7 +14,7 @@ class Blackbox2(AsyncGeneratorProvider, ProviderModelMixin):
13
14
working = True
14
15
supports_system_message = True
15
16
supports_message_history = True
16
17
supports_stream = False
17
18
default_model = 'llama-3.1-70b'
18
19
models = [default_model]
19
20
@@ -62,8 +63,8 @@ class Blackbox2(AsyncGeneratorProvider, ProviderModelMixin):
62
63
raise KeyError("'prompt' key not found in the response")
63
64
except Exception as e:
64
65
if attempt == max_retries - 1:
65
yield f"Error after {max_retries} attempts: {str(e)}"
66
raise RuntimeError(f"Error after {max_retries} attempts: {str(e)}")
66
67
else:
67
68
wait_time = delay * (2 ** attempt) + random.uniform(0, 1)
68
print(f"Attempt {attempt + 1} failed. Retrying in {wait_time:.2f} seconds...")
69
debug.log(f"Attempt {attempt + 1} failed. Retrying in {wait_time:.2f} seconds...")
69
70
await asyncio.sleep(wait_time)
@@ -305,7 +305,7 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
305
305
if not cls.needs_auth:
306
306
cls._create_request_args(cookies)
307
307
RequestConfig.proof_token = get_config(cls._headers.get("user-agent"))
308
async with session.get(cls.url, headers=INIT_HEADERS) as response:
308
async with session.get(cls.url, headers=cls._headers) as response:
309
309
cls._update_request_args(session)
310
310
await raise_for_status(response)
311
311
try:
@@ -538,6 +538,7 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
538
538
await page.send(nodriver.cdp.network.enable())
539
539
page.add_handler(nodriver.cdp.network.RequestWillBeSent, on_request)
540
540
page = await browser.get(cls.url)
541
await asyncio.sleep(1)
541
542
body = await page.evaluate("JSON.stringify(window.__remixContext)")
542
543
if body:
543
544
match = re.search(r'"accessToken":"(.*?)"', body)
@@ -0,0 +1,9 @@
1
from __future__ import annotations
2
3
from .cli import get_api_parser, run_api_args
4
5
parser = get_api_parser()
6
args = parser.parse_args()
7
if args.gui is None:
8
args.gui = True
9
run_api_args(args)
@@ -23,7 +23,7 @@ from starlette.status import (
23
23
HTTP_500_INTERNAL_SERVER_ERROR,
24
24
)
25
25
from fastapi.encoders import jsonable_encoder
26
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
26
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials, HTTPBasic
27
27
from fastapi.middleware.cors import CORSMiddleware
28
28
from starlette.responses import FileResponse
29
29
from pydantic import BaseModel, Field
@@ -50,7 +50,7 @@ logger = logging.getLogger(__name__)
50
50
51
51
DEFAULT_PORT = 1337
52
52
53
def create_app(g4f_api_key: str = None):
53
def create_app():
54
54
app = FastAPI()
55
55
56
56
# Add CORS middleware
@@ -62,7 +62,7 @@ def create_app(g4f_api_key: str = None):
62
62
allow_headers=["*"],
63
63
)
64
64
65
api = Api(app, g4f_api_key=g4f_api_key)
65
api = Api(app)
66
66
67
67
if AppConfig.gui:
68
68
@app.get("/")
@@ -86,9 +86,14 @@ def create_app(g4f_api_key: str = None):
86
86
87
87
return app
88
88
89
def create_app_debug(g4f_api_key: str = None):
89
def create_app_debug():
90
90
g4f.debug.logging = True
91
return create_app(g4f_api_key)
91
return create_app()
92
93
def create_app_with_gui_and_debug():
94
g4f.debug.logging = True
95
AppConfig.gui = True
96
return create_app()
92
97
93
98
class ChatCompletionsConfig(BaseModel):
94
99
messages: Messages = Field(examples=[[{"role": "system", "content": ""}, {"role": "user", "content": ""}]])
@@ -156,8 +161,8 @@ class ErrorResponse(Response):
156
161
return cls(format_exception(exception, config), status_code)
157
162
158
163
@classmethod
159
def from_message(cls, message: str, status_code: int = HTTP_500_INTERNAL_SERVER_ERROR):
160
return cls(format_exception(message), status_code)
164
def from_message(cls, message: str, status_code: int = HTTP_500_INTERNAL_SERVER_ERROR, headers: dict = None):
165
return cls(format_exception(message), status_code, headers=headers)
161
166
162
167
def render(self, content) -> bytes:
163
168
return str(content).encode(errors="ignore")
@@ -184,26 +189,57 @@ def set_list_ignored_providers(ignored: list[str]):
184
189
list_ignored_providers = ignored
185
190
186
191
class Api:
187
def __init__(self, app: FastAPI, g4f_api_key=None) -> None:
192
def __init__(self, app: FastAPI) -> None:
188
193
self.app = app
189
194
self.client = AsyncClient()
190
self.g4f_api_key = g4f_api_key
191
195
self.get_g4f_api_key = APIKeyHeader(name="g4f-api-key")
192
196
self.conversations: dict[str, dict[str, BaseConversation]] = {}
193
197
194
198
security = HTTPBearer(auto_error=False)
199
basic_security = HTTPBasic()
200
201
async def get_username(self, request: Request):
202
credentials = await self.basic_security(request)
203
current_password_bytes = credentials.password.encode()
204
is_correct_password = secrets.compare_digest(
205
current_password_bytes, AppConfig.g4f_api_key.encode()
206
)
207
if not is_correct_password:
208
raise HTTPException(
209
status_code=HTTP_401_UNAUTHORIZED,
210
detail="Incorrect username or password",
211
headers={"WWW-Authenticate": "Basic"},
212
)
213
return credentials.username
195
214
196
215
def register_authorization(self):
216
if AppConfig.g4f_api_key:
217
print(f"Register authentication key: {''.join(['*' for _ in range(len(AppConfig.g4f_api_key))])}")
197
218
@self.app.middleware("http")
198
219
async def authorization(request: Request, call_next):
199
if self.g4f_api_key and request.url.path not in ("/", "/v1"):
220
if AppConfig.g4f_api_key is not None:
200
221
try:
201
222
user_g4f_api_key = await self.get_g4f_api_key(request)
202
except HTTPException as e:
203
if e.status_code == 403:
223
except HTTPException:
224
user_g4f_api_key = None
225
if request.url.path.startswith("/v1"):
226
if user_g4f_api_key is None:
204
227
return ErrorResponse.from_message("G4F API key required", HTTP_401_UNAUTHORIZED)
205
if not secrets.compare_digest(self.g4f_api_key, user_g4f_api_key):
206
return ErrorResponse.from_message("Invalid G4F API key", HTTP_403_FORBIDDEN)
228
if not secrets.compare_digest(AppConfig.g4f_api_key, user_g4f_api_key):
229
return ErrorResponse.from_message("Invalid G4F API key", HTTP_403_FORBIDDEN)
230
else:
231
path = request.url.path
232
if user_g4f_api_key is not None and path.startswith("/images/"):
233
if not secrets.compare_digest(AppConfig.g4f_api_key, user_g4f_api_key):
234
return ErrorResponse.from_message("Invalid G4F API key", HTTP_403_FORBIDDEN)
235
elif path.startswith("/backend-api/") or path.startswith("/images/") or path.startswith("/chat/") and path != "/chat/":
236
try:
237
username = await self.get_username(request)
238
except HTTPException as e:
239
return ErrorResponse.from_message(e.detail, e.status_code, e.headers)
240
response = await call_next(request)
241
response.headers["X-Username"] = username
242
return response
207
243
return await call_next(request)
208
244
209
245
def register_validation_exception_handler(self):
@@ -512,8 +548,12 @@ def run_api(
512
548
host, port = bind.split(":")
513
549
if port is None:
514
550
port = DEFAULT_PORT
551
if AppConfig.gui and debug:
552
method = "create_app_with_gui_and_debug"
553
else:
554
method = "create_app_debug" if debug else "create_app"
515
555
uvicorn.run(
516
f"g4f.api:create_app{'_debug' if debug else ''}",
556
f"g4f.api:{method}",
517
557
host=host,
518
558
port=int(port),
519
559
workers=workers,
@@ -1,19 +1,18 @@
1
1
from __future__ import annotations
2
2
3
3
import argparse
4
from argparse import ArgumentParser
4
5
5
6
from g4f import Provider
6
7
from g4f.gui.run import gui_parser, run_gui_args
7
8
import g4f.cookies
8
9
9
def main():
10
parser = argparse.ArgumentParser(description="Run gpt4free")
11
subparsers = parser.add_subparsers(dest="mode", help="Mode to run the g4f in.")
12
api_parser = subparsers.add_parser("api")
10
def get_api_parser():
11
api_parser = ArgumentParser(description="Run the API and GUI")
13
12
api_parser.add_argument("--bind", default=None, help="The bind string. (Default: 0.0.0.0:1337)")
14
api_parser.add_argument("--port", default=None, help="Change the port of the server.")
13
api_parser.add_argument("--port", "-p", default=None, help="Change the port of the server.")
15
14
api_parser.add_argument("--debug", "-d", action="store_true", help="Enable verbose logging.")
16
api_parser.add_argument("--gui", "-g", default=False, action="store_true", help="Add gui to the api.")
15
api_parser.add_argument("--gui", "-g", default=None, action="store_true", help="Add gui to the api.")
17
16
api_parser.add_argument("--model", default=None, help="Default model for chat completion. (incompatible with --reload and --workers)")
18
17
api_parser.add_argument("--provider", choices=[provider.__name__ for provider in Provider.__providers__ if provider.working],
19
18
default=None, help="Default provider for chat completion. (incompatible with --reload and --workers)")
@@ -29,6 +28,12 @@ def main():
29
28
api_parser.add_argument("--cookie-browsers", nargs="+", choices=[browser.__name__ for browser in g4f.cookies.browsers],
30
29
default=[], help="List of browsers to access or retrieve cookies from. (incompatible with --reload and --workers)")
31
30
api_parser.add_argument("--reload", action="store_true", help="Enable reloading.")
31
return api_parser
32
33
def main():
34
parser = argparse.ArgumentParser(description="Run gpt4free")
35
subparsers = parser.add_subparsers(dest="mode", help="Mode to run the g4f in.")
36
subparsers.add_parser("api", parents=[get_api_parser()], add_help=False)
32
37
subparsers.add_parser("gui", parents=[gui_parser()], add_help=False)
33
38
34
39
args = parser.parse_args()
@@ -744,7 +744,11 @@ const delete_conversation = async (conversation_id) => {
744
744
};
745
745
746
746
const set_conversation = async (conversation_id) => {
747
history.pushState({}, null, `/chat/${conversation_id}`);
747
try {
748
history.pushState({}, null, `/chat/${conversation_id}`);
749
} catch (e) {
750
console.error(e);
751
}
748
752
window.conversation_id = conversation_id;
749
753
750
754
await clear_conversation();
@@ -898,7 +902,11 @@ async function add_conversation(conversation_id, content) {
898
902
items: [],
899
903
});
900
904
}
901
history.pushState({}, null, `/chat/${conversation_id}`);
905
try {
906
history.pushState({}, null, `/chat/${conversation_id}`);
907
} catch (e) {
908
console.error(e);
909
}
902
910
}
903
911
904
912
async function save_system_message() {
@@ -1287,23 +1295,29 @@ async function on_api() {
1287
1295
1288
1296
register_settings_storage();
1289
1297
1290
models = await api("models");
1291
models.forEach((model) => {
1292
let option = document.createElement("option");
1293
option.value = option.text = model;
1294
modelSelect.appendChild(option);
1295
});
1296
1297
providers = await api("providers")
1298
Object.entries(providers).forEach(([provider, label]) => {
1299
let option = document.createElement("option");
1300
option.value = provider;
1301
option.text = label;
1302
providerSelect.appendChild(option);
1303
})
1298
try {
1299
models = await api("models");
1300
models.forEach((model) => {
1301
let option = document.createElement("option");
1302
option.value = option.text = model;
1303
modelSelect.appendChild(option);
1304
});
1305
providers = await api("providers")
1306
Object.entries(providers).forEach(([provider, label]) => {
1307
let option = document.createElement("option");
1308
option.value = provider;
1309
option.text = label;
1310
providerSelect.appendChild(option);
1311
});
1312
await load_provider_models(appStorage.getItem("provider"));
1313
} catch (e) {
1314
console.error(e)
1315
if (document.location.pathname == "/chat/") {
1316
document.location.href = `/chat/error`;
1317
}
1318
}
1304
1319
1305
1320
await load_settings_storage()
1306
await load_provider_models(appStorage.getItem("provider"));
1307
1321
1308
1322
const hide_systemPrompt = document.getElementById("hide-systemPrompt")
1309
1323
const slide_systemPrompt_icon = document.querySelector(".slide-systemPrompt i");
@@ -1465,7 +1479,7 @@ async function api(ressource, args=null, file=null, message_id=null) {
1465
1479
const url = `/backend-api/v2/${ressource}`;
1466
1480
const headers = {};
1467
1481
if (api_key) {
1468
headers.authorization = `Bearer ${api_key}`;
1482
headers.x_api_key = api_key;
1469
1483
}
1470
1484
if (ressource == "conversation") {
1471
1485
let body = JSON.stringify(args);
@@ -153,7 +153,7 @@ class Backend_Api(Api):
153
153
return response
154
154
155
155
def get_provider_models(self, provider: str):
156
api_key = None if request.authorization is None else request.authorization.token
156
api_key = request.headers.get("x_api_key")
157
157
models = super().get_provider_models(provider, api_key)
158
158
if models is None:
159
159
return "Provider not found", 404