diff --git a/apps/application/serializers/application_chat.py b/apps/application/serializers/application_chat.py
index d20c7e3b239..6ded95a7968 100644
--- a/apps/application/serializers/application_chat.py
+++ b/apps/application/serializers/application_chat.py
@@ -68,6 +68,7 @@ class ApplicationChatQuerySerializers(serializers.Serializer):
validators.RegexValidator(regex=re.compile("^and|or$"), message=_("Only supports and|or"), code=500)
],
)
+ order_by = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_("order by"))
def is_valid(self, *, raise_exception=False):
super().is_valid(raise_exception=True)
@@ -101,6 +102,7 @@ def get_query_set(self, select_ids=None):
"star_num": models.IntegerField(),
"trample_num": models.IntegerField(),
"comparer": models.CharField(),
+ "application_chat.chat_record_count": models.IntegerField(),
"application_chat.update_time": models.DateTimeField(),
"application_chat.id": models.UUIDField(),
"application_chat_record_temp.id": models.UUIDField(),
@@ -144,7 +146,13 @@ def get_query_set(self, select_ids=None):
else:
condition = base_condition
- return {"default_queryset": query_set.filter(condition).order_by("-application_chat.update_time")}
+ order_by = self.data.get("order_by")
+ if order_by:
+ field = order_by.lstrip("-")
+ order_by = f"{'-' if order_by.startswith('-') else ''}application_chat.{field}"
+ else:
+ order_by = "-application_chat.update_time"
+ return {"default_queryset": query_set.filter(condition).order_by(order_by)}
def list(self, with_valid=True):
if with_valid:
diff --git a/apps/application/serializers/application_chat_record.py b/apps/application/serializers/application_chat_record.py
index d1a9833ff0c..40bab389c08 100644
--- a/apps/application/serializers/application_chat_record.py
+++ b/apps/application/serializers/application_chat_record.py
@@ -129,6 +129,7 @@ class ApplicationChatRecordQuerySerializers(serializers.Serializer):
application_id = serializers.UUIDField(required=True, label=_("Application ID"))
chat_id = serializers.UUIDField(required=True, label=_("Chat ID"))
order_asc = serializers.BooleanField(required=False, allow_null=True, label=_("Is it in order"))
+ order_by = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_("order by"))
def is_valid(self, *, raise_exception=False):
super().is_valid(raise_exception=True)
@@ -146,7 +147,9 @@ def is_valid(self, *, raise_exception=False):
def list(self, with_valid=True):
if with_valid:
self.is_valid(raise_exception=True)
- order_by = "create_time" if self.data.get("order_asc") is None or self.data.get("order_asc") else "-create_time"
+ order_by = self.data.get("order_by") or (
+ "create_time" if self.data.get("order_asc") is None or self.data.get("order_asc") else "-create_time"
+ )
return [
ChatRecordSerializerModel(chat_record).data
for chat_record in QuerySet(ChatRecord)
@@ -249,7 +252,9 @@ def reset_chat_record(chat_record, show_source, show_exec):
def page(self, current_page: int, page_size: int, with_valid=True, show_source=None, show_exec=None):
if with_valid:
self.is_valid(raise_exception=True)
- order_by = "-create_time" if self.data.get("order_asc") is None or self.data.get("order_asc") else "create_time"
+ order_by = self.data.get("order_by") or (
+ "-create_time" if self.data.get("order_asc") is None or self.data.get("order_asc") else "create_time"
+ )
if show_source is None:
show_source = True
if show_exec is None:
diff --git a/apps/knowledge/serializers/document.py b/apps/knowledge/serializers/document.py
index f6e5088f026..2ec2c4aabe3 100644
--- a/apps/knowledge/serializers/document.py
+++ b/apps/knowledge/serializers/document.py
@@ -659,6 +659,7 @@ def get_query_set(self):
"paragraph_count": models.IntegerField(),
"hit_num": models.IntegerField(),
"last_hit_time": models.DateTimeField(),
+ "nick_name": models.CharField(),
"update_time": models.IntegerField(),
"create_time": models.DateTimeField(),
}
diff --git a/apps/knowledge/serializers/problem.py b/apps/knowledge/serializers/problem.py
index de880031ce0..9735580d29e 100644
--- a/apps/knowledge/serializers/problem.py
+++ b/apps/knowledge/serializers/problem.py
@@ -4,11 +4,12 @@
import uuid_utils.compat as uuid
from django.db import transaction
+from django.db import models
from django.db.models import QuerySet
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
-from common.db.search import native_search, native_page_search
+from common.db.search import native_search, native_page_search, get_dynamics_model
from common.exception.app_exception import AppApiException
from common.utils.common import get_file_content
from knowledge.models import Problem, ProblemParagraphMapping, Paragraph, Knowledge, LocalState, SourceType
@@ -264,6 +265,7 @@ class Query(serializers.Serializer):
workspace_id = serializers.CharField(required=True, label=_("workspace id"))
knowledge_id = serializers.UUIDField(required=True, label=_("knowledge id"))
content = serializers.CharField(required=False, label=_("content"))
+ order_by = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_("order by"))
def is_valid(self, *, raise_exception=False):
super().is_valid(raise_exception=True)
@@ -276,12 +278,30 @@ def is_valid(self, *, raise_exception=False):
def get_query_set(self):
self.is_valid()
- query_set = QuerySet(model=Problem)
+ query_set = QuerySet(
+ model=get_dynamics_model(
+ {
+ "knowledge_id": models.CharField(),
+ "content": models.CharField(),
+ }
+ )
+ )
query_set = query_set.filter(**{"knowledge_id": self.data.get("knowledge_id")})
if "content" in self.data:
query_set = query_set.filter(**{"content__icontains": self.data.get("content")})
- query_set = query_set.order_by("-create_time")
- return query_set
+ order_by_query_set = QuerySet(
+ model=get_dynamics_model(
+ {
+ "paragraph_count": models.IntegerField(),
+ "hit_num": models.IntegerField(),
+ "last_hit_time": models.DateTimeField(),
+ "create_time": models.DateTimeField(),
+ "update_time": models.DateTimeField(),
+ }
+ )
+ )
+ order_by_query_set = order_by_query_set.order_by(self.data.get("order_by") or "-create_time")
+ return {"problem_query_set": query_set, "order_by_query": order_by_query_set}
def list(self):
query_set = self.get_query_set()
diff --git a/apps/knowledge/sql/list_problem.sql b/apps/knowledge/sql/list_problem.sql
index 26edb1ba569..4f21469fd55 100644
--- a/apps/knowledge/sql/list_problem.sql
+++ b/apps/knowledge/sql/list_problem.sql
@@ -4,3 +4,5 @@ SELECT problem.*,
INNER JOIN paragraph p ON ppm.paragraph_id = p.id
WHERE ppm.problem_id = problem.id AND p.local_state <> 'deleted') AS "paragraph_count"
FROM problem problem
+${problem_query_set}
+${order_by_query}
diff --git a/apps/system_manage/serializers/chat_user.py b/apps/system_manage/serializers/chat_user.py
index e039b35f429..cce94b86b10 100644
--- a/apps/system_manage/serializers/chat_user.py
+++ b/apps/system_manage/serializers/chat_user.py
@@ -8,7 +8,8 @@
import uuid_utils.compat as uuid
from django.core import validators
from django.db import transaction
-from django.db.models import Q, QuerySet
+from django.db.models import Q, QuerySet, OuterRef, Subquery, CharField
+from django.db.models.functions import Cast
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
@@ -136,6 +137,7 @@ class Query(serializers.Serializer):
email = serializers.CharField(required=False, label=_("Email"), allow_null=True, allow_blank=True)
source = serializers.CharField(required=False, label=_("Source"), allow_null=True, allow_blank=True)
is_active = serializers.BooleanField(required=False, label=_("Is active"), allow_null=True)
+ order_by = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_("order by"))
def get_query_set(self):
username = self.data.get("username")
@@ -154,7 +156,12 @@ def get_query_set(self):
is_active = self.data.get("is_active", None)
if is_active is not None:
query_set = query_set.filter(is_active=is_active)
- query_set = query_set.order_by("-create_time")
+ quota_sub = ChatUserTokenQuota.objects.filter(user_id=Cast(OuterRef("pk"), output_field=CharField()))
+ query_set = query_set.annotate(
+ total_tokens=Subquery(quota_sub.values("total_tokens")[:1]),
+ used_tokens=Subquery(quota_sub.values("used_tokens")[:1]),
+ )
+ query_set = query_set.order_by(self.data.get("order_by") or "-create_time")
return query_set
def page(self, current_page: int, page_size: int, with_valid=True):
diff --git a/apps/system_manage/views/system_chat_user.py b/apps/system_manage/views/system_chat_user.py
index ba137542806..02eb1a54d1f 100644
--- a/apps/system_manage/views/system_chat_user.py
+++ b/apps/system_manage/views/system_chat_user.py
@@ -250,6 +250,7 @@ def get(self, request: Request, current_page, page_size):
"email": request.query_params.get("email", None),
"source": request.query_params.get("source", None),
"is_active": request.query_params.get("is_active", None),
+ "order_by": request.query_params.get("order_by", None),
"user_id": str(request.user.id),
}
)
diff --git a/apps/trigger/serializers/trigger.py b/apps/trigger/serializers/trigger.py
index f5494934036..57f44e881f2 100644
--- a/apps/trigger/serializers/trigger.py
+++ b/apps/trigger/serializers/trigger.py
@@ -1,11 +1,12 @@
# coding=utf-8
"""
- @project: MaxKB
- @Author:niu
- @file: trigger.py
- @date:2026/1/14 11:48
- @desc:
+@project: MaxKB
+@Author:niu
+@file: trigger.py
+@date:2026/1/14 11:48
+@desc:
"""
+
import os.path
import re
from typing import Dict
@@ -29,25 +30,30 @@
class BatchActiveSerializer(serializers.Serializer):
- id_list = serializers.ListField(required=True, child=serializers.UUIDField(required=True), label=_('id list'))
+ id_list = serializers.ListField(required=True, child=serializers.UUIDField(required=True), label=_("id list"))
is_active = serializers.BooleanField(required=True, label=_("is_active"))
def is_valid(self, *, model=None, raise_exception=False):
super().is_valid(raise_exception=True)
if model is not None:
- id_list = self.data.get('id_list')
+ id_list = self.data.get("id_list")
model_list = QuerySet(model).filter(id__in=id_list)
if len(model_list) != len(id_list):
model_id_list = [str(m.id) for m in model_list]
error_id_list = list(filter(lambda row_id: not model_id_list.__contains__(row_id), id_list))
- raise AppApiException(500, _('The following id does not exist: %s') % ','.join(map(str, error_id_list)))
+ raise AppApiException(500, _("The following id does not exist: %s") % ",".join(map(str, error_id_list)))
class InputField(serializers.Serializer):
- source = serializers.CharField(required=True, label=_("source"), validators=[
- validators.RegexValidator(regex=re.compile("^custom|reference$"),
- message=_("The field only supports custom|reference"), code=500)
- ])
+ source = serializers.CharField(
+ required=True,
+ label=_("source"),
+ validators=[
+ validators.RegexValidator(
+ regex=re.compile("^custom|reference$"), message=_("The field only supports custom|reference"), code=500
+ )
+ ],
+ )
value = ObjectField(required=True, label=_("Variable Value"), model_type_list=[str, list])
@@ -75,10 +81,10 @@ def _validate_input_dict(value, field_name):
return value
def validate_api_input_field_list(self, value):
- return self._validate_input_dict(value, 'api_input_field_list')
+ return self._validate_input_dict(value, "api_input_field_list")
def validate_user_input_field_list(self, value):
- return self._validate_input_dict(value, 'user_input_field_list')
+ return self._validate_input_dict(value, "user_input_field_list")
class ToolTaskParameterSerializer(serializers.Serializer):
@@ -98,15 +104,14 @@ def _validate_input_dict(value, field_name):
return value
def validate_user_input_field_list(self, value):
- return self._validate_input_dict(value, 'user_input_field_list')
+ return self._validate_input_dict(value, "user_input_field_list")
class TriggerValidationMixin:
-
def validate(self, attrs):
# trigger_setting 校验
- trigger_type = attrs.get('trigger_type')
- trigger_setting = attrs.get('trigger_setting')
+ trigger_type = attrs.get("trigger_type")
+ trigger_setting = attrs.get("trigger_setting")
if trigger_type and trigger_setting:
if trigger_type == TriggerTypeChoices.SCHEDULED:
@@ -114,25 +119,23 @@ def validate(self, attrs):
elif trigger_type == TriggerTypeChoices.EVENT:
self._validate_event_setting(trigger_setting)
else:
- raise AppApiException(500, _('Error trigger type'))
+ raise AppApiException(500, _("Error trigger type"))
return attrs
@staticmethod
def _validate_required_field(setting, field_name, trigger_type):
if field_name not in setting:
- raise serializers.ValidationError({
- 'trigger_setting': _('%s type requires %s field') % (trigger_type, field_name)
- })
+ raise serializers.ValidationError(
+ {"trigger_setting": _("%s type requires %s field") % (trigger_type, field_name)}
+ )
@staticmethod
def _validate_non_empty_array(value, field_name):
if not isinstance(value, list):
- raise serializers.ValidationError({
- 'trigger_setting': _('%s must be an array') % field_name})
+ raise serializers.ValidationError({"trigger_setting": _("%s must be an array") % field_name})
if len(value) == 0:
- raise serializers.ValidationError({
- 'trigger_setting': _('%s must not be empty') % field_name})
+ raise serializers.ValidationError({"trigger_setting": _("%s must not be empty") % field_name})
@staticmethod
def _validate_number_range(values, field_name, min_val, max_val):
@@ -142,12 +145,12 @@ def _validate_number_range(values, field_name, min_val, max_val):
if num < min_val or num > max_val:
raise ValueError
except (ValueError, TypeError):
- raise serializers.ValidationError({
- 'trigger_setting': _('%s values must be between %s and %s') % (field_name, min_val, max_val)
- })
+ raise serializers.ValidationError(
+ {"trigger_setting": _("%s values must be between %s and %s") % (field_name, min_val, max_val)}
+ )
def _validate_time_array(self, time_list):
- self._validate_non_empty_array(time_list, 'time')
+ self._validate_non_empty_array(time_list, "time")
for time_str in time_list:
self._validate_time_format(time_str)
@@ -156,146 +159,138 @@ def _validate_time_array(self, time_list):
def _validate_time_format(time_str):
import re
- pattern = r'^([01]\d|2[0-3]):([0-5]\d)$'
+ pattern = r"^([01]\d|2[0-3]):([0-5]\d)$"
if not re.match(pattern, str(time_str)):
- raise serializers.ValidationError({
- 'trigger_setting': _('Invalid time format: %s, must be HH:MM (e.g., 09:00)') % time_str
- })
+ raise serializers.ValidationError(
+ {"trigger_setting": _("Invalid time format: %s, must be HH:MM (e.g., 09:00)") % time_str}
+ )
def _validate_scheduled_setting(self, setting):
- schedule_type = setting.get('schedule_type')
+ schedule_type = setting.get("schedule_type")
- valid_types = ['daily', 'weekly', 'monthly', 'interval', 'cron']
+ valid_types = ["daily", "weekly", "monthly", "interval", "cron"]
if schedule_type not in valid_types:
raise serializers.ValidationError(
- {'trigger_setting': _('schedule_type must be one of %s') % ', '.join(valid_types)
- })
- if schedule_type == 'daily':
+ {"trigger_setting": _("schedule_type must be one of %s") % ", ".join(valid_types)}
+ )
+ if schedule_type == "daily":
self._validate_daily(setting)
- elif schedule_type == 'weekly':
+ elif schedule_type == "weekly":
self._validate_weekly(setting)
- elif schedule_type == 'monthly':
+ elif schedule_type == "monthly":
self._validate_monthly(setting)
- elif schedule_type == 'interval':
+ elif schedule_type == "interval":
self._validate_interval(setting)
- elif schedule_type == 'cron':
+ elif schedule_type == "cron":
self._validate_cron(setting)
def _validate_daily(self, setting):
- self._validate_required_field(setting, 'time', 'daily')
- self._validate_time_array(setting['time'])
+ self._validate_required_field(setting, "time", "daily")
+ self._validate_time_array(setting["time"])
def _validate_weekly(self, setting):
- self._validate_required_field(setting, 'days', 'weekly')
- self._validate_required_field(setting, 'time', 'weekly')
- days = setting['days']
- self._validate_non_empty_array(days, 'days')
- self._validate_number_range(days, 'days', 1, 7)
- self._validate_time_array(setting['time'])
+ self._validate_required_field(setting, "days", "weekly")
+ self._validate_required_field(setting, "time", "weekly")
+ days = setting["days"]
+ self._validate_non_empty_array(days, "days")
+ self._validate_number_range(days, "days", 1, 7)
+ self._validate_time_array(setting["time"])
def _validate_monthly(self, setting):
- self._validate_required_field(setting, 'days', 'monthly')
- self._validate_required_field(setting, 'time', 'monthly')
- days = setting['days']
- self._validate_non_empty_array(days, 'days')
- self._validate_number_range(days, 'days', 1, 31)
- self._validate_time_array(setting['time'])
+ self._validate_required_field(setting, "days", "monthly")
+ self._validate_required_field(setting, "time", "monthly")
+ days = setting["days"]
+ self._validate_non_empty_array(days, "days")
+ self._validate_number_range(days, "days", 1, 31)
+ self._validate_time_array(setting["time"])
def _validate_interval(self, setting):
- self._validate_required_field(setting, 'interval_value', 'interval')
- self._validate_required_field(setting, 'interval_unit', 'interval')
- interval_value = setting['interval_value']
- interval_unit = setting['interval_unit']
+ self._validate_required_field(setting, "interval_value", "interval")
+ self._validate_required_field(setting, "interval_unit", "interval")
+ interval_value = setting["interval_value"]
+ interval_unit = setting["interval_unit"]
try:
value_int = int(interval_value)
if value_int < 1:
raise ValueError
except (ValueError, TypeError):
- raise serializers.ValidationError({
- 'trigger_setting': _('interval_value must be an integer greater than or equal to 1')
- })
- valid_units = ['minutes', 'hours']
+ raise serializers.ValidationError(
+ {"trigger_setting": _("interval_value must be an integer greater than or equal to 1")}
+ )
+ valid_units = ["minutes", "hours"]
if interval_unit not in valid_units:
- raise serializers.ValidationError({
- 'trigger_setting': _('interval_unit must be one of %s') % ', '.join(valid_units)
- })
+ raise serializers.ValidationError(
+ {"trigger_setting": _("interval_unit must be one of %s") % ", ".join(valid_units)}
+ )
@staticmethod
def _validate_cron(setting):
from apscheduler.triggers.cron import CronTrigger
- cron_expression: str = setting.get('cron_expression')
+ cron_expression: str = setting.get("cron_expression")
if not cron_expression:
- raise serializers.ValidationError({
- 'trigger_setting': _('cron type requires cron_expression field')
- })
+ raise serializers.ValidationError({"trigger_setting": _("cron type requires cron_expression field")})
try:
CronTrigger.from_crontab(cron_expression.strip())
except ValueError:
- raise serializers.ValidationError({
- 'trigger_setting': _('Invalid cron expression: %s') % cron_expression
- })
+ raise serializers.ValidationError({"trigger_setting": _("Invalid cron expression: %s") % cron_expression})
@staticmethod
def _validate_event_setting(setting):
- body = setting.get('body')
+ body = setting.get("body")
if body is not None and not isinstance(body, list):
- raise serializers.ValidationError({
- 'trigger_setting': _('body must be an array')
- })
- if not setting.get('token'):
- raise serializers.ValidationError({
- 'trigger_setting': _('token is required for EVENT triggers')
- })
+ raise serializers.ValidationError({"trigger_setting": _("body must be an array")})
+ if not setting.get("token"):
+ raise serializers.ValidationError({"trigger_setting": _("token is required for EVENT triggers")})
class TriggerTaskCreateRequest(serializers.Serializer):
source_type = serializers.ChoiceField(required=True, choices=TriggerTaskTypeChoices)
- source_id = serializers.CharField(required=True, label=_('source_id'))
- is_active = serializers.BooleanField(required=False, label=_('Is active'))
+ source_id = serializers.CharField(required=True, label=_("source_id"))
+ is_active = serializers.BooleanField(required=False, label=_("Is active"))
meta = serializers.DictField(default=dict, required=False)
parameter = serializers.DictField(default=dict, required=False)
def validate(self, attrs):
- source_type = attrs.get('source_type')
- parameter = attrs.get('parameter')
+ source_type = attrs.get("source_type")
+ parameter = attrs.get("parameter")
if source_type == TriggerTaskTypeChoices.APPLICATION:
serializer = ApplicationTaskParameterSerializer(data=parameter)
serializer.is_valid(raise_exception=True)
- attrs['parameter'] = serializer.validated_data
+ attrs["parameter"] = serializer.validated_data
if source_type == TriggerTaskTypeChoices.TOOL:
serializer = ToolTaskParameterSerializer(data=parameter)
serializer.is_valid(raise_exception=True)
- attrs['parameter'] = serializer.validated_data
+ attrs["parameter"] = serializer.validated_data
return attrs
class TriggerTaskEditRequest(serializers.Serializer):
source_type = serializers.ChoiceField(required=False, choices=TriggerTaskTypeChoices)
- source_id = serializers.CharField(required=False, label=_('source_id'))
- is_active = serializers.BooleanField(required=False, label=_('Is active'))
+ source_id = serializers.CharField(required=False, label=_("source_id"))
+ is_active = serializers.BooleanField(required=False, label=_("Is active"))
meta = serializers.DictField(default=dict, required=False)
parameter = serializers.DictField(default=dict, required=False)
def validate(self, attrs):
- source_type = attrs.get('source_type')
- parameter = attrs.get('parameter')
+ source_type = attrs.get("source_type")
+ parameter = attrs.get("parameter")
if source_type == TriggerTaskTypeChoices.APPLICATION:
serializer = ApplicationTaskParameterSerializer(data=parameter)
serializer.is_valid(raise_exception=True)
- attrs['parameter'] = serializer.validated_data
+ attrs["parameter"] = serializer.validated_data
if source_type == TriggerTaskTypeChoices.TOOL:
serializer = ToolTaskParameterSerializer(data=parameter)
serializer.is_valid(raise_exception=True)
- attrs['parameter'] = serializer.validated_data
+ attrs["parameter"] = serializer.validated_data
return attrs
class TriggerEditRequest(TriggerValidationMixin, serializers.Serializer):
- name = serializers.CharField(required=False, label=_('trigger name'))
- desc = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_('trigger description'))
+ name = serializers.CharField(required=False, label=_("trigger name"))
+ desc = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_("trigger description"))
trigger_type = serializers.ChoiceField(required=False, choices=TriggerTypeChoices)
trigger_setting = serializers.DictField(required=False, label=_("trigger setting"))
meta = serializers.DictField(default=dict, required=False)
@@ -304,12 +299,12 @@ class TriggerEditRequest(TriggerValidationMixin, serializers.Serializer):
class TriggerCreateRequest(TriggerValidationMixin, serializers.Serializer):
id = serializers.UUIDField(required=True, label=_("Trigger ID"))
- name = serializers.CharField(required=True, label=_('trigger name'))
- desc = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_('trigger description'))
+ name = serializers.CharField(required=True, label=_("trigger name"))
+ desc = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_("trigger description"))
trigger_type = serializers.ChoiceField(required=True, choices=TriggerTypeChoices)
trigger_setting = serializers.DictField(required=True, label=_("trigger setting"))
meta = serializers.DictField(default=dict, required=False)
- is_active = serializers.BooleanField(required=False, label=_('Is active'))
+ is_active = serializers.BooleanField(required=False, label=_("Is active"))
trigger_task = TriggerTaskCreateRequest(many=True)
@@ -328,13 +323,13 @@ class Meta:
class ApplicationTriggerTaskSerializer(serializers.ModelSerializer):
class Meta:
model = Application
- fields = ['id', 'name', 'work_flow', 'icon', 'type']
+ fields = ["id", "name", "work_flow", "icon", "type"]
class ToolTriggerTaskSerializer(serializers.ModelSerializer):
class Meta:
model = Tool
- fields = ['id', 'name', 'input_field_list', 'icon']
+ fields = ["id", "name", "input_field_list", "icon"]
class TriggerResponse(serializers.ModelSerializer):
@@ -344,7 +339,7 @@ class Meta:
class TriggerSerializer(serializers.Serializer):
- workspace_id = serializers.CharField(required=True, label=_('workspace id'))
+ workspace_id = serializers.CharField(required=True, label=_("workspace id"))
user_id = serializers.UUIDField(required=True, label=_("User ID"))
@transaction.atomic
@@ -357,42 +352,41 @@ def insert(self, instance, with_valid=True):
serializer.is_valid(raise_exception=True)
valid_data = serializer.validated_data
- trigger_id = valid_data.get('id') if valid_data.get('id') else uuid.uuid7()
+ trigger_id = valid_data.get("id") if valid_data.get("id") else uuid.uuid7()
trigger_model = Trigger(
id=trigger_id,
- name=valid_data.get('name'),
- workspace_id=self.data.get('workspace_id'),
- desc=valid_data.get('desc') or '',
- trigger_type=valid_data.get('trigger_type'),
- trigger_setting=valid_data.get('trigger_setting'),
- meta=valid_data.get('meta', {}),
- is_active=valid_data.get('is_active') or False,
- user_id=self.data.get('user_id'),
+ name=valid_data.get("name"),
+ workspace_id=self.data.get("workspace_id"),
+ desc=valid_data.get("desc") or "",
+ trigger_type=valid_data.get("trigger_type"),
+ trigger_setting=valid_data.get("trigger_setting"),
+ meta=valid_data.get("meta", {}),
+ is_active=valid_data.get("is_active") or False,
+ user_id=self.data.get("user_id"),
)
trigger_model.save()
- trigger_tasks = valid_data.get('trigger_task')
+ trigger_tasks = valid_data.get("trigger_task")
if trigger_tasks:
-
is_active_map = self.batch_get_source_active_status(trigger_tasks)
trigger_task_models = [
TriggerTask(
id=uuid.uuid7(),
trigger_id=trigger_id,
- source_type=task_data.get('source_type'),
- source_id=task_data.get('source_id'),
- is_active=is_active_map.get((task_data.get('source_type'), task_data.get('source_id'))) or False,
- parameter=task_data.get('parameter', {}),
- meta=task_data.get('meta', {})
+ source_type=task_data.get("source_type"),
+ source_id=task_data.get("source_id"),
+ is_active=is_active_map.get((task_data.get("source_type"), task_data.get("source_id"))) or False,
+ parameter=task_data.get("parameter", {}),
+ meta=task_data.get("meta", {}),
)
for task_data in trigger_tasks
]
TriggerTask.objects.bulk_create(trigger_task_models)
else:
- raise AppApiException(500, _('Trigger task can not be empty'))
+ raise AppApiException(500, _("Trigger task can not be empty"))
if trigger_model.is_active:
deploy(TriggerModelSerializer(trigger_model).data, **{})
@@ -405,17 +399,17 @@ def batch_get_source_active_status(trigger_tasks: list) -> Dict[tuple, bool]:
返回: {(source_type, source_id): is_active}
"""
config = {
- TriggerTaskTypeChoices.APPLICATION: (Application, 'is_publish'),
- TriggerTaskTypeChoices.TOOL: (Tool, 'is_active'),
+ TriggerTaskTypeChoices.APPLICATION: (Application, "is_publish"),
+ TriggerTaskTypeChoices.TOOL: (Tool, "is_active"),
}
source_ids_by_type = {}
for task_data in trigger_tasks:
- source_type = task_data.get('source_type')
- source_id = task_data.get('source_id')
+ source_type = task_data.get("source_type")
+ source_id = task_data.get("source_id")
if source_type not in config:
- raise AppApiException(500, _('Error source type'))
+ raise AppApiException(500, _("Error source type"))
if source_type not in source_ids_by_type:
source_ids_by_type[source_type] = []
@@ -424,10 +418,10 @@ def batch_get_source_active_status(trigger_tasks: list) -> Dict[tuple, bool]:
is_active_map = {}
for source_type, source_ids in source_ids_by_type.items():
source_model, field = config[source_type]
- source_query_set = QuerySet(source_model).filter(id__in=source_ids).values('id', field)
+ source_query_set = QuerySet(source_model).filter(id__in=source_ids).values("id", field)
for source in source_query_set:
- is_active_map[(source_type, str(source['id']))] = source[field]
+ is_active_map[(source_type, str(source["id"]))] = source[field]
return is_active_map
@@ -435,20 +429,20 @@ def batch_get_source_active_status(trigger_tasks: list) -> Dict[tuple, bool]:
def is_active_source(source_type: str, source_id: str):
config = {
- TriggerTaskTypeChoices.APPLICATION: (Application, 'is_publish'),
- TriggerTaskTypeChoices.TOOL: (Tool, 'is_active'),
+ TriggerTaskTypeChoices.APPLICATION: (Application, "is_publish"),
+ TriggerTaskTypeChoices.TOOL: (Tool, "is_active"),
}
if source_type not in config:
- raise AppApiException(500, _('Error source type'))
+ raise AppApiException(500, _("Error source type"))
source_model, field = config.get(TriggerTaskTypeChoices(source_type))
source = QuerySet(source_model).filter(id=source_id).first()
if not source:
- raise AppApiException(500, _('%s id does not exist') % source_type)
+ raise AppApiException(500, _("%s id does not exist") % source_type)
return getattr(source, field)
class Batch(serializers.Serializer):
- workspace_id = serializers.CharField(required=True, label=_('workspace id'))
+ workspace_id = serializers.CharField(required=True, label=_("workspace id"))
user_id = serializers.UUIDField(required=True, label=_("User ID"))
def is_valid(self, *, raise_exception=False):
@@ -456,7 +450,7 @@ def is_valid(self, *, raise_exception=False):
@transaction.atomic
def batch_delete(self, instance: Dict, with_valid=True):
- from trigger.handler.simple_tools import deploy, undeploy
+ from trigger.handler.simple_tools import undeploy
if with_valid:
BatchSerializer(data=instance).is_valid(model=Trigger, raise_exception=True)
@@ -464,9 +458,7 @@ def batch_delete(self, instance: Dict, with_valid=True):
workspace_id = self.data.get("workspace_id")
trigger_id_list = instance.get("id_list")
trigger_id_list = list(
- QuerySet(Trigger)
- .filter(id__in=trigger_id_list, workspace_id=workspace_id)
- .values_list("id", flat=True)
+ QuerySet(Trigger).filter(id__in=trigger_id_list, workspace_id=workspace_id).values_list("id", flat=True)
)
for trigger_id in trigger_id_list:
trigger = QuerySet(Trigger).filter(id=trigger_id).first()
@@ -489,7 +481,8 @@ def batch_switch(self, instance: Dict, with_valid=True):
trigger_id_list = instance.get("id_list")
is_active = instance.get("is_active")
Trigger.objects.filter(workspace_id=workspace_id, id__in=trigger_id_list, is_active=not is_active).update(
- is_active=is_active)
+ is_active=is_active
+ )
if is_active:
for trigger_id in trigger_id_list:
trigger = QuerySet(Trigger).filter(id=trigger_id).first()
@@ -503,18 +496,18 @@ def batch_switch(self, instance: Dict, with_valid=True):
class TriggerOperateSerializer(serializers.Serializer):
- trigger_id = serializers.UUIDField(required=True, label=_('trigger id'))
+ trigger_id = serializers.UUIDField(required=True, label=_("trigger id"))
user_id = serializers.UUIDField(required=True, label=_("User ID"))
- workspace_id = serializers.CharField(required=True, label=_('workspace id'))
+ workspace_id = serializers.CharField(required=True, label=_("workspace id"))
def is_valid(self, *, raise_exception=False):
super().is_valid(raise_exception=True)
- workspace_id = self.data.get('workspace_id')
- query_set = QuerySet(Trigger).filter(id=self.data.get('trigger_id'))
+ workspace_id = self.data.get("workspace_id")
+ query_set = QuerySet(Trigger).filter(id=self.data.get("trigger_id"))
if workspace_id:
query_set = query_set.filter(workspace_id=workspace_id)
if not query_set.exists():
- raise AppApiException(500, _('Trigger id does not exist'))
+ raise AppApiException(500, _("Trigger id does not exist"))
@transaction.atomic
def edit(self, instance: Dict, with_valid=True):
@@ -523,14 +516,14 @@ def edit(self, instance: Dict, with_valid=True):
if with_valid:
self.is_valid()
TriggerEditRequest(data=instance).is_valid(raise_exception=True)
- trigger_id = self.data.get('trigger_id')
- workspace_id = self.data.get('workspace_id')
+ trigger_id = self.data.get("trigger_id")
+ workspace_id = self.data.get("workspace_id")
trigger = Trigger.objects.filter(workspace_id=workspace_id, id=trigger_id).first()
if not trigger:
- raise serializers.ValidationError(_('Trigger not found'))
+ raise serializers.ValidationError(_("Trigger not found"))
- trigger_direct_edit_field_list = ['name', 'desc', 'trigger_type', 'trigger_setting', 'meta', 'is_active']
- trigger_deploy_edit_field_list = ['trigger_type', 'trigger_setting', 'is_active']
+ trigger_direct_edit_field_list = ["name", "desc", "trigger_type", "trigger_setting", "meta", "is_active"]
+ trigger_deploy_edit_field_list = ["trigger_type", "trigger_setting", "is_active"]
# is need to redeploy
need_redeploy = any(field in instance for field in trigger_deploy_edit_field_list)
@@ -539,24 +532,27 @@ def edit(self, instance: Dict, with_valid=True):
trigger.__setattr__(field, instance.get(field))
trigger.save()
# 处理trigger task
- trigger_tasks = instance.get('trigger_task')
+ trigger_tasks = instance.get("trigger_task")
if trigger_tasks is not None:
# 检查是否为空列表
if not trigger_tasks:
- raise serializers.ValidationError(_('Trigger must have at least one task'))
+ raise serializers.ValidationError(_("Trigger must have at least one task"))
is_active_map = TriggerSerializer.batch_get_source_active_status(trigger_tasks)
- trigger_task_model_list = [TriggerTask(
- id=task_data.get('id') or uuid.uuid7(),
- trigger_id=trigger_id,
- source_type=task_data.get('source_type'),
- source_id=task_data.get('source_id'),
- is_active=is_active_map.get((task_data.get('source_type'), task_data.get('source_id'))) or False,
- parameter=task_data.get('parameter', []),
- meta=task_data.get('meta', {})
- ) for task_data in trigger_tasks]
+ trigger_task_model_list = [
+ TriggerTask(
+ id=task_data.get("id") or uuid.uuid7(),
+ trigger_id=trigger_id,
+ source_type=task_data.get("source_type"),
+ source_id=task_data.get("source_id"),
+ is_active=is_active_map.get((task_data.get("source_type"), task_data.get("source_id"))) or False,
+ parameter=task_data.get("parameter", []),
+ meta=task_data.get("meta", {}),
+ )
+ for task_data in trigger_tasks
+ ]
TriggerTask.objects.filter(trigger_id=trigger_id).delete()
@@ -564,11 +560,11 @@ def edit(self, instance: Dict, with_valid=True):
else:
# 用户没提交 trigger_task 字段,确保数据库中有 task
if not TriggerTask.objects.filter(trigger_id=trigger_id).exists():
- raise serializers.ValidationError(_('Trigger must have at least one task'))
+ raise serializers.ValidationError(_("Trigger must have at least one task"))
# 重新部署触发器任务
if need_redeploy:
- if trigger.is_active and trigger.trigger_type == 'SCHEDULED':
+ if trigger.is_active and trigger.trigger_type == "SCHEDULED":
deploy(TriggerModelSerializer(trigger).data, **{})
else:
undeploy(TriggerModelSerializer(trigger).data, **{})
@@ -576,11 +572,11 @@ def edit(self, instance: Dict, with_valid=True):
return self.one(with_valid=False)
def delete(self):
- from trigger.handler.simple_tools import deploy, undeploy
+ from trigger.handler.simple_tools import undeploy
self.is_valid(raise_exception=True)
- trigger_id = self.data.get('trigger_id')
- trigger = QuerySet(Trigger).filter(workspace_id=self.data.get('workspace_id'), id=trigger_id).first()
+ trigger_id = self.data.get("trigger_id")
+ trigger = QuerySet(Trigger).filter(workspace_id=self.data.get("workspace_id"), id=trigger_id).first()
if trigger:
undeploy(TriggerModelSerializer(trigger).data, **{})
TaskRecord.objects.filter(trigger_id=trigger_id).delete()
@@ -591,8 +587,8 @@ def delete(self):
def one(self, with_valid=True):
if with_valid:
self.is_valid()
- trigger_id = self.data.get('trigger_id')
- workspace_id = self.data.get('workspace_id')
+ trigger_id = self.data.get("trigger_id")
+ workspace_id = self.data.get("workspace_id")
trigger = QuerySet(Trigger).filter(workspace_id=workspace_id, id=trigger_id).first()
trigger_tasks = list(QuerySet(TriggerTask).filter(trigger_id=trigger_id))
@@ -615,54 +611,61 @@ def one(self, with_valid=True):
if tool_ids:
tools = Tool.objects.filter(workspace_id=workspace_id, id__in=tool_ids)
workflows = ToolWorkflow.objects.filter(
- tool_id__in=tools.filter(tool_type='WORKFLOW').values_list('id', flat=True),
- is_publish=True
+ tool_id__in=tools.filter(tool_type="WORKFLOW").values_list("id", flat=True), is_publish=True
)
workflow_dict = {wf.tool_id: wf.work_flow for wf in workflows}
tool_task_list = []
for tool in tools:
tool_data = {
- 'id': str(tool.id),
- 'name': tool.name,
- 'input_field_list': tool.input_field_list,
- 'icon': tool.icon,
- 'tool_type': tool.tool_type
+ "id": str(tool.id),
+ "name": tool.name,
+ "input_field_list": tool.input_field_list,
+ "icon": tool.icon,
+ "tool_type": tool.tool_type,
}
# 如果是工作流类型,添加 work_flow 字段
- if tool.tool_type == 'WORKFLOW':
- tool_data['work_flow'] = workflow_dict.get(tool.id)
+ if tool.tool_type == "WORKFLOW":
+ tool_data["work_flow"] = workflow_dict.get(tool.id)
tool_task_list.append(tool_data)
return {
**TriggerModelSerializer(trigger).data,
- 'trigger_task': trigger_task_list,
- 'application_task_list': application_task_list,
- 'tool_task_list': tool_task_list,
+ "trigger_task": trigger_task_list,
+ "application_task_list": application_task_list,
+ "tool_task_list": tool_task_list,
}
class TriggerQuerySerializer(serializers.Serializer):
- name = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_('Trigger name'))
- type = serializers.CharField(required=False, allow_blank=True, allow_null=True, label=_('Trigger type'))
- is_active = serializers.BooleanField(required=False, allow_null=True, label=_('Is active'))
- task = serializers.CharField(required=False, allow_blank=True, allow_null=True, label=_('Trigger task'))
- create_user = serializers.CharField(required=False, allow_blank=True, allow_null=True, label=_('Create user'))
- workspace_id = serializers.CharField(required=True, label=_('workspace id'))
+ name = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_("Trigger name"))
+ type = serializers.CharField(required=False, allow_blank=True, allow_null=True, label=_("Trigger type"))
+ is_active = serializers.BooleanField(required=False, allow_null=True, label=_("Is active"))
+ task = serializers.CharField(required=False, allow_blank=True, allow_null=True, label=_("Trigger task"))
+ create_user = serializers.CharField(required=False, allow_blank=True, allow_null=True, label=_("Create user"))
+ workspace_id = serializers.CharField(required=True, label=_("workspace id"))
+ order_by = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_("order by"))
def get_query_set(self):
trigger_query_set = QuerySet(
- model=get_dynamics_model({
- 't.name': models.CharField(),
- 'trigger_type': models.CharField(),
- 't.workspace_id': models.CharField(),
- 't.is_active': models.BooleanField(),
- 't.user_id': models.CharField(),
- }))
- task_query_set = QuerySet(model=get_dynamics_model({
- 'trigger_task_str': models.CharField(),
- }))
- trigger_query_set = trigger_query_set.filter(**{'t.workspace_id': self.data.get("workspace_id")})
+ model=get_dynamics_model(
+ {
+ "t.name": models.CharField(),
+ "trigger_type": models.CharField(),
+ "t.workspace_id": models.CharField(),
+ "t.is_active": models.BooleanField(),
+ "t.user_id": models.CharField(),
+ }
+ )
+ )
+ task_query_set = QuerySet(
+ model=get_dynamics_model(
+ {
+ "trigger_task_str": models.CharField(),
+ }
+ )
+ )
+ trigger_query_set = trigger_query_set.filter(**{"t.workspace_id": self.data.get("workspace_id")})
if self.data.get("name"):
- trigger_query_set = trigger_query_set.filter(**{'t.name__icontains': self.data.get("name")})
+ trigger_query_set = trigger_query_set.filter(**{"t.name__icontains": self.data.get("name")})
if self.data.get("type"):
trigger_query_set = trigger_query_set.filter(trigger_type=self.data.get("type"))
if self.data.get("is_active") is not None:
@@ -672,17 +675,43 @@ def get_query_set(self):
if self.data.get("create_user"):
trigger_query_set = trigger_query_set.filter(**{"t.user_id": self.data.get("create_user")})
- return {"trigger_query_set": trigger_query_set, "task_query_set": task_query_set}
+ order_by_query_set = QuerySet(
+ model=get_dynamics_model(
+ {
+ "create_time": models.DateTimeField(),
+ "update_time": models.DateTimeField(),
+ "next_run_time": models.DateTimeField(),
+ }
+ )
+ )
+ order_by = self.data.get("order_by", "")
+ if order_by:
+ order_by_query_set = order_by_query_set.order_by(order_by)
+ else:
+ order_by_query_set = order_by_query_set.order_by("-create_time")
+
+ return {
+ "trigger_query_set": trigger_query_set,
+ "task_query_set": task_query_set,
+ "order_by_query": order_by_query_set,
+ }
def page(self, current_page: int, page_size: int, with_valid=True):
if with_valid:
self.is_valid(raise_exception=True)
- return native_page_search(current_page, page_size, self.get_query_set(), get_file_content(
- os.path.join(PROJECT_DIR, "apps", "trigger", "sql", "get_trigger_page_list.sql")
- ))
+ return native_page_search(
+ current_page,
+ page_size,
+ self.get_query_set(),
+ get_file_content(os.path.join(PROJECT_DIR, "apps", "trigger", "sql", "get_trigger_page_list.sql")),
+ )
def list(self, with_valid=True):
if with_valid:
self.is_valid(raise_exception=True)
- return native_search(self.get_query_set(), select_string=get_file_content(
- os.path.join(PROJECT_DIR, "apps", "trigger", "sql", "get_trigger_page_list.sql")))
+ return native_search(
+ self.get_query_set(),
+ select_string=get_file_content(
+ os.path.join(PROJECT_DIR, "apps", "trigger", "sql", "get_trigger_page_list.sql")
+ ),
+ )
diff --git a/apps/trigger/sql/get_trigger_page_list.sql b/apps/trigger/sql/get_trigger_page_list.sql
index ec33d6fa6d9..80bae643295 100644
--- a/apps/trigger/sql/get_trigger_page_list.sql
+++ b/apps/trigger/sql/get_trigger_page_list.sql
@@ -42,4 +42,4 @@ FROM (SELECT t.id,
${trigger_query_set}
) AS sub
${task_query_set}
-ORDER BY sub.create_time DESC
\ No newline at end of file
+${order_by_query}
\ No newline at end of file
diff --git a/apps/trigger/views/trigger.py b/apps/trigger/views/trigger.py
index dd3cb6bbcf2..f505c9562ff 100644
--- a/apps/trigger/views/trigger.py
+++ b/apps/trigger/views/trigger.py
@@ -280,6 +280,7 @@ def get(self, request: Request, workspace_id: str, current_page: int, page_size:
"type": request.query_params.get("type"),
"is_active": request.query_params.get("is_active"),
"create_user": request.query_params.get("create_user"),
+ "order_by": request.query_params.get("order_by"),
}
).page(current_page, page_size)
)
diff --git a/apps/users/serializers/user.py b/apps/users/serializers/user.py
index 291c2be3ef6..5acaa29bc10 100644
--- a/apps/users/serializers/user.py
+++ b/apps/users/serializers/user.py
@@ -365,6 +365,7 @@ class Query(serializers.Serializer):
label=_("Source"),
allow_blank=True,
)
+ order_by = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_("order by"))
def get_query_set(self):
username = self.data.get("username")
@@ -383,7 +384,7 @@ def get_query_set(self):
query_set = query_set.filter(is_active=is_active)
if source is not None:
query_set = query_set.filter(source=source)
- query_set = query_set.order_by("-create_time")
+ query_set = query_set.order_by(self.data.get("order_by") or "-create_time")
return query_set
def list(self, with_valid=True):
diff --git a/ui/src/views/application-detail/chat-log/index.vue b/ui/src/views/application-detail/chat-log/index.vue
index 67ec499a72d..bb3e30f02dc 100644
--- a/ui/src/views/application-detail/chat-log/index.vue
+++ b/ui/src/views/application-detail/chat-log/index.vue
@@ -228,7 +228,7 @@ onMounted(() => loadChatLogs())
-
+
{{ row.asker?.username || '-' }}
diff --git a/ui/src/views/knowledge-detail/library/document/index.vue b/ui/src/views/knowledge-detail/library/document/index.vue
index 93b03d778d8..c12f5a570b8 100644
--- a/ui/src/views/knowledge-detail/library/document/index.vue
+++ b/ui/src/views/knowledge-detail/library/document/index.vue
@@ -465,7 +465,7 @@ onBeforeUnmount(() => {
{{ datetimeFormat(row.last_hit_time) }}
-
+
{{ datetimeFormat(row.update_time) }}