diff --git a/apps/common/middleware/gzip.py b/apps/common/middleware/gzip.py
new file mode 100644
index 000000000..229ec09b3
--- /dev/null
+++ b/apps/common/middleware/gzip.py
@@ -0,0 +1,84 @@
+# coding=utf-8
+"""
+ @project: MaxKB
+ @Author:虎
+ @file: gzip.py
+ @date:2025/2/27 10:03
+ @desc:
+"""
+from django.utils.cache import patch_vary_headers
+from django.utils.deprecation import MiddlewareMixin
+from django.utils.regex_helper import _lazy_re_compile
+from django.utils.text import compress_sequence, compress_string
+
+re_accepts_gzip = _lazy_re_compile(r"\bgzip\b")
+
+
+class GZipMiddleware(MiddlewareMixin):
+ """
+ Compress content if the browser allows gzip compression.
+ Set the Vary header accordingly, so that caches will base their storage
+ on the Accept-Encoding header.
+ """
+
+ max_random_bytes = 100
+
+ def process_response(self, request, response):
+ if request.method != 'GET' or request.path.startswith('/api'):
+ return response
+ # It's not worth attempting to compress really short responses.
+ if not response.streaming and len(response.content) < 200:
+ return response
+
+ # Avoid gzipping if we've already got a content-encoding.
+ if response.has_header("Content-Encoding"):
+ return response
+
+ patch_vary_headers(response, ("Accept-Encoding",))
+
+ ae = request.META.get("HTTP_ACCEPT_ENCODING", "")
+ if not re_accepts_gzip.search(ae):
+ return response
+
+ if response.streaming:
+ if response.is_async:
+ # pull to lexical scope to capture fixed reference in case
+ # streaming_content is set again later.
+ orignal_iterator = response.streaming_content
+
+ async def gzip_wrapper():
+ async for chunk in orignal_iterator:
+ yield compress_string(
+ chunk,
+ max_random_bytes=self.max_random_bytes,
+ )
+
+ response.streaming_content = gzip_wrapper()
+ else:
+ response.streaming_content = compress_sequence(
+ response.streaming_content,
+ max_random_bytes=self.max_random_bytes,
+ )
+ # Delete the `Content-Length` header for streaming content, because
+ # we won't know the compressed size until we stream it.
+ del response.headers["Content-Length"]
+ else:
+ # Return the compressed content only if it's actually shorter.
+ compressed_content = compress_string(
+ response.content,
+ max_random_bytes=self.max_random_bytes,
+ )
+ if len(compressed_content) >= len(response.content):
+ return response
+ response.content = compressed_content
+ response.headers["Content-Length"] = str(len(response.content))
+
+ # If there is a strong ETag, make it weak to fulfill the requirements
+ # of RFC 9110 Section 8.8.1 while also allowing conditional request
+ # matches on ETags.
+ etag = response.get("ETag")
+ if etag and etag.startswith('"'):
+ response.headers["ETag"] = "W/" + etag
+ response.headers["Content-Encoding"] = "gzip"
+
+ return response
diff --git a/apps/dataset/serializers/document_serializers.py b/apps/dataset/serializers/document_serializers.py
index b8c013418..963c033d4 100644
--- a/apps/dataset/serializers/document_serializers.py
+++ b/apps/dataset/serializers/document_serializers.py
@@ -1106,7 +1106,7 @@ class DocumentSerializers(ApiMixin, serializers.Serializer):
'order_by_query': QuerySet(Document).order_by('-create_time', 'id')
}, select_string=get_file_content(
os.path.join(PROJECT_DIR, "apps", "dataset", 'sql', 'list_document.sql')),
- with_search_one=False), dataset_id
+ with_search_one=False), dataset_id
@staticmethod
def _batch_sync(document_id_list: List[str]):
@@ -1263,6 +1263,7 @@ def save_image(image_list):
exist_image_list = [str(i.get('id')) for i in
QuerySet(Image).filter(id__in=[i.id for i in image_list]).values('id')]
save_image_list = [image for image in image_list if not exist_image_list.__contains__(str(image.id))]
+ save_image_list = list({img.id: img for img in save_image_list}.values())
if len(save_image_list) > 0:
QuerySet(Image).bulk_create(save_image_list)
diff --git a/apps/smartdoc/settings/base.py b/apps/smartdoc/settings/base.py
index cda860b76..b804c637e 100644
--- a/apps/smartdoc/settings/base.py
+++ b/apps/smartdoc/settings/base.py
@@ -55,7 +55,7 @@ MIDDLEWARE = [
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
- 'django.middleware.gzip.GZipMiddleware',
+ 'common.middleware.gzip.GZipMiddleware',
'common.middleware.static_headers_middleware.StaticHeadersMiddleware',
'common.middleware.cross_domain_middleware.CrossDomainMiddleware'
diff --git a/ui/src/components/ai-chat/ExecutionDetailDialog.vue b/ui/src/components/ai-chat/ExecutionDetailDialog.vue
index 4f97c0085..76917fc9d 100644
--- a/ui/src/components/ai-chat/ExecutionDetailDialog.vue
+++ b/ui/src/components/ai-chat/ExecutionDetailDialog.vue
@@ -454,6 +454,7 @@
:data="paragraph.metadata"
:content="paragraph.page_content"
:index="paragraphIndex"
+ :score="paragraph.metadata?.relevance_score"
/>
diff --git a/ui/src/components/ai-chat/component/ParagraphCard.vue b/ui/src/components/ai-chat/component/ParagraphCard.vue
index de8010deb..82fbd13cd 100644
--- a/ui/src/components/ai-chat/component/ParagraphCard.vue
+++ b/ui/src/components/ai-chat/component/ParagraphCard.vue
@@ -9,10 +9,10 @@