171 lines
6.6 KiB
Python
171 lines
6.6 KiB
Python
"""
|
||
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 API(LLM_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)
|