format: 项目格式化

This commit is contained in:
肖应宇 2026-03-04 16:25:16 +08:00
parent b878011a2c
commit 89b02c4c93
14 changed files with 599 additions and 381 deletions

View File

@ -1,35 +1,38 @@
"""
包初始化文件
"""
from .api.chat_routes import (chat_endpoint_handler,
delete_conversation_handler,
get_conversation_handler,
get_conversations_handler, get_models_handler,
save_conversation_handler, serve_upload_handler,
stop_generation_handler, upload_file_handler)
from .models.chat_models import ChatMessage, ChatRequest, ModelInfo
from .utils.helpers import (
get_current_timestamp,
generate_unique_id,
format_api_response,
log_request,
log_response,
extract_delta_content
)
from .api.chat_routes import (
chat_endpoint_handler,
get_models_handler,
get_conversations_handler,
get_conversation_handler,
save_conversation_handler,
delete_conversation_handler,
upload_file_handler,
serve_upload_handler,
stop_generation_handler
)
from .utils.helpers import (extract_delta_content, format_api_response,
generate_unique_id, get_current_timestamp,
log_request, log_response)
__all__ = [
# Models
'ChatMessage', 'ChatRequest', 'ModelInfo',
"ChatMessage",
"ChatRequest",
"ModelInfo",
# Utils
'get_current_timestamp', 'generate_unique_id', 'format_api_response',
'log_request', 'log_response', 'extract_delta_content',
"get_current_timestamp",
"generate_unique_id",
"format_api_response",
"log_request",
"log_response",
"extract_delta_content",
# API Handlers
'chat_endpoint_handler', 'get_models_handler', 'get_conversations_handler',
'get_conversation_handler', 'save_conversation_handler', 'delete_conversation_handler',
'upload_file_handler', 'serve_upload_handler', 'stop_generation_handler'
]
"chat_endpoint_handler",
"get_models_handler",
"get_conversations_handler",
"get_conversation_handler",
"save_conversation_handler",
"delete_conversation_handler",
"upload_file_handler",
"serve_upload_handler",
"stop_generation_handler",
]

View File

@ -1 +1 @@
# api/__init__.py
# api/__init__.py

File diff suppressed because it is too large Load Diff

View File

@ -2,13 +2,16 @@
GLM-4.6V 平台路由处理器zai-sdk
所有智谱 GLM 相关逻辑均集中在此文件main.py 无感知任何平台细节
"""
import json
import os
import sys
import json
from pathlib import Path
from fastapi import HTTPException
from fastapi.responses import JSONResponse, StreamingResponse
from utils.helpers import get_current_timestamp, generate_unique_id
from utils.helpers import generate_unique_id, get_current_timestamp
from utils.logger import log_info
@ -19,7 +22,9 @@ def init():
"""
api_key = os.getenv("ZHIPU_API_KEY") or os.getenv("GLM_API_KEY")
if not api_key:
raise ValueError("GLM 模式需要设置环境变量 ZHIPU_API_KEY在 https://open.bigmodel.cn 申请)")
raise ValueError(
"GLM 模式需要设置环境变量 ZHIPU_API_KEY在 https://open.bigmodel.cn 申请)"
)
log_info(f"[GLM] 初始化完成ZHIPU_API_KEY 已配置")
@ -28,47 +33,61 @@ async def chat_handler(body: dict):
GLM 聊天处理器对外接口与百炼 chat_endpoint_handler 完全兼容
流式/非流式自动适配支持图像文档附件联网搜索深度思考
"""
from utils.glm_adapter import glm_stream_generator, glm_chat_sync
from utils.glm_adapter import glm_chat_sync, glm_stream_generator
if not isinstance(body, dict):
raise HTTPException(status_code=400, detail="请求体必须是 JSON 对象")
messages = body.get("messages", [])
model = body.get("model", "glm-4.6v")
stream = body.get("stream", True)
messages = body.get("messages", [])
model = body.get("model", "glm-4.6v")
stream = body.get("stream", True)
temperature = body.get("temperature", 0.7)
max_tokens = body.get("max_tokens", body.get("maxTokens", 2000))
web_search = body.get("webSearch", False) or body.get("deepSearch", False)
deep_think = body.get("deepThinking", False)
files = body.get("files", [])
max_tokens = body.get("max_tokens", body.get("maxTokens", 2000))
web_search = body.get("webSearch", False) or body.get("deepSearch", False)
deep_think = body.get("deepThinking", False)
files = body.get("files", [])
# 兼容前端简化格式(非 messages 结构)
if not messages:
msg_text = body.get("message", "")
msg_text = body.get("message", "")
sys_prompt = body.get("systemPrompt", "你是一个智能助手。")
user_content = msg_text if isinstance(msg_text, list) else [{"type": "text", "text": msg_text}]
user_content = (
msg_text
if isinstance(msg_text, list)
else [{"type": "text", "text": msg_text}]
)
messages = [
{"role": "system", "content": sys_prompt},
{"role": "user", "content": user_content},
{"role": "user", "content": user_content},
]
log_info(f"[GLM] model={model} stream={stream} web_search={web_search} "
f"thinking={deep_think} files={len(files)} msgs={len(messages)}")
log_info(
f"[GLM] model={model} stream={stream} web_search={web_search} "
f"thinking={deep_think} files={len(files)} msgs={len(messages)}"
)
if stream:
return StreamingResponse(
glm_stream_generator(
messages=messages, model=model, temperature=temperature,
max_tokens=max_tokens, files=files or None,
web_search=web_search, deep_thinking=deep_think,
messages=messages,
model=model,
temperature=temperature,
max_tokens=max_tokens,
files=files or None,
web_search=web_search,
deep_thinking=deep_think,
),
media_type="text/event-stream",
)
result = glm_chat_sync(
messages=messages, model=model, temperature=temperature,
max_tokens=max_tokens, files=files or None,
web_search=web_search, deep_thinking=deep_think,
messages=messages,
model=model,
temperature=temperature,
max_tokens=max_tokens,
files=files or None,
web_search=web_search,
deep_thinking=deep_think,
)
resp = {
"id": f"chatcmpl-{generate_unique_id()}",

View File

@ -2,9 +2,12 @@
"""
初始化日志系统
"""
import os
from utils.logger import setup_global_logger
def init_logging_system():
"""
初始化日志系统
@ -26,13 +29,12 @@ def init_logging_system():
# 设置全局日志系统
logger = setup_global_logger(
name="ai-chat-api",
log_level=log_level,
log_dir=log_dir
name="ai-chat-api", log_level=log_level, log_dir=log_dir
)
return logger
if __name__ == "__main__":
logger = init_logging_system()
logger.info("Logging system initialized successfully")
logger.info("Logging system initialized successfully")

View File

@ -10,6 +10,7 @@ AI Chat API Server — 主入口(纯基础设施层)
- 百炼 DashScope api/chat_routes.py
- 智谱 GLM-4.6V api/chat_routes_glm.py + utils/glm_adapter.py
"""
import os
import sys
from datetime import datetime, timezone
@ -27,10 +28,10 @@ if _venv_lib.exists():
# ── 第三方导入 ────────────────────────────────────────────────────────
from dotenv import load_dotenv
from fastapi import FastAPI, File, UploadFile, Request
from fastapi import FastAPI, File, Request, UploadFile
from fastapi.responses import JSONResponse
sys.path.append('/home/mt/project/ai-chat-ui/server')
sys.path.append("/home/mt/project/ai-chat-ui/server")
# ── 工具/日志(与平台无关)───────────────────────────────────────────
from utils.helpers import log_response
@ -55,15 +56,11 @@ else:
_platform.init() # 各平台自行完成初始化API Key 校验等)
# 通用路由处理器(文件上传、会话管理等,与平台无关,统一用百炼路由中的实现)
from api.chat_routes import (
get_conversations_handler,
get_conversation_handler,
save_conversation_handler,
delete_conversation_handler,
upload_file_handler,
serve_upload_handler,
stop_generation_handler,
)
from api.chat_routes import (delete_conversation_handler,
get_conversation_handler,
get_conversations_handler,
save_conversation_handler, serve_upload_handler,
stop_generation_handler, upload_file_handler)
# ── FastAPI 应用 ──────────────────────────────────────────────────────
app = FastAPI(
@ -80,7 +77,9 @@ async def logging_middleware(request: Request, call_next):
response = await call_next(request)
ms = (datetime.now(timezone.utc) - start_time).total_seconds() * 1000
icon = "" if response.status_code < 400 else ""
logger.info(f"{icon} {request.method} {request.url.path} | 状态: {response.status_code} | 耗时: {ms:.0f}ms")
logger.info(
f"{icon} {request.method} {request.url.path} | 状态: {response.status_code} | 耗时: {ms:.0f}ms"
)
log_response(response.status_code, ms)
response.headers["X-Process-Time"] = f"{ms:.2f}ms"
return response
@ -88,6 +87,7 @@ async def logging_middleware(request: Request, call_next):
# ── 路由注册 ──────────────────────────────────────────────────────────
@app.get("/health")
async def health_check():
return {
@ -115,6 +115,7 @@ async def get_models():
# ── 通用路由(与平台无关)────────────────────────────────────────────
@app.get("/api/chat-ui/conversations")
async def get_conversations():
return await get_conversations_handler()
@ -158,6 +159,7 @@ async def stop_generation_by_id(message_id: str):
# ── 程序入口 ──────────────────────────────────────────────────────────
if __name__ == "__main__":
import uvicorn
port = int(os.getenv("PORT", 8000))
print("=" * 55)
print(f" AI Chat Server v3.0 启动中...")
@ -165,4 +167,4 @@ if __name__ == "__main__":
print(f" 监听端口 : {port}")
print(f" 切换平台 : 修改 .env 中 LLM_BACKEND=glm|dashscope重启")
print("=" * 55)
uvicorn.run(app, host="0.0.0.0", port=port)
uvicorn.run(app, host="0.0.0.0", port=port)

View File

@ -1 +1 @@
# models/__init__.py
# models/__init__.py

View File

@ -1,14 +1,18 @@
"""
数据模型定义
"""
from typing import Any, Dict, List, Optional, Union
from pydantic import BaseModel
from typing import Dict, List, Optional, Any, Union
class ChatMessageContentItem(BaseModel):
type: str # "text" or "image_url"
text: Optional[str] = None
image_url: Optional[Dict[str, str]] = None # {"url": "...", "detail": "auto|low|high"}
image_url: Optional[Dict[str, str]] = (
None # {"url": "...", "detail": "auto|low|high"}
)
class ChatMessage(BaseModel):
@ -35,4 +39,4 @@ class ModelInfo(BaseModel):
name: str
description: str
maxTokens: int
provider: str
provider: str

View File

@ -1 +1 @@
# utils/__init__.py
# utils/__init__.py

View File

@ -1,10 +1,11 @@
"""
GLM 文件 ID 缓存基于磁盘的简单 KVsha256 file_id3天有效期
"""
import hashlib
import json
import time
import threading
import time
from pathlib import Path
_CACHE_FILE = Path(__file__).parent.parent / "uploads" / ".glm_file_cache.json"
@ -24,7 +25,9 @@ def _load() -> dict:
def _save(data: dict) -> None:
try:
_CACHE_FILE.parent.mkdir(parents=True, exist_ok=True)
_CACHE_FILE.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")
_CACHE_FILE.write_text(
json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8"
)
except Exception as e:
print(f"[file_cache] 写入失败:{e}")

View File

@ -3,10 +3,11 @@ GLM-4.6V 适配层(基于 zai-sdk
SDKpip install zai-sdk
模型glm-4.6v支持文本/图像/文档/深度思考
"""
import base64
import json
import os
import sys
import json
import base64
import threading
from pathlib import Path
from typing import AsyncGenerator
@ -15,7 +16,9 @@ from typing import AsyncGenerator
# ── 自动注入 venv site-packages ───────────────────────────────────────
def _ensure_venv():
server_dir = Path(__file__).parent.parent
for sp in sorted((server_dir / ".venv" / "lib").glob("python*/site-packages"), reverse=True):
for sp in sorted(
(server_dir / ".venv" / "lib").glob("python*/site-packages"), reverse=True
):
if sp.exists() and str(sp) not in sys.path:
sys.path.insert(0, str(sp))
print(f"[GLM] venv 注入:{sp}")
@ -34,7 +37,7 @@ def get_client():
from zai import ZhipuAiClient
except ImportError:
raise ImportError("GLM 模式需要安装 zai-sdk.venv/bin/pip install zai-sdk")
api_key = os.getenv("ZHIPU_API_KEY").strip() or os.getenv("GLM_API_KEY").strip()
api_key = os.getenv("ZHIPU_API_KEY").strip() or os.getenv("GLM_API_KEY").strip()
if not api_key:
raise ValueError("GLM 模式需要设置环境变量 ZHIPU_API_KEY")
_client = ZhipuAiClient(api_key=api_key)
@ -43,15 +46,15 @@ def get_client():
# ── 模型映射 ──────────────────────────────────────────────────────────
DEFAULT_TEXT_MODEL = "glm-4.5-Air" # glm-4.6 文本统一模型
DEFAULT_TEXT_MODEL = "glm-4.5-Air" # glm-4.6 文本统一模型
DEFAULT_VISION_MODEL = "glm-4.5-Air"
MODEL_MAP = {
"qwen-max": "glm-4.5-Air",
"qwen-plus": "glm-4.5-Air",
"qwen-turbo": "glm-4.5-Air",
"qwen-vl-max": "glm-4.5-Air",
"qwen-vl-plus": "glm-4.5-Air",
"qwen-max": "glm-4.5-Air",
"qwen-plus": "glm-4.5-Air",
"qwen-turbo": "glm-4.5-Air",
"qwen-vl-max": "glm-4.5-Air",
"qwen-vl-plus": "glm-4.5-Air",
}
@ -63,7 +66,9 @@ def resolve_model(model: str, has_vision: bool = False) -> str:
# ── 文件上传(含 file_id 缓存)───────────────────────────────────────
def upload_file_for_extract(local_path: Path) -> str:
from utils.file_cache import sha256_of_file, get as cache_get, set as cache_set
from utils.file_cache import get as cache_get
from utils.file_cache import set as cache_set
from utils.file_cache import sha256_of_file
file_hash = sha256_of_file(local_path)
cached = cache_get(file_hash)
@ -73,18 +78,20 @@ def upload_file_for_extract(local_path: Path) -> str:
client = get_client()
mime_map = {
".pdf": "application/pdf",
".pdf": "application/pdf",
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
".doc": "application/msword",
".doc": "application/msword",
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
".xls": "application/vnd.ms-excel",
".xls": "application/vnd.ms-excel",
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
".ppt": "application/vnd.ms-powerpoint",
".ppt": "application/vnd.ms-powerpoint",
}
mime = mime_map.get(local_path.suffix.lower(), "application/octet-stream")
print(f"[GLM] 上传文件:{local_path.name}{mime}")
with open(local_path, "rb") as f:
file_obj = client.files.create(file=(local_path.name, f, mime), purpose="file-extract")
file_obj = client.files.create(
file=(local_path.name, f, mime), purpose="file-extract"
)
file_id = file_obj.id
cache_set(file_hash, file_id)
print(f"[GLM] 上传成功file_id={file_id}")
@ -94,7 +101,9 @@ def upload_file_for_extract(local_path: Path) -> str:
# ── 图像编码 ─────────────────────────────────────────────────────────
def encode_image(image_source: str) -> dict:
"""将图像来源统一转为 OpenAI image_url 格式"""
if image_source.startswith("data:image") or image_source.startswith(("http://", "https://")):
if image_source.startswith("data:image") or image_source.startswith(
("http://", "https://")
):
return {"type": "image_url", "image_url": {"url": image_source}}
# 本地路径 → base64
local = Path(image_source.replace("file://", "").lstrip("/"))
@ -138,7 +147,9 @@ def build_glm_messages(messages: list, files: list | None = None) -> tuple[list,
elif t == "image_url":
has_vision = True
img_val = item.get("image_url", "")
img_src = img_val.get("url", "") if isinstance(img_val, dict) else img_val
img_src = (
img_val.get("url", "") if isinstance(img_val, dict) else img_val
)
new_content.append(encode_image(img_src))
else:
new_content.append({"type": "text", "text": str(item)})
@ -172,9 +183,13 @@ def build_glm_messages(messages: list, files: list | None = None) -> tuple[list,
fid = upload_file_for_extract(local)
inserts.append({"type": "file", "file": {"file_id": fid}})
except Exception as e:
inserts.append({"type": "text", "text": f"[文件上传失败:{filename}{e}]"})
inserts.append(
{"type": "text", "text": f"[文件上传失败:{filename}{e}]"}
)
else:
inserts.append({"type": "text", "text": f"[附件:{filename},类型:{suffix}]"})
inserts.append(
{"type": "text", "text": f"[附件:{filename},类型:{suffix}]"}
)
if inserts:
for i in range(len(glm_messages) - 1, -1, -1):
@ -195,6 +210,7 @@ def build_glm_messages(messages: list, files: list | None = None) -> tuple[list,
# ── 哨兵对象 ─────────────────────────────────────────────────────────
_SENTINEL = object()
# ── 流式调用 ────────────────────────────────────────────────────────
async def glm_stream_generator(
messages: list,
@ -213,7 +229,7 @@ async def glm_stream_generator(
import asyncio
import queue
from utils.helpers import get_current_timestamp, generate_unique_id
from utils.helpers import generate_unique_id, get_current_timestamp
glm_msgs, has_vision = build_glm_messages(messages, files)
actual_model = resolve_model(model, has_vision)
@ -221,13 +237,18 @@ async def glm_stream_generator(
extra_kwargs: dict = {}
if web_search:
extra_kwargs["tools"] = [
{"type": "web_search", "web_search": {"enable":True,"search_result": True}}
{
"type": "web_search",
"web_search": {"enable": True, "search_result": True},
}
]
if not deep_thinking:
# 智普默认开启思考模式,所以要用非门(不知道“非门”描述是否准确。前端选择开启思考模式,这里不做变动。前端选择关闭思考模式,这里关闭。)
extra_kwargs["thinking"] = {"type": "disabled"}
print(f"[GLM] 流式请求model={actual_model} vision={has_vision} "
f"web_search={web_search} thinking={deep_thinking}")
print(
f"[GLM] 流式请求model={actual_model} vision={has_vision} "
f"web_search={web_search} thinking={deep_thinking}"
)
chunk_queue: queue.Queue = queue.Queue(maxsize=128)
@ -254,8 +275,8 @@ async def glm_stream_generator(
loop = asyncio.get_running_loop()
full_reasoning = "" # 累计思考内容(用于判断是否首次)
full_content = "" # 累计正式回答(用于判断是否首次)
full_reasoning = "" # 累计思考内容(用于判断是否首次)
full_content = "" # 累计正式回答(用于判断是否首次)
while True:
item = await loop.run_in_executor(None, chunk_queue.get)
@ -271,7 +292,7 @@ async def glm_stream_generator(
try:
delta = item.choices[0].delta
reasoning = getattr(delta, "reasoning_content", "") or ""
text = getattr(delta, "content", "") or ""
text = getattr(delta, "content", "") or ""
delta_str = ""
@ -300,7 +321,9 @@ async def glm_stream_generator(
"object": "chat.completion.chunk",
"created": get_current_timestamp(),
"model": actual_model,
"choices": [{"index": 0, "delta": {"content": delta_str}, "finish_reason": None}],
"choices": [
{"index": 0, "delta": {"content": delta_str}, "finish_reason": None}
],
}
yield f"data: {json.dumps(data, ensure_ascii=False)}\n\n"
@ -318,7 +341,6 @@ async def glm_stream_generator(
yield "data: [DONE]\n\n"
# ── 非流式调用 ────────────────────────────────────────────────────────
def glm_chat_sync(
messages: list,
@ -334,13 +356,12 @@ def glm_chat_sync(
extra_kwargs: dict = {}
if web_search:
extra_kwargs["tools"] = [{
"type": "web_search",
"web_search": {
"enable": True,
"search_result": True
}
}]
extra_kwargs["tools"] = [
{
"type": "web_search",
"web_search": {"enable": True, "search_result": True},
}
]
if deep_thinking:
extra_kwargs["thinking"] = {"type": "enabled"}
@ -358,8 +379,8 @@ def glm_chat_sync(
usage = None
if hasattr(resp, "usage") and resp.usage:
usage = {
"promptTokens": resp.usage.prompt_tokens,
"promptTokens": resp.usage.prompt_tokens,
"completionTokens": resp.usage.completion_tokens,
"totalTokens": resp.usage.total_tokens,
"totalTokens": resp.usage.total_tokens,
}
return {"content": content, "model": actual_model, "usage": usage}

View File

@ -1,13 +1,15 @@
"""
通用工具函数
"""
import os
import json
import os
import uuid
from datetime import datetime
from typing import Dict
from .logger import log_request_info, log_response_info, log_error_detail, log_chat_interaction
from .logger import (log_chat_interaction, log_error_detail, log_request_info,
log_response_info)
def get_current_timestamp():
@ -20,14 +22,16 @@ def generate_unique_id():
return str(uuid.uuid4())
def format_api_response(content: str, conversation_id: str = None, model: str = "qwen-plus"):
def format_api_response(
content: str, conversation_id: str = None, model: str = "qwen-plus"
):
"""格式化API响应"""
return {
"id": generate_unique_id(),
"conversationId": conversation_id or generate_unique_id(),
"content": content,
"model": model,
"createdAt": get_current_timestamp()
"createdAt": get_current_timestamp(),
}
@ -44,5 +48,5 @@ def log_response(status_code: int, process_time: float):
def extract_delta_content(full_content: str, previous_content: str) -> str:
"""提取增量内容"""
if len(full_content) > len(previous_content):
return full_content[len(previous_content):]
return ""
return full_content[len(previous_content) :]
return ""

View File

@ -2,20 +2,27 @@
统一日志管理系统
提供结构化日志记录功能支持不同日志级别文件输出轮转等
"""
import json
import logging
import os
import sys
from datetime import datetime
from pathlib import Path
from logging.handlers import RotatingFileHandler
import json
from pathlib import Path
class LoggerSetup:
"""日志系统配置类"""
def __init__(self, name: str = "ai-chat-server", log_level: str = "INFO",
log_dir: str = "logs", max_bytes: int = 10 * 1024 * 1024, backup_count: int = 5):
def __init__(
self,
name: str = "ai-chat-server",
log_level: str = "INFO",
log_dir: str = "logs",
max_bytes: int = 10 * 1024 * 1024,
backup_count: int = 5,
):
"""
初始化日志系统
@ -37,7 +44,7 @@ class LoggerSetup:
# 设置日志格式(去掉 funcName:lineno保持人类可读性
self.formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
"%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
# 创建logger实例
@ -66,7 +73,7 @@ class LoggerSetup:
str(log_file),
maxBytes=self.max_bytes,
backupCount=self.backup_count,
encoding='utf-8'
encoding="utf-8",
)
file_handler.setLevel(self.log_level)
file_handler.setFormatter(self.formatter)
@ -83,9 +90,13 @@ class LoggerSetup:
_logger_instance = None
def setup_global_logger(name: str = "ai-chat-server", log_level: str = "INFO",
log_dir: str = "logs", max_bytes: int = 10 * 1024 * 1024,
backup_count: int = 5):
def setup_global_logger(
name: str = "ai-chat-server",
log_level: str = "INFO",
log_dir: str = "logs",
max_bytes: int = 10 * 1024 * 1024,
backup_count: int = 5,
):
"""
设置全局日志系统
@ -170,12 +181,17 @@ def log_structured(level: str, message: str, **details):
# 转换为更易读的格式
detail_str = ", ".join(f"{k}={v}" for k, v in details.items() if v)
formatted_msg = f"[{message}] {detail_str}"
getattr(logger, level.lower())(formatted_msg)
def log_request_info(method: str, path: str, client_ip: str = "unknown",
user_agent: str = "", referer: str = ""):
def log_request_info(
method: str,
path: str,
client_ip: str = "unknown",
user_agent: str = "",
referer: str = "",
):
"""记录请求信息日志"""
log_structured(
"info",
@ -184,12 +200,17 @@ def log_request_info(method: str, path: str, client_ip: str = "unknown",
path=path,
client_ip=client_ip,
user_agent=user_agent,
referer=referer
referer=referer,
)
def log_response_info(status_code: int, process_time: float, path: str = "",
method: str = "", client_ip: str = ""):
def log_response_info(
status_code: int,
process_time: float,
path: str = "",
method: str = "",
client_ip: str = "",
):
"""记录响应信息日志"""
log_structured(
"info",
@ -198,37 +219,52 @@ def log_response_info(status_code: int, process_time: float, path: str = "",
process_time_ms=process_time,
path=path,
method=method,
client_ip=client_ip
client_ip=client_ip,
)
def log_error_detail(error_type: str, error_message: str, traceback_info: str = "",
context: dict = None):
def log_error_detail(
error_type: str, error_message: str, traceback_info: str = "", context: dict = None
):
"""记录详细的错误信息"""
log_structured(
"error",
f"{error_type}: {error_message}",
traceback=traceback_info,
context=context or {}
context=context or {},
)
def log_chat_interaction(user_input: str, ai_response: str, model: str = "",
conversation_id: str = "", tokens_used: dict = None):
def log_chat_interaction(
user_input: str,
ai_response: str,
model: str = "",
conversation_id: str = "",
tokens_used: dict = None,
):
"""记录聊天交互日志"""
log_structured(
"info",
"Chat Interaction",
user_input=user_input[:100] + "..." if len(user_input) > 100 else user_input, # 截断长输入
ai_response=ai_response[:100] + "..." if len(ai_response) > 100 else ai_response,
user_input=(
user_input[:100] + "..." if len(user_input) > 100 else user_input
), # 截断长输入
ai_response=(
ai_response[:100] + "..." if len(ai_response) > 100 else ai_response
),
model=model,
conversation_id=conversation_id,
tokens_used=tokens_used
tokens_used=tokens_used,
)
def log_system_status(status: str, uptime: float = 0, cpu_usage: float = 0,
memory_usage: float = 0, disk_usage: float = 0):
def log_system_status(
status: str,
uptime: float = 0,
cpu_usage: float = 0,
memory_usage: float = 0,
disk_usage: float = 0,
):
"""记录系统状态日志"""
log_structured(
"info",
@ -237,5 +273,5 @@ def log_system_status(status: str, uptime: float = 0, cpu_usage: float = 0,
uptime_seconds=uptime,
cpu_percent=cpu_usage,
memory_percent=memory_usage,
disk_percent=disk_usage
)
disk_percent=disk_usage,
)

View File

@ -1,30 +1,36 @@
import asyncio
import os
import sys
import asyncio
from pathlib import Path
# Add project root to sys.path
root_dir = Path(__file__).parent
sys.path.insert(0, str(root_dir))
from utils.glm_adapter import glm_stream_generator, _ensure_venv, glm_chat_sync
# Set API key from .env if needed
from dotenv import load_dotenv
from utils.glm_adapter import _ensure_venv, glm_chat_sync, glm_stream_generator
load_dotenv()
async def test_stream():
msgs = [{"role": "user", "content": "今天北京天气怎样?"}]
print("Testing stream...")
async for chunk in glm_stream_generator(msgs, "glm-4.5-air", 0.7, 1024, web_search=True):
async for chunk in glm_stream_generator(
msgs, "glm-4.5-air", 0.7, 1024, web_search=True
):
print(chunk, end="")
def test_sync():
msgs = [{"role": "user", "content": "今天几号?武汉天气怎样?"}]
print("Testing sync...")
res = glm_chat_sync(msgs, "glm-4.5-air", 0.7, 1024, web_search=True)
print(res)
if __name__ == "__main__":
_ensure_venv()
# test_sync()