Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 27 additions & 8 deletions apps/common/exception/app_exception.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,44 @@
# coding=utf-8
"""
@project: qabot
@Author:虎虎
@file: app_exception.py
@date:2023/9/4 14:04
@desc:
@project: qabot
@Author:虎虎
@file: app_exception.py
@date:2023/9/4 14:04
@desc:
"""

from rest_framework import status


class AppApiException(Exception):
"""
项目内异常
"""

status_code = status.HTTP_200_OK

def __init__(self, code, message):
self.code = code
self.message = message


class BadRequest400(AppApiException):
"""
请求参数错误
"""

status_code = status.HTTP_400_BAD_REQUEST

def __init__(self, code, message):
self.code = code
self.message = message


class NotFound404(AppApiException):
"""
未认证(未登录)异常
"""
未认证(未登录)异常
"""

status_code = status.HTTP_404_NOT_FOUND

def __init__(self, code, message):
Expand All @@ -35,6 +50,7 @@ class AppAuthenticationFailed(AppApiException):
"""
未认证(未登录)异常
"""

status_code = status.HTTP_401_UNAUTHORIZED

def __init__(self, code, message):
Expand All @@ -46,6 +62,7 @@ class AppUnauthorizedFailed(AppApiException):
"""
未授权(没有权限)异常
"""

status_code = status.HTTP_403_FORBIDDEN

def __init__(self, code, message):
Expand All @@ -57,6 +74,7 @@ class AppEmbedIdentityFailed(AppApiException):
"""
嵌入cookie异常
"""

status_code = 460

def __init__(self, code, message):
Expand All @@ -66,8 +84,9 @@ def __init__(self, code, message):

class AppChatNumOutOfBoundsFailed(AppApiException):
"""
访问次数超过今日访问量
访问次数超过今日访问量
"""

status_code = 461

def __init__(self, code, message):
Expand Down
63 changes: 37 additions & 26 deletions apps/common/exception/handle_exception.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# coding=utf-8
"""
@project: qabot
@Author:虎虎
@file: handle_exception.py
@date:2023/9/5 19:29
@desc:
@project: qabot
@Author:虎虎
@file: handle_exception.py
@date:2023/9/5 19:29
@desc:
"""
import logging

import traceback

from rest_framework.exceptions import ValidationError, ErrorDetail, APIException
Expand All @@ -29,19 +29,31 @@ def to_result(key, args, parent_key=None):
:param parent_key 父key
:return: 接口响应对象
"""
error_detail = list(filter(
lambda d: True if isinstance(d, ErrorDetail) else True if isinstance(d, dict) and len(
d.keys()) > 0 else False,
(args[0] if len(args) > 0 else {key: [ErrorDetail(_('Unknown exception'), code='unknown')]}).get(key)))[0]
error_detail = list(
filter(
lambda d: (
True if isinstance(d, ErrorDetail) else True if isinstance(d, dict) and len(d.keys()) > 0 else False
),
(args[0] if len(args) > 0 else {key: [ErrorDetail(_("Unknown exception"), code="unknown")]}).get(key),
)
)[0]

if isinstance(error_detail, dict):
return list(map(lambda k: to_result(k, args=[error_detail],
parent_key=key if parent_key is None else parent_key + '.' + key),
error_detail.keys() if len(error_detail) > 0 else []))[0]

return result.Result(500 if isinstance(error_detail.code, str) else error_detail.code,
message=f"【{key if parent_key is None else parent_key + '.' + key}】为必填参数" if str(
error_detail) == "This field is required." else error_detail)
return list(
map(
lambda k: to_result(
k, args=[error_detail], parent_key=key if parent_key is None else parent_key + "." + key
),
error_detail.keys() if len(error_detail) > 0 else [],
)
)[0]

return result.Result(
500 if isinstance(error_detail.code, str) else error_detail.code,
message=f"【{key if parent_key is None else parent_key + '.' + key}】为必填参数"
if str(error_detail) == "This field is required."
else error_detail,
)


def validation_error_to_result(exc: ValidationError):
Expand All @@ -52,11 +64,10 @@ def validation_error_to_result(exc: ValidationError):
"""
try:
v = find_err_detail(exc.detail)
if v is None:
return result.error(str(exc.detail))
return result.error(str(v))
except Exception as e:
return result.error(str(exc.detail))
message = str(v) if v is not None else str(exc.detail)
except Exception:
message = str(exc.detail)
return result.Result(code=400, message=message, response_status=400)


def find_err_detail(exc_detail):
Expand All @@ -77,7 +88,7 @@ def find_err_detail(exc_detail):
if isinstance(_value, dict) and len(_value.keys()) > 0:
try:
return find_err_detail(ReturnDict(_value, serializer=exc_detail.serializer.fields[key]))
except Exception as e:
except Exception:
return _value
if isinstance(exc_detail, list):
for v in exc_detail:
Expand All @@ -89,7 +100,7 @@ def find_err_detail(exc_detail):
def get_label(key, exc_detail):
try:
return exc_detail.serializer.fields[key].label
except Exception as e:
except Exception:
return key


Expand All @@ -105,6 +116,6 @@ def handle_exception(exc, context):
if issubclass(exception_class, APIException):
return result.error(exc.detail)
if response is None:
maxkb_logger.error(f'{str(exc)}:{traceback.format_exc()}')
return result.error(str(exc))
maxkb_logger.error(f"{str(exc)}:{traceback.format_exc()}")
return result.error(_("Unknown exception"), response_status=500)
return response
57 changes: 34 additions & 23 deletions apps/common/handle/handle_exception.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# coding=utf-8
"""
@project: qabot
@Author:虎
@file: handle_exception.py
@date:2023/9/5 19:29
@desc:
@project: qabot
@Author:虎
@file: handle_exception.py
@date:2023/9/5 19:29
@desc:
"""
import logging

import traceback

from rest_framework.exceptions import ValidationError, ErrorDetail, APIException
Expand All @@ -28,19 +28,31 @@ def to_result(key, args, parent_key=None):
:param parent_key 父key
:return: 接口响应对象
"""
error_detail = list(filter(
lambda d: True if isinstance(d, ErrorDetail) else True if isinstance(d, dict) and len(
d.keys()) > 0 else False,
(args[0] if len(args) > 0 else {key: [ErrorDetail(_('Unknown exception'), code='unknown')]}).get(key)))[0]
error_detail = list(
filter(
lambda d: (
True if isinstance(d, ErrorDetail) else True if isinstance(d, dict) and len(d.keys()) > 0 else False
),
(args[0] if len(args) > 0 else {key: [ErrorDetail(_("Unknown exception"), code="unknown")]}).get(key),
)
)[0]

if isinstance(error_detail, dict):
return list(map(lambda k: to_result(k, args=[error_detail],
parent_key=key if parent_key is None else parent_key + '.' + key),
error_detail.keys() if len(error_detail) > 0 else []))[0]
return list(
map(
lambda k: to_result(
k, args=[error_detail], parent_key=key if parent_key is None else parent_key + "." + key
),
error_detail.keys() if len(error_detail) > 0 else [],
)
)[0]

return result.Result(500 if isinstance(error_detail.code, str) else error_detail.code,
message=f"【{key if parent_key is None else parent_key + '.' + key}】为必填参数" if str(
error_detail) == "This field is required." else error_detail)
return result.Result(
500 if isinstance(error_detail.code, str) else error_detail.code,
message=f"【{key if parent_key is None else parent_key + '.' + key}】为必填参数"
if str(error_detail) == "This field is required."
else error_detail,
)


def validation_error_to_result(exc: ValidationError):
Expand All @@ -51,11 +63,10 @@ def validation_error_to_result(exc: ValidationError):
"""
try:
v = find_err_detail(exc.detail)
if v is None:
return result.error(str(exc.detail))
return result.error(str(v))
except Exception as e:
return result.error(str(exc.detail))
message = str(v) if v is not None else str(exc.detail)
except Exception:
message = str(exc.detail)
return result.Result(code=400, message=message, response_status=400)


def find_err_detail(exc_detail):
Expand Down Expand Up @@ -90,6 +101,6 @@ def handle_exception(exc, context):
if issubclass(exception_class, APIException):
return result.error(exc.detail)
if response is None:
maxkb_logger.error(f'{str(exc)}:{traceback.format_exc()}')
return result.error(str(exc))
maxkb_logger.error(f"{str(exc)}:{traceback.format_exc()}")
return result.error(_("Unknown exception"), response_status=500)
return response
44 changes: 44 additions & 0 deletions apps/common/tests/test_defect_exception_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import json
from unittest.mock import patch

from django.test import SimpleTestCase
from rest_framework.exceptions import ValidationError

from common.exception import handle_exception as exception_handle_module
from common.exception.app_exception import BadRequest400
from common.handle import handle_exception as common_handle_module


class ExceptionHandlerDefectTests(SimpleTestCase):
def test_bad_request_uses_http_400(self):
for module in (exception_handle_module, common_handle_module):
with self.subTest(module=module.__name__):
response = module.handle_exception(BadRequest400(400, "Invalid parameter"), {})

self.assertEqual(400, response.status_code)
self.assertEqual(400, json.loads(response.content)["code"])

def test_validation_error_uses_http_400(self):
for module in (exception_handle_module, common_handle_module):
with self.subTest(module=module.__name__):
response = module.handle_exception(ValidationError("Invalid parameter"), {})

self.assertEqual(400, response.status_code)
self.assertEqual(400, json.loads(response.content)["code"])

def test_unhandled_exception_hides_internal_details(self):
internal_message = "secret database path /opt/maxkb/private"

for module in (exception_handle_module, common_handle_module):
with (
self.subTest(module=module.__name__),
patch(f"{module.__name__}.maxkb_logger") as logger,
):
response = module.handle_exception(RuntimeError(internal_message), {})
payload = json.loads(response.content)

self.assertEqual(500, response.status_code)
self.assertEqual(500, payload["code"])
self.assertNotIn("secret", str(payload["message"]).lower())
self.assertNotIn("/opt/maxkb", str(payload["message"]))
logger.error.assert_called_once()
Original file line number Diff line number Diff line change
Expand Up @@ -237,43 +237,43 @@ def _type_impl_map():
ModelTypeConst.LLM: (
aliyun_bai_lian_llm_model_credential,
BaiLianChatModel,
_("Alibaba Cloud Bailian LLM model"),
_(""),
),
ModelTypeConst.EMBEDDING: (
aliyun_bai_lian_embedding_model_credential,
AliyunBaiLianEmbedding,
_("Alibaba Cloud Bailian embedding model"),
_(""),
),
ModelTypeConst.STT: (
aliyun_bai_lian_stt_model_credential,
AliyunBaiLianSpeechToText,
_("Alibaba Cloud Bailian speech-to-text model"),
_(""),
),
ModelTypeConst.TTS: (
aliyun_bai_lian_tts_model_credential,
AliyunBaiLianTextToSpeech,
_("Alibaba Cloud Bailian TTS model"),
_(""),
),
ModelTypeConst.IMAGE: (qwenvl_model_credential, QwenVLChatModel, _("Alibaba Cloud Bailian vision model")),
ModelTypeConst.IMAGE: (qwenvl_model_credential, QwenVLChatModel, _("")),
ModelTypeConst.TTI: (
qwentti_model_credential,
QwenTextToImageModel,
_("Alibaba Cloud Bailian image generation model"),
_(""),
),
ModelTypeConst.RERANKER: (
aliyun_bai_lian_model_credential,
AliyunBaiLianReranker,
_("Alibaba Cloud Bailian rerank model"),
_(""),
),
ModelTypeConst.TTV: (
aliyun_bai_lian_ttv_model_credential,
GenerationVideoModel,
_("Alibaba Cloud Bailian text-to-video model"),
_(""),
),
ModelTypeConst.ITV: (
aliyun_bai_lian_itv_model_credential,
GenerationVideoModel,
_("Alibaba Cloud Bailian image-to-video model"),
_(""),
),
}

Expand Down
Loading
Loading