Improve logging

This commit is contained in:
hlohaus 2025-07-13 20:07:39 +02:00
parent a31cb50d6a
commit 6106ca95c9
5 changed files with 56 additions and 16 deletions

View file

@ -36,4 +36,4 @@ RUN git clone https://github.com/hlohaus/deepseek4free.git \
&& cd deepseek4free && git checkout 21Feb \ && cd deepseek4free && git checkout 21Feb \
&& pip install --no-cache-dir . && cd .. && rm -rf deepseek4free && pip install --no-cache-dir . && cd .. && rm -rf deepseek4free
CMD git pull origin main && docker/update.sh & docker/start.sh CMD python -m etc.tool.update && docker/update.sh & docker/start.sh

View file

@ -15,7 +15,7 @@ echo "UPDATE: d$c"
git pull origin main git pull origin main
sleep 120 sleep 120
echo "UPDATE: #$c" echo "UPDATE: #$c"
python -m etc.tool.update git pull origin main
sleep 120 sleep 120
done done
echo "STOPPED." echo "STOPPED."

View file

@ -188,6 +188,13 @@ class AppConfig:
for key, value in data.items(): for key, value in data.items():
setattr(cls, key, value) setattr(cls, key, value)
def remove_authorization(request: Request) -> Request:
new_header = request.headers.mutablecopy()
del new_header["authorization"]
request.scope["headers"] = new_header.raw
delattr(request, "_headers")
return request
class Api: class Api:
def __init__(self, app: FastAPI) -> None: def __init__(self, app: FastAPI) -> None:
self.app = app self.app = app
@ -220,14 +227,16 @@ class Api:
session_key = get_session_key() session_key = get_session_key()
@self.app.middleware("http") @self.app.middleware("http")
async def authorization(request: Request, call_next): async def authorization(request: Request, call_next):
user = None
if AppConfig.g4f_api_key is not None or AppConfig.demo: if AppConfig.g4f_api_key is not None or AppConfig.demo:
is_authorization_header = False
try: try:
user_g4f_api_key = await self.get_g4f_api_key(request) user_g4f_api_key = await self.get_g4f_api_key(request)
except HTTPException: except HTTPException:
user_g4f_api_key = await self.security(request) user_g4f_api_key = await self.security(request)
if hasattr(user_g4f_api_key, "credentials"): if hasattr(user_g4f_api_key, "credentials"):
user_g4f_api_key = user_g4f_api_key.credentials user_g4f_api_key = user_g4f_api_key.credentials
user = None is_authorization_header = True
if AppConfig.g4f_api_key is None or not secrets.compare_digest(AppConfig.g4f_api_key, user_g4f_api_key): if AppConfig.g4f_api_key is None or not secrets.compare_digest(AppConfig.g4f_api_key, user_g4f_api_key):
if has_crypto and user_g4f_api_key: if has_crypto and user_g4f_api_key:
try: try:
@ -260,10 +269,12 @@ class Api:
user = await self.get_username(request) user = await self.get_username(request)
except HTTPException as e: except HTTPException as e:
return ErrorResponse.from_message(e.detail, e.status_code, e.headers) return ErrorResponse.from_message(e.detail, e.status_code, e.headers)
response = await call_next(request) if is_authorization_header:
response.headers["x-user"] = user request = remove_authorization(request)
return response response = await call_next(request)
return await call_next(request) if user is not None:
response.headers["x_user"] = user
return response
def register_validation_exception_handler(self): def register_validation_exception_handler(self):
@self.app.exception_handler(RequestValidationError) @self.app.exception_handler(RequestValidationError)

View file

@ -215,6 +215,13 @@ class Backend_Api(Api):
f.write(f"{json.dumps(request.json)}\n") f.write(f"{json.dumps(request.json)}\n")
return {} return {}
@app.route('/backend-api/v2/usage/<date>', methods=['GET'])
def get_usage(date: str):
cache_dir = Path(get_cookies_dir()) / ".usage"
cache_file = cache_dir / f"{date}.jsonl"
print(f"Loading usage data from {cache_file}")
return cache_file.read_text() if cache_file.exists() else (jsonify({"error": {"message": "No usage data found for this date"}}), 404)
@app.route('/backend-api/v2/log', methods=['POST']) @app.route('/backend-api/v2/log', methods=['POST'])
def add_log(): def add_log():
cache_dir = Path(get_cookies_dir()) / ".logging" cache_dir = Path(get_cookies_dir()) / ".logging"

View file

@ -5,13 +5,14 @@ import re
import json import json
import asyncio import asyncio
import time import time
import datetime
from pathlib import Path from pathlib import Path
from typing import Optional, Callable, AsyncIterator, Iterator, Dict, Any, Tuple, List, Union from typing import Optional, AsyncIterator, Iterator, Dict, Any, Tuple, List, Union
from ..typing import Messages from ..typing import Messages
from ..providers.helper import filter_none from ..providers.helper import filter_none
from ..providers.asyncio import to_async_iterator from ..providers.asyncio import to_async_iterator
from ..providers.response import Reasoning, FinishReason, Sources from ..providers.response import Reasoning, FinishReason, Sources, Usage, ProviderInfo
from ..providers.types import ProviderType from ..providers.types import ProviderType
from ..cookies import get_cookies_dir from ..cookies import get_cookies_dir
from .web_search import do_search, get_search_message from .web_search import do_search, get_search_message
@ -141,7 +142,7 @@ class AuthManager:
env_var = f"{cls.aliases[provider_name].upper()}_API_KEY" env_var = f"{cls.aliases[provider_name].upper()}_API_KEY"
api_key = os.environ.get(env_var) api_key = os.environ.get(env_var)
if api_key: if api_key:
debug.log(f"Loading API key from environment variable {env_var}") print(f"Loading API key for {provider_name} from environment variable {env_var}")
return api_key return api_key
return None return None
@ -236,9 +237,10 @@ async def async_iter_run_tools(
messages, sources = await perform_web_search(messages, web_search) messages, sources = await perform_web_search(messages, web_search)
# Get API key # Get API key
api_key = AuthManager.load_api_key(provider) if not kwargs.get("api_key"):
if api_key: api_key = AuthManager.load_api_key(provider)
kwargs["api_key"] = api_key if api_key:
kwargs["api_key"] = api_key
# Process tool calls # Process tool calls
if tool_calls: if tool_calls:
@ -248,7 +250,17 @@ async def async_iter_run_tools(
# Generate response # Generate response
response = to_async_iterator(provider.async_create_function(model=model, messages=messages, **kwargs)) response = to_async_iterator(provider.async_create_function(model=model, messages=messages, **kwargs))
model_info = model
async for chunk in response: async for chunk in response:
if isinstance(chunk, ProviderInfo):
model_info = getattr(chunk, 'model', model_info)
elif isinstance(chunk, Usage):
usage = {"user": kwargs.get("user"), "model": model_info, "provider": provider.get_parent(), **chunk.get_dict()}
usage_dir = Path(get_cookies_dir()) / ".usage"
usage_file = usage_dir / f"{datetime.date.today()}.jsonl"
usage_dir.mkdir(parents=True, exist_ok=True)
with usage_file.open("a" if usage_file.exists() else "w") as f:
f.write(f"{json.dumps(usage)}\n")
yield chunk yield chunk
# Yield sources if available # Yield sources if available
@ -277,7 +289,7 @@ def iter_run_tools(
debug.error(f"Couldn't do web search:", e) debug.error(f"Couldn't do web search:", e)
# Get API key if needed # Get API key if needed
if provider is not None: if not kwargs.get("api_key"):
api_key = AuthManager.load_api_key(provider) api_key = AuthManager.load_api_key(provider)
if api_key: if api_key:
kwargs["api_key"] = api_key kwargs["api_key"] = api_key
@ -321,7 +333,7 @@ def iter_run_tools(
# Process response chunks # Process response chunks
thinking_start_time = 0 thinking_start_time = 0
processor = ThinkingProcessor() processor = ThinkingProcessor()
model_info = model
for chunk in provider.create_function(model=model, messages=messages, provider=provider, **kwargs): for chunk in provider.create_function(model=model, messages=messages, provider=provider, **kwargs):
if isinstance(chunk, FinishReason): if isinstance(chunk, FinishReason):
if sources is not None: if sources is not None:
@ -331,6 +343,16 @@ def iter_run_tools(
continue continue
elif isinstance(chunk, Sources): elif isinstance(chunk, Sources):
sources = None sources = None
elif isinstance(chunk, ProviderInfo):
model_info = getattr(chunk, 'model', model_info)
elif isinstance(chunk, Usage):
usage = {"user": kwargs.get("user"), "model": model_info, "provider": provider.get_parent(), **chunk.get_dict()}
usage_dir = Path(get_cookies_dir()) / ".usage"
usage_file = usage_dir / f"{datetime.date.today()}.jsonl"
usage_dir.mkdir(parents=True, exist_ok=True)
with usage_file.open("a" if usage_file.exists() else "w") as f:
f.write(f"{json.dumps(usage)}\n")
if not isinstance(chunk, str): if not isinstance(chunk, str):
yield chunk yield chunk
continue continue