import json from aiohttp import web from config.logger import setup_logging from core.utils.util import get_vision_url, is_valid_image_file from core.utils.vllm import create_instance import base64 TAG = __name__ # 设置最大文件大小为5MB MAX_FILE_SIZE = 5 * 1024 * 1024 class VisionHandler: def __init__(self, config: dict): self.config = config self.logger = setup_logging() def _create_error_response(self, message: str) -> dict: """创建统一的错误响应格式""" return {"success": False, "message": message} async def handle_post(self, request): """处理 MCP Vision POST 请求""" try: # 获取请求头信息 device_id = request.headers.get("Device-Id", "") client_id = request.headers.get("Client-Id", "") self.logger.bind(tag=TAG).debug(f"Device-Id: {device_id}") self.logger.bind(tag=TAG).debug(f"Client-Id: {client_id}") # 解析multipart/form-data请求 reader = await request.multipart() # 读取question字段 question_field = await reader.next() if question_field is None: raise ValueError("缺少问题字段") question = await question_field.text() self.logger.bind(tag=TAG).debug(f"Question: {question}") # 读取图片文件 image_field = await reader.next() if image_field is None: raise ValueError("缺少图片文件") # 读取图片数据 image_data = await image_field.read() if not image_data: raise ValueError("图片数据为空") # 检查文件大小 if len(image_data) > MAX_FILE_SIZE: raise ValueError( f"图片大小超过限制,最大允许{MAX_FILE_SIZE/1024/1024}MB" ) # 检查文件格式 if not is_valid_image_file(image_data): raise ValueError( "不支持的文件格式,请上传有效的图片文件(支持JPEG、PNG、GIF、BMP、TIFF、WEBP格式)" ) # 将图片转换为base64编码 image_base64 = base64.b64encode(image_data).decode("utf-8") vllm_config = self.config["VLLM"]["ChatGLMVLLM"] provider = create_instance("openai", vllm_config) response = provider.response(question, image_base64) return_json = { "success": True, "result": response, } response = web.Response( text=json.dumps(return_json, separators=(",", ":")), content_type="application/json", ) except ValueError as e: self.logger.bind(tag=TAG).error(f"MCP Vision POST请求异常: {e}") return_json = self._create_error_response(str(e)) response = web.Response( text=json.dumps(return_json, separators=(",", ":")), content_type="application/json", ) except Exception as e: self.logger.bind(tag=TAG).error(f"MCP Vision POST请求异常: {e}") return_json = self._create_error_response("处理请求时发生错误") response = web.Response( text=json.dumps(return_json, separators=(",", ":")), content_type="application/json", ) finally: self._add_cors_headers(response) return response async def handle_get(self, request): """处理 MCP Vision GET 请求""" try: vision_explain = get_vision_url(self.config) message = f"MCP Vision 接口运行正常,视觉解释接口地址是:{vision_explain}" response = web.Response(text=message, content_type="text/plain") except Exception as e: self.logger.bind(tag=TAG).error(f"MCP Vision GET请求异常: {e}") return_json = self._create_error_response("服务器内部错误") response = web.Response( text=json.dumps(return_json, separators=(",", ":")), content_type="application/json", ) finally: self._add_cors_headers(response) return response def _add_cors_headers(self, response): """添加CORS头信息""" response.headers["Access-Control-Allow-Headers"] = ( "client-id, content-type, device-id" ) response.headers["Access-Control-Allow-Credentials"] = "true" response.headers["Access-Control-Allow-Origin"] = "*"