XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet
公开
关注 0 Fork 0 Star 1
返回提交历史

XFEstudio/gpt4free

Add Authentication Setup Guide

fc4fe211
Heiner Lohaus <hlohaus@users.noreply.github.com>
提交于

代码差异

14 个文件 +263 -50
Modified README.md +7 -3
@@ -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 [![Image with cat](/docs/cat.jpeg)](docs/client.md)
207 [![Image with cat](/docs/images/cat.jpeg)](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.
Modified docs/async_client.md +1 -1
@@ -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,
Added docs/authentication.md +139 -0
@@ -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](/)
Modified docs/client.md +2 -2
@@ -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,
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
@@ -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)
Modified g4f/Provider/needs_auth/OpenaiChat.py +2 -1
@@ -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)
Added g4f/__main__.py +9 -0
@@ -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)
Modified g4f/api/__init__.py +55 -15
@@ -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,
Modified g4f/cli.py +11 -6
@@ -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()
Modified g4f/gui/client/static/js/chat.v1.js +32 -18
@@ -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);
Modified g4f/gui/server/backend.py +1 -1
@@ -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