Files
2026-07-13 12:35:57 +08:00

144 lines
4.9 KiB
Python

from fastapi import APIRouter, HTTPException
from pydantic import BaseModel, Field
from typing import Optional
import time
import logging
from services.similarity_service import SimilarityCalculator
from config.settings import settings
# 创建路由器
router = APIRouter(prefix="/knowledge/similarity", tags=["knowledge-test"])
# 设置日志
logger = logging.getLogger(__name__)
# 全局相似度计算器实例
similarity_calculator = None
# Pydantic模型定义
class SimilarityRequest(BaseModel):
x_collection: str = Field(..., description="X轴collection名称")
y_collection: str = Field(..., description="Y轴collection名称")
x_max_items: Optional[int] = Field(100, description="X轴最大项目数")
y_max_items: Optional[int] = Field(100, description="Y轴最大项目数")
max_items: Optional[int] = Field(100, description="最大项目数(向后兼容)")
def init_similarity_calculator():
"""初始化相似度计算器"""
global similarity_calculator
try:
similarity_calculator = SimilarityCalculator()
logger.info("✅ 相似度计算器初始化成功")
except Exception as e:
logger.error(f"❌ 相似度计算器初始化失败: {e}")
similarity_calculator = None
@router.get("/health")
async def similarity_health_check():
"""相似度服务健康检查"""
try:
calculator_ready = similarity_calculator is not None
connection_info = {}
if similarity_calculator:
connection_info = similarity_calculator.test_connection()
return {
"status": "healthy",
"calculator_ready": calculator_ready,
"timestamp": time.time(),
"connections": connection_info,
"vector_db_type": settings.vector_db_type
}
except Exception as e:
raise HTTPException(
status_code=500,
detail={
"status": "error",
"calculator_ready": False,
"error": str(e),
"timestamp": time.time()
}
)
@router.get("/collections")
async def get_similarity_collections():
"""获取所有collections"""
try:
if not similarity_calculator:
init_similarity_calculator()
if not similarity_calculator:
raise HTTPException(status_code=500, detail="相似度计算器未初始化")
collections = similarity_calculator.get_collections()
return {
"success": True,
"collections": collections,
"count": len(collections),
"vector_db_type": settings.vector_db_type
}
except Exception as e:
raise HTTPException(
status_code=500,
detail={"success": False, "error": str(e)}
)
@router.post("/calculate")
async def calculate_similarity_matrix(request: SimilarityRequest):
"""计算相似度矩阵"""
try:
if not similarity_calculator:
init_similarity_calculator()
if not similarity_calculator:
raise HTTPException(status_code=500, detail="相似度计算器未初始化")
# 解析参数
x_collection = request.x_collection
y_collection = request.y_collection
x_max_items = int(request.x_max_items or request.max_items or 100)
y_max_items = int(request.y_max_items or request.max_items or 100)
# 限制最大值
x_max_items = min(x_max_items, 3000)
y_max_items = min(y_max_items, 3000)
logger.info(f"🎯 收到相似度计算请求:")
logger.info(f" X: {x_collection} (最大{x_max_items}项)")
logger.info(f" Y: {y_collection} (最大{y_max_items}项)")
# 计算相似度矩阵
result = similarity_calculator.calculate_similarity_matrix(
x_collection=x_collection,
y_collection=y_collection,
x_max_items=x_max_items,
y_max_items=y_max_items
)
return {
"success": True,
"result": result,
"message": f"成功计算 {len(result['y_data'])} x {len(result['x_data'])} 相似度矩阵",
"vector_db_type": settings.vector_db_type
}
except ValueError as e:
# 维度不匹配等业务逻辑错误,返回正常响应但标记失败
error_msg = str(e)
logger.warning(f"⚠️ 相似度计算失败 ({x_collection} vs {y_collection}): {error_msg}")
return {
"success": False,
"error": error_msg,
"error_type": "dimension_mismatch" if "维度不匹配" in error_msg else "calculation_error",
"x_collection": x_collection,
"y_collection": y_collection
}
except Exception as e:
# 系统级错误,仍然抛出 HTTP 异常
logger.error(f"❌ 相似度计算API系统错误: {e}")
raise HTTPException(
status_code=500,
detail={"success": False, "error": str(e)}
)