ai-chat-ui/server/main.py

171 lines
6.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
AI Chat API Server — 主入口(纯基础设施层)
职责:
- 注入运行时依赖venv site-packages
- 读取 LLM_BACKEND 环境变量,动态加载对应平台模块
- 注册 FastAPI 路由和中间件
平台代码位置main.py 中不包含任何平台逻辑):
- 百炼 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
from pathlib import Path
# ── 注入 venv site-packages兼容 start.sh 用系统 python3 启动)────────
# 必须在所有第三方 import 前执行
_venv_lib = Path(__file__).parent / ".venv" / "lib"
if _venv_lib.exists():
for _sp in sorted(_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"[启动] venv 注入:{_sp}")
break
# ── 第三方导入 ────────────────────────────────────────────────────────
from dotenv import load_dotenv
from fastapi import FastAPI, File, Request, UploadFile
from fastapi.responses import JSONResponse
sys.path.append("/home/mt/project/ai-chat-ui/server")
# ── 工具/日志(与平台无关)───────────────────────────────────────────
from utils.helpers import log_response
from utils.logger import get_logger
logger = get_logger()
# ── 加载环境变量 ──────────────────────────────────────────────────────
load_dotenv()
LLM_BACKEND = os.getenv("LLM_BACKEND", "dashscope").lower().strip()
if LLM_BACKEND not in {"dashscope", "glm"}:
logger.warning(f"未知的 LLM_BACKEND='{LLM_BACKEND}',回退到 dashscope")
LLM_BACKEND = "dashscope"
# ── 动态加载平台模块 ──────────────────────────────────────────────────
if LLM_BACKEND == "glm":
import api.chat_routes_glm as _platform
else:
import api.chat_routes as _platform
_platform.init() # 各平台自行完成初始化API Key 校验等)
# 通用路由处理器(文件上传、会话管理等,与平台无关,统一用百炼路由中的实现)
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(
title=f"AI Chat APILLM_BACKEND={LLM_BACKEND}",
version="3.0.0",
)
@app.middleware("http")
async def logging_middleware(request: Request, call_next):
start_time = datetime.now(timezone.utc)
client_ip = request.client.host if request.client else "unknown"
logger.info(f"{request.method} {request.url.path} | IP: {client_ip}")
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"
)
log_response(response.status_code, ms)
response.headers["X-Process-Time"] = f"{ms:.2f}ms"
return response
# ── 路由注册 ──────────────────────────────────────────────────────────
@app.get("/health")
async def health_check():
return {
"status": "healthy",
"backend": LLM_BACKEND,
"timestamp": datetime.now(timezone.utc).isoformat(),
}
@app.post("/api/chat-ui/chat")
async def chat_endpoint(request: Request):
"""聊天接口(自动路由到当前平台)"""
return await _platform.chat_handler(await request.json())
@app.get("/api/chat-ui/models")
async def get_models():
"""模型列表(由当前平台返回)"""
result = _platform.models_handler()
# 支持同步和异步两种返回
if hasattr(result, "__await__"):
return await result
return result
# ── 通用路由(与平台无关)────────────────────────────────────────────
@app.get("/api/chat-ui/conversations")
async def get_conversations():
return await get_conversations_handler()
@app.get("/api/chat-ui/conversations/{conversation_id}")
async def get_conversation(conversation_id: str):
return await get_conversation_handler(conversation_id)
@app.post("/api/chat-ui/conversations")
async def save_conversation(request: Request):
return await save_conversation_handler(await request.json())
@app.delete("/api/chat-ui/conversations/{conversation_id}")
async def delete_conversation(conversation_id: str):
return await delete_conversation_handler(conversation_id)
@app.post("/api/chat-ui/upload")
async def upload_file(file: UploadFile = File(...)):
return await upload_file_handler(file=file)
@app.get("/uploads/{filename}")
async def serve_upload(filename: str):
return serve_upload_handler(filename)
@app.post("/api/chat-ui/stop")
async def stop_generation():
return await stop_generation_handler()
@app.post("/api/chat-ui/stop/{message_id}")
async def stop_generation_by_id(message_id: str):
return await stop_generation_handler(message_id)
# ── 程序入口 ──────────────────────────────────────────────────────────
if __name__ == "__main__":
import uvicorn
port = int(os.getenv("PORT", 8000))
print("=" * 55)
print(f" AI Chat Server v3.0 启动中...")
print(f" 后端平台 : {LLM_BACKEND.upper()} [LLM_BACKEND={LLM_BACKEND}]")
print(f" 监听端口 : {port}")
print(f" 切换平台 : 修改 .env 中 LLM_BACKEND=glm|dashscope重启")
print("=" * 55)
uvicorn.run(app, host="0.0.0.0", port=port)