返回提交历史
Modified
docker/Dockerfile-slim
+1
-1
Modified
docker/update.sh
+1
-1
Modified
g4f/api/__init__.py
+16
-5
Modified
g4f/gui/server/backend_api.py
+7
-0
Modified
g4f/tools/run_tools.py
+31
-9
XFEstudio/gpt4free
Improve logging
6106ca95
代码差异
5 个文件
+56
-16
@@ -36,4 +36,4 @@ RUN git clone https://github.com/hlohaus/deepseek4free.git \
36
36
&& cd deepseek4free && git checkout 21Feb \
37
37
&& pip install --no-cache-dir . && cd .. && rm -rf deepseek4free
38
38
39
CMD git pull origin main && docker/update.sh & docker/start.sh
39
CMD python -m etc.tool.update && docker/update.sh & docker/start.sh
@@ -15,7 +15,7 @@ echo "UPDATE: d$c"
15
15
git pull origin main
16
16
sleep 120
17
17
echo "UPDATE: #$c"
18
python -m etc.tool.update
18
git pull origin main
19
19
sleep 120
20
20
done
21
21
echo "STOPPED."
@@ -188,6 +188,13 @@ class AppConfig:
188
188
for key, value in data.items():
189
189
setattr(cls, key, value)
190
190
191
def remove_authorization(request: Request) -> Request:
192
new_header = request.headers.mutablecopy()
193
del new_header["authorization"]
194
request.scope["headers"] = new_header.raw
195
delattr(request, "_headers")
196
return request
197
191
198
class Api:
192
199
def __init__(self, app: FastAPI) -> None:
193
200
self.app = app
@@ -220,14 +227,16 @@ class Api:
220
227
session_key = get_session_key()
221
228
@self.app.middleware("http")
222
229
async def authorization(request: Request, call_next):
230
user = None
223
231
if AppConfig.g4f_api_key is not None or AppConfig.demo:
232
is_authorization_header = False
224
233
try:
225
234
user_g4f_api_key = await self.get_g4f_api_key(request)
226
235
except HTTPException:
227
236
user_g4f_api_key = await self.security(request)
228
237
if hasattr(user_g4f_api_key, "credentials"):
229
238
user_g4f_api_key = user_g4f_api_key.credentials
230
user = None
239
is_authorization_header = True
231
240
if AppConfig.g4f_api_key is None or not secrets.compare_digest(AppConfig.g4f_api_key, user_g4f_api_key):
232
241
if has_crypto and user_g4f_api_key:
233
242
try:
@@ -260,10 +269,12 @@ class Api:
260
269
user = await self.get_username(request)
261
270
except HTTPException as e:
262
271
return ErrorResponse.from_message(e.detail, e.status_code, e.headers)
263
response = await call_next(request)
264
response.headers["x-user"] = user
265
return response
266
return await call_next(request)
272
if is_authorization_header:
273
request = remove_authorization(request)
274
response = await call_next(request)
275
if user is not None:
276
response.headers["x_user"] = user
277
return response
267
278
268
279
def register_validation_exception_handler(self):
269
280
@self.app.exception_handler(RequestValidationError)
@@ -214,6 +214,13 @@ class Backend_Api(Api):
214
214
with cache_file.open("a" if cache_file.exists() else "w") as f:
215
215
f.write(f"{json.dumps(request.json)}\n")
216
216
return {}
217
218
@app.route('/backend-api/v2/usage/<date>', methods=['GET'])
219
def get_usage(date: str):
220
cache_dir = Path(get_cookies_dir()) / ".usage"
221
cache_file = cache_dir / f"{date}.jsonl"
222
print(f"Loading usage data from {cache_file}")
223
return cache_file.read_text() if cache_file.exists() else (jsonify({"error": {"message": "No usage data found for this date"}}), 404)
217
224
218
225
@app.route('/backend-api/v2/log', methods=['POST'])
219
226
def add_log():
@@ -5,13 +5,14 @@ import re
5
5
import json
6
6
import asyncio
7
7
import time
8
import datetime
8
9
from pathlib import Path
9
from typing import Optional, Callable, AsyncIterator, Iterator, Dict, Any, Tuple, List, Union
10
from typing import Optional, AsyncIterator, Iterator, Dict, Any, Tuple, List, Union
10
11
11
12
from ..typing import Messages
12
13
from ..providers.helper import filter_none
13
14
from ..providers.asyncio import to_async_iterator
14
from ..providers.response import Reasoning, FinishReason, Sources
15
from ..providers.response import Reasoning, FinishReason, Sources, Usage, ProviderInfo
15
16
from ..providers.types import ProviderType
16
17
from ..cookies import get_cookies_dir
17
18
from .web_search import do_search, get_search_message
@@ -141,7 +142,7 @@ class AuthManager:
141
142
env_var = f"{cls.aliases[provider_name].upper()}_API_KEY"
142
143
api_key = os.environ.get(env_var)
143
144
if api_key:
144
debug.log(f"Loading API key from environment variable {env_var}")
145
print(f"Loading API key for {provider_name} from environment variable {env_var}")
145
146
return api_key
146
147
return None
147
148
@@ -236,9 +237,10 @@ async def async_iter_run_tools(
236
237
messages, sources = await perform_web_search(messages, web_search)
237
238
238
239
# Get API key
239
api_key = AuthManager.load_api_key(provider)
240
if api_key:
241
kwargs["api_key"] = api_key
240
if not kwargs.get("api_key"):
241
api_key = AuthManager.load_api_key(provider)
242
if api_key:
243
kwargs["api_key"] = api_key
242
244
243
245
# Process tool calls
244
246
if tool_calls:
@@ -248,9 +250,19 @@ async def async_iter_run_tools(
248
250
# Generate response
249
251
response = to_async_iterator(provider.async_create_function(model=model, messages=messages, **kwargs))
250
252
253
model_info = model
251
254
async for chunk in response:
255
if isinstance(chunk, ProviderInfo):
256
model_info = getattr(chunk, 'model', model_info)
257
elif isinstance(chunk, Usage):
258
usage = {"user": kwargs.get("user"), "model": model_info, "provider": provider.get_parent(), **chunk.get_dict()}
259
usage_dir = Path(get_cookies_dir()) / ".usage"
260
usage_file = usage_dir / f"{datetime.date.today()}.jsonl"
261
usage_dir.mkdir(parents=True, exist_ok=True)
262
with usage_file.open("a" if usage_file.exists() else "w") as f:
263
f.write(f"{json.dumps(usage)}\n")
252
264
yield chunk
253
265
254
266
# Yield sources if available
255
267
if sources:
256
268
yield sources
@@ -277,7 +289,7 @@ def iter_run_tools(
277
289
debug.error(f"Couldn't do web search:", e)
278
290
279
291
# Get API key if needed
280
if provider is not None:
292
if not kwargs.get("api_key"):
281
293
api_key = AuthManager.load_api_key(provider)
282
294
if api_key:
283
295
kwargs["api_key"] = api_key
@@ -321,7 +333,7 @@ def iter_run_tools(
321
333
# Process response chunks
322
334
thinking_start_time = 0
323
335
processor = ThinkingProcessor()
324
336
model_info = model
325
337
for chunk in provider.create_function(model=model, messages=messages, provider=provider, **kwargs):
326
338
if isinstance(chunk, FinishReason):
327
339
if sources is not None:
@@ -331,6 +343,16 @@ def iter_run_tools(
331
343
continue
332
344
elif isinstance(chunk, Sources):
333
345
sources = None
346
elif isinstance(chunk, ProviderInfo):
347
model_info = getattr(chunk, 'model', model_info)
348
elif isinstance(chunk, Usage):
349
usage = {"user": kwargs.get("user"), "model": model_info, "provider": provider.get_parent(), **chunk.get_dict()}
350
usage_dir = Path(get_cookies_dir()) / ".usage"
351
usage_file = usage_dir / f"{datetime.date.today()}.jsonl"
352
usage_dir.mkdir(parents=True, exist_ok=True)
353
with usage_file.open("a" if usage_file.exists() else "w") as f:
354
f.write(f"{json.dumps(usage)}\n")
355
334
356
if not isinstance(chunk, str):
335
357
yield chunk
336
358
continue