diff --git a/.vscode/launch.json b/.vscode/launch.json index 4a979d6..8c2a781 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -15,7 +15,8 @@ "--host", "127.0.0.1", "--port", - "8000" + "8000", + "--no-access-log" ], "jinja": true } diff --git a/Dockerfile b/Dockerfile index 8bd8c83..43c5d1b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,4 +17,4 @@ ENV MODEL_SEARCH='["gemini-2.0-flash-exp"]' EXPOSE 8000 # Run the application -CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--no-access-log"] diff --git a/app/api/routes.py b/app/api/routes.py index e87c8da..3437452 100644 --- a/app/api/routes.py +++ b/app/api/routes.py @@ -1,6 +1,5 @@ from http.client import HTTPException from fastapi import APIRouter, Depends, Header -import logging from fastapi.responses import StreamingResponse from app.core.security import SecurityService @@ -10,9 +9,10 @@ from app.services.chat_service import ChatService from app.services.embedding_service import EmbeddingService from app.schemas.request_model import ChatRequest, EmbeddingRequest from app.core.config import settings +from app.core.logger import get_api_logger router = APIRouter() -logger = logging.getLogger(__name__) +logger = get_api_logger() # 初始化服务 security_service = SecurityService(settings.ALLOWED_TOKENS) @@ -59,7 +59,6 @@ async def chat_completion( tools=request.tools, tool_choice=request.tool_choice, ) - # 处理流式响应 if request.stream: diff --git a/app/core/logger.py b/app/core/logger.py new file mode 100644 index 0000000..234b0a3 --- /dev/null +++ b/app/core/logger.py @@ -0,0 +1,105 @@ +import logging +import sys +from typing import Dict, Optional +import platform + +# ANSI转义序列颜色代码 +COLORS = { + 'DEBUG': '\033[34m', # 蓝色 + 'INFO': '\033[32m', # 绿色 + 'WARNING': '\033[33m', # 黄色 + 'ERROR': '\033[31m', # 红色 + 'CRITICAL': '\033[1;31m' # 红色加粗 +} + +# Windows系统启用ANSI支持 +if platform.system() == 'Windows': + import ctypes + kernel32 = ctypes.windll.kernel32 + kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7) + + +class ColoredFormatter(logging.Formatter): + """ + 自定义的日志格式化器,添加颜色支持 + """ + def format(self, record): + # 获取对应级别的颜色代码 + color = COLORS.get(record.levelname, '') + # 添加颜色代码和重置代码 + record.levelname = f"{color}{record.levelname}\033[0m" + return super().format(record) + +# 日志格式 +FORMATTER = ColoredFormatter( + "%(asctime)s - %(name)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(message)s" +) + +# 日志级别映射 +LOG_LEVELS = { + "debug": logging.DEBUG, + "info": logging.INFO, + "warning": logging.WARNING, + "error": logging.ERROR, + "critical": logging.CRITICAL, +} + +class Logger: + _loggers: Dict[str, logging.Logger] = {} + + @staticmethod + def setup_logger( + name: str, + level: str = "info", + ) -> logging.Logger: + """ + 设置并获取logger + :param name: logger名称 + :param level: 日志级别 + :return: logger实例 + """ + if name in Logger._loggers: + return Logger._loggers[name] + + logger = logging.getLogger(name) + logger.setLevel(LOG_LEVELS.get(level.lower(), logging.INFO)) + logger.propagate = False + + # 添加控制台输出 + console_handler = logging.StreamHandler(sys.stdout) + console_handler.setFormatter(FORMATTER) + logger.addHandler(console_handler) + + Logger._loggers[name] = logger + return logger + + @staticmethod + def get_logger(name: str) -> Optional[logging.Logger]: + """ + 获取已存在的logger + :param name: logger名称 + :return: logger实例或None + """ + return Logger._loggers.get(name) + +# 预定义的loggers +def get_api_logger(): + return Logger.setup_logger("api") + +def get_chat_logger(): + return Logger.setup_logger("chat") + +def get_model_logger(): + return Logger.setup_logger("model") + +def get_security_logger(): + return Logger.setup_logger("security") + +def get_key_manager_logger(): + return Logger.setup_logger("key_manager") + +def get_main_logger(): + return Logger.setup_logger("main") + +def get_embeddings_logger(): + return Logger.setup_logger("embeddings") diff --git a/app/core/security.py b/app/core/security.py index 00e13be..223a730 100644 --- a/app/core/security.py +++ b/app/core/security.py @@ -1,8 +1,8 @@ from fastapi import HTTPException, Header -import logging from typing import Optional +from app.core.logger import get_security_logger -logger = logging.getLogger(__name__) +logger = get_security_logger() class SecurityService: diff --git a/app/main.py b/app/main.py index fab4a88..5213640 100644 --- a/app/main.py +++ b/app/main.py @@ -1,15 +1,12 @@ from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware -import logging +from app.core.logger import get_main_logger from app.api.routes import router import uvicorn # 配置日志 -logging.basicConfig( - level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" -) -logger = logging.getLogger(__name__) +logger = get_main_logger() app = FastAPI() diff --git a/app/services/chat_service.py b/app/services/chat_service.py index 6d90ffb..db8e805 100644 --- a/app/services/chat_service.py +++ b/app/services/chat_service.py @@ -2,12 +2,12 @@ import httpx import json import time import uuid -import logging from typing import Dict, Any, Optional, AsyncGenerator, Union import openai from app.core.config import settings +from app.core.logger import get_chat_logger -logger = logging.getLogger(__name__) +logger = get_chat_logger() class ChatService: @@ -170,6 +170,9 @@ class ChatService: gemini_model = model gemini_messages = self.convert_messages_to_gemini_format(messages) + if not stream: + # 非流式模式下,移除代码执行工具 + tools.remove({"code_execution": {}}) payload = { "contents": gemini_messages, "generationConfig": {"temperature": temperature}, @@ -241,7 +244,9 @@ class ChatService: url = f"https://generativelanguage.googleapis.com/v1beta/models/{gemini_model}:generateContent?key={api_key}" response = await client.post(url, json=payload) if response.status_code != 200: - raise Exception(f"API error: {response.status_code}") + error_text = response.text + error_code = response.status_code + raise Exception(f"API调用错误 - 状态码: {error_code}, 响应内容: {error_text}") gemini_response = response.json() return self.convert_gemini_response_to_openai(gemini_response, model, finish_reason="stop") except Exception as e: @@ -287,10 +292,10 @@ class ChatService: language = code_data.get("language", "").lower() code = code_data.get("code", "").strip() - return f"""\n```{language}\n{code}\n```\n""" + return f"""\n【代码执行】\n```{language}\n{code}\n```\n""" def format_execution_result(self, result_data: dict) -> str: """格式化执行结果输出""" outcome = result_data.get("outcome", "") output = result_data.get("output", "").strip() - return f"""\n【执行结果】\noutcome: {outcome}\noutput: {output}\n""" + return f"""\n【执行结果】\noutcome: {outcome}\noutput:\n```{output}```\n""" diff --git a/app/services/embedding_service.py b/app/services/embedding_service.py index bb8e3c1..a2eaf36 100644 --- a/app/services/embedding_service.py +++ b/app/services/embedding_service.py @@ -1,8 +1,8 @@ -import logging import openai from typing import Union, List, Dict, Any +from app.core.logger import get_embeddings_logger -logger = logging.getLogger(__name__) +logger = get_embeddings_logger() class EmbeddingService: diff --git a/app/services/key_manager.py b/app/services/key_manager.py index 1a1b308..c62b539 100644 --- a/app/services/key_manager.py +++ b/app/services/key_manager.py @@ -1,9 +1,9 @@ import asyncio from itertools import cycle -import logging from typing import Dict +from app.core.logger import get_key_manager_logger -logger = logging.getLogger(__name__) +logger = get_key_manager_logger() class KeyManager: @@ -42,7 +42,7 @@ class KeyManager: current_key = await self.get_next_key() if current_key == initial_key: - await self.reset_failure_counts() + # await self.reset_failure_counts() 取消重置 return current_key async def handle_api_failure(self, api_key: str) -> str: diff --git a/app/services/model_service.py b/app/services/model_service.py index 056f924..5027376 100644 --- a/app/services/model_service.py +++ b/app/services/model_service.py @@ -1,9 +1,9 @@ import requests from datetime import datetime, timezone from typing import Optional, Dict, Any -import logging +from app.core.logger import get_model_logger -logger = logging.getLogger(__name__) +logger = get_model_logger() class ModelService: