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
36 changes: 23 additions & 13 deletions apps/application/api/application_chat.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
# coding=utf-8
"""
@project: MaxKB
@Author:虎虎
@file: application_chat.py
@date:2025/6/10 13:54
@desc:
@project: MaxKB
@Author:虎虎
@file: application_chat.py
@date:2025/6/10 13:54
@desc:
"""

from django.utils.translation import gettext_lazy as _
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import OpenApiParameter

from application.serializers.application_chat import ApplicationChatQuerySerializers, \
ApplicationChatResponseSerializers, ApplicationChatRecordExportRequest
from application.serializers.application_chat import (
ApplicationChatQuerySerializers,
ApplicationChatResponseSerializers,
ApplicationChatRecordExportRequest,
)
from common.mixins.api_mixin import APIMixin
from common.result import ResultSerializer, ResultPageSerializer

Expand All @@ -38,16 +42,17 @@ def get_parameters():
name="workspace_id",
description="工作空间id",
type=OpenApiTypes.STR,
location='path',
location="path",
required=True,
),
OpenApiParameter(
name="application_id",
description="application ID",
type=OpenApiTypes.STR,
location='path',
location="path",
required=True,
), OpenApiParameter(
),
OpenApiParameter(
name="start_time",
description="start Time",
type=OpenApiTypes.STR,
Expand All @@ -71,6 +76,12 @@ def get_parameters():
type=OpenApiTypes.STR,
required=False,
),
OpenApiParameter(
name="source",
description="source",
type=OpenApiTypes.STR,
required=False,
),
OpenApiParameter(
name="min_star",
description=_("Minimum number of likes"),
Expand Down Expand Up @@ -109,17 +120,16 @@ def get_parameters():
name="current_page",
description=_("Current page"),
type=OpenApiTypes.INT,
location='path',
location="path",
required=True,
),
OpenApiParameter(
name="page_size",
description=_("Page size"),
type=OpenApiTypes.INT,
location='path',
location="path",
required=True,
),

]

@staticmethod
Expand Down
21 changes: 21 additions & 0 deletions apps/application/serializers/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,16 @@ class ApplicationQueryRequest(serializers.Serializer):
label=_("Publish status"),
choices=[("published", _("Published")), ("unpublished", _("Unpublished"))],
)
status = serializers.ChoiceField(
required=False,
label=_("Publish status"),
choices=[
("published", _("Published")),
("unpublished", _("Unpublished")),
("on_portal", _("On portal")),
],
)
type = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_("Application type"))
user_id = serializers.UUIDField(required=False, label=_("User ID"))
create_user = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_("create user"))

Expand Down Expand Up @@ -584,6 +594,14 @@ def get_query_set(self, instance: Dict, workspace_manage: bool, is_x_pack_ee: bo
if publish_status is not None:
is_publish = True if publish_status == "published" else False
application_query_set = application_query_set.filter(is_publish=is_publish)
status = instance.get("status")
if status is not None:
if status == "unpublished":
application_query_set = application_query_set.filter(is_publish=False)
elif status == "published":
application_query_set = application_query_set.filter(is_publish=True)
elif status == "on_portal":
application_query_set = application_query_set.filter(is_portal=True)
if workspace_id is not None:
folder_query_set = folder_query_set.filter(workspace_id=workspace_id)
application_query_set = application_query_set.filter(workspace_id=workspace_id)
Expand All @@ -599,6 +617,9 @@ def get_query_set(self, instance: Dict, workspace_manage: bool, is_x_pack_ee: bo
application_query_set = application_query_set.filter(desc__contains=desc)
if create_user is not None:
application_query_set = application_query_set.filter(user_id=create_user)
application_type = instance.get("type")
if application_type:
application_query_set = application_query_set.filter(type=application_type)
application_custom_sql_query_set = application_query_set
application_query_set = application_query_set.order_by("-create_time")

Expand Down
4 changes: 4 additions & 0 deletions apps/application/serializers/application_chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class ApplicationChatQuerySerializers(serializers.Serializer):
start_time = serializers.DateField(format="%Y-%m-%d", label=_("Start time"))
end_time = serializers.DateField(format="%Y-%m-%d", label=_("End time"))
application_id = serializers.UUIDField(required=True, label=_("Application ID"))
source = serializers.CharField(required=False, allow_blank=True, allow_null=True, label=_("Source"))
min_star = serializers.IntegerField(required=False, min_value=0, label=_("Minimum number of likes"))
min_trample = serializers.IntegerField(required=False, min_value=0, label=_("Minimum number of clicks"))
comparer = serializers.CharField(
Expand Down Expand Up @@ -96,6 +97,7 @@ def get_query_set(self, select_ids=None):
"application_chat.application_id": models.CharField(),
"application_chat.abstract": models.CharField(),
"application_chat.asker": models.JSONField(),
"application_chat.source": models.JSONField(),
"star_num": models.IntegerField(),
"trample_num": models.IntegerField(),
"comparer": models.CharField(),
Expand All @@ -113,6 +115,8 @@ def get_query_set(self, select_ids=None):
}
if "abstract" in self.data and self.data.get("abstract") is not None:
base_query_dict["application_chat.abstract__icontains"] = self.data.get("abstract")
if "source" in self.data and self.data.get("source") is not None:
base_query_dict["application_chat.source__type"] = self.data.get("source")
if select_ids is not None and len(select_ids) > 0:
base_query_dict["application_chat.id__in"] = select_ids
base_condition = Q(**base_query_dict)
Expand Down
4 changes: 4 additions & 0 deletions apps/knowledge/serializers/knowledge.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ class Query(serializers.Serializer):
max_length=256,
min_length=1,
)
type = serializers.IntegerField(required=False, label=_("knowledge type"), allow_null=True)
user_id = serializers.UUIDField(required=False, label=_("user id"), allow_null=True)
scope = serializers.CharField(required=False, label=_("knowledge scope"), allow_null=True)
create_user = serializers.UUIDField(required=False, label=_("create user"), allow_null=True)
Expand All @@ -250,6 +251,7 @@ def get_query_set(self, workspace_manage, is_x_pack_ee):
{
"temp.name": models.CharField(),
"temp.desc": models.CharField(),
"temp.type": models.IntegerField(),
"document_temp.char_length": models.IntegerField(),
"temp.create_time": models.DateTimeField(),
"temp.user_id": models.CharField(),
Expand All @@ -268,6 +270,8 @@ def get_query_set(self, workspace_manage, is_x_pack_ee):
if "name" in self.data and self.data.get("name") is not None:
query_set = query_set.filter(**{"temp.name__icontains": self.data.get("name")})
folder_query_set = folder_query_set.filter(**{"name__icontains": self.data.get("name")})
if "type" in self.data and self.data.get("type") is not None:
query_set = query_set.filter(**{"temp.type": self.data.get("type")})
if "workspace_id" in self.data and self.data.get("workspace_id") is not None:
query_set = query_set.filter(**{"temp.workspace_id": self.data.get("workspace_id")})
folder_query_set = folder_query_set.filter(**{"workspace_id": self.data.get("workspace_id")})
Expand Down
1 change: 1 addition & 0 deletions apps/knowledge/views/knowledge.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ def get(self, request: Request, workspace_id: str, current_page: int, page_size:
"folder_id": request.query_params.get("folder_id"),
"name": request.query_params.get("name"),
"desc": request.query_params.get("desc"),
"type": request.query_params.get("type"),
"scope": KnowledgeScope.WORKSPACE,
"user_id": request.user.id,
"create_user": request.query_params.get("create_user"),
Expand Down
11 changes: 8 additions & 3 deletions apps/tools/serializers/tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -623,7 +623,7 @@ def convert_value(name: str, value: str, _type: str, is_required: bool):
return None
try:
return common_convert_value(_type, value)
except Exception as e:
except Exception:
raise AppApiException(
500,
_("Field: {name} Type: {type} Value: {value} Type conversion error").format(
Expand Down Expand Up @@ -1087,7 +1087,7 @@ def update_template_workflow(self, tool_id: str):
tool_instance_bytes = self.data.get("file").read()
try:
tool_instance = RestrictedUnpickler(io.BytesIO(tool_instance_bytes)).load()
except Exception as e:
except Exception:
raise AppApiException(1001, _("Unsupported file format"))
tool = tool_instance.tool
tool["id"] = tool_id
Expand All @@ -1109,7 +1109,7 @@ def import_(self, scope=ToolScope.WORKSPACE, name=None, source=None):
tool_instance_bytes = self.data.get("file").read()
try:
tool_instance = RestrictedUnpickler(io.BytesIO(tool_instance_bytes)).load()
except Exception as e:
except Exception:
raise AppApiException(1001, _("Unsupported file format"))
if self.data.get("folder_id") is None:
folder_id = self.data.get("workspace_id")
Expand Down Expand Up @@ -1731,6 +1731,7 @@ class Query(serializers.Serializer):
child=serializers.CharField(), required=False, label=_("tool type list"), allow_null=True, allow_empty=True
)
create_user = serializers.UUIDField(required=False, label=_("create user"), allow_null=True)
is_active = serializers.BooleanField(required=False, label=_("is active"), allow_null=True)

def page_tool(self, current_page: int, page_size: int):
self.is_valid(raise_exception=True)
Expand Down Expand Up @@ -1792,6 +1793,10 @@ def get_query_set(self, workspace_manage, is_x_pack_ee):
if scope is not None:
tool_query_set = tool_query_set.filter(scope=scope)

is_active = self.data.get("is_active")
if is_active is not None:
tool_query_set = tool_query_set.filter(is_active=is_active)

tool_type_list = self.data.get("tool_type_list")
if tool_type_list:
tool_query_set = tool_query_set.filter(tool_type__in=tool_type_list)
Expand Down
1 change: 1 addition & 0 deletions apps/tools/views/tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ def get(self, request: Request, workspace_id: str, current_page: int, page_size:
"tool_type": request.query_params.get("tool_type"),
"user_id": request.user.id,
"create_user": request.query_params.get("create_user"),
"is_active": request.query_params.get("is_active"),
}
).page_tool_with_folders(current_page, page_size)
)
Expand Down
17 changes: 11 additions & 6 deletions ui/src/components/business/related-resources-drawer/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ const resourceTypeOptions = Object.entries(resourceLabels).map(([value, label])
const searchFields = [
{ label: '名称', value: 'resource_name' },
{ label: '创建者', value: 'user_name' },
{ label: '类型', value: 'resource_type', multiple: true, options: resourceTypeOptions },
]
const relationTabs = [
{ value: 'dependency', label: '依赖' },
Expand All @@ -53,12 +52,13 @@ const resourceRows = computed(() =>
)
const pagination = ref({ currentPage: 1, pageSize: 20, total: 0 })
const searchQuery = ref<Dict<unknown>>({})
const selectedResourceType = ref<string | null>(null)

function loadResources() {
if (!visible.value || !targetResource.value) return
const isDependency = relationDirection.value === 'dependency'
const { resource_type, ...query } = searchQuery.value
if (resource_type) query[isDependency ? 'target_type' : 'source_type'] = resource_type
const query: Dict<unknown> = { ...searchQuery.value }
if (selectedResourceType.value) query[isDependency ? 'target_type' : 'source_type'] = [selectedResourceType.value]
if (showWorkspace.value && selectedWorkspaceIds.value.length) query.workspace_ids = JSON.stringify(selectedWorkspaceIds.value)
const request = isDependency ? props.api.getUsingResources : props.api.getUsedByResources
loading.value = true
Expand All @@ -80,16 +80,17 @@ function searchResources(query?: Dict<unknown>) {

function changeDirection() {
searchQuery.value = {}
selectedResourceType.value = null
selectedWorkspaceIds.value = []
relatedResources.value = []
pagination.value = { currentPage: 1, pageSize: 20, total: 0 }
loadResources()
}

/* 工作空间筛选:确认或重置后从第一页查询。 */
/* 表头筛选:确认或重置后从第一页查询。 */
const workspaceOptions = ref<OptionItem<string>[]>([])
const selectedWorkspaceIds = ref<string[]>([])
function handleWorkspaceFilterChange() {
function handleFilterChange() {
pagination.value.currentPage = 1
loadResources()
}
Expand Down Expand Up @@ -130,6 +131,7 @@ function resetData() {
relatedResources.value = []
pagination.value = { currentPage: 1, pageSize: 20, total: 0 }
searchQuery.value = {}
selectedResourceType.value = null
relationDirection.value = 'dependency'
workspaceOptions.value = []
selectedWorkspaceIds.value = []
Expand Down Expand Up @@ -183,6 +185,9 @@ defineExpose({ open, close })
</el-table-column>
<el-table-column prop="desc" label="描述" min-width="160" show-overflow-tooltip />
<el-table-column label="类型" min-width="100">
<template #header>
<MkTableFilter mode="single" v-model="selectedResourceType" label="类型" :options="resourceTypeOptions" @change="handleFilterChange" />
</template>
<template #default="{ row }">{{ resourceLabels[row.resourceType as ResourceType] }}</template>
</el-table-column>
<el-table-column v-if="showWorkspace" prop="workspace_name" label="工作空间" min-width="140" show-overflow-tooltip>
Expand All @@ -193,7 +198,7 @@ defineExpose({ open, close })
v-model="selectedWorkspaceIds"
label="工作空间"
:options="workspaceOptions"
@change="handleWorkspaceFilterChange"
@change="handleFilterChange"
/>
</template>
</el-table-column>
Expand Down
54 changes: 11 additions & 43 deletions ui/src/views/application-detail/chat-log/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ const { application } = useApplicationDetailContext()
const loading = ref(false)
const chatLogs = ref<ChatLog[]>([])
const paginationConfig = ref({ currentPage: 1, pageSize: 20, total: 0 })
const sourceOptions: OptionItem<string>[] = Object.entries(CHAT_LOG_SOURCE_LABELS).map(([value, label]) => ({ value, label }))
const searchFields: OptionItem<string>[] = [
{ label: '摘要', value: 'abstract' },
{ label: '用户', value: 'username' },
{ label: '来源', value: 'source', options: sourceOptions },
]
const chatLogSearchQuery = ref<Dict<unknown>>()
const chatLogDateQuery = ref({ start_time: beforeDay(7), end_time: beforeDay(0) })
Expand All @@ -44,9 +46,12 @@ function handleDateFilterChange({ startTime, endTime }: MkDateRangeValue) {
return loadChatLogs()
}

// 反馈筛选的草稿、重置和确认由 MkTableFilter 管理
const feedbackResetValue = { min_star: 0, min_trample: 0 }
const feedbackFilter = ref({ ...feedbackResetValue })
// 表头用户反馈筛选,单选复用 min_star/min_trample 参数
const selectedFeedback = ref<string | null>(null)
const feedbackOptions: OptionItem<string>[] = [
{ label: '赞同', value: 'star' },
{ label: '反对', value: 'trample' },
]

function handleFeedbackChange() {
paginationConfig.value.currentPage = 1
Expand All @@ -56,7 +61,8 @@ function handleFeedbackChange() {
const chatLogQuery = computed<Dict<unknown>>(() => ({
...chatLogSearchQuery.value,
...chatLogDateQuery.value,
...feedbackFilter.value,
...(selectedFeedback.value === 'star' ? { min_star: 1 } : {}),
...(selectedFeedback.value === 'trample' ? { min_trample: 1 } : {}),
comparer: 'and',
}))

Expand Down Expand Up @@ -184,45 +190,7 @@ onMounted(() => loadChatLogs())
<el-table-column prop="chat_record_count" label="对话次数" width="100" />
<el-table-column width="140">
<template #header>
<MkTableFilter
v-model="feedbackFilter"
mode="custom"
label="用户反馈"
width="180"
:reset-value="feedbackResetValue"
@change="handleFeedbackChange"
>
<template #default="{ value }">
<div class="space-y-3">
<div class="flex-between gap-2">
<span>赞同 ≥</span>
<el-input-number
v-model="value.min_star"
class="w-25!"
size="small"
controls-position="right"
align="left"
:min="0"
:value-on-clear="0"
step-strictly
/>
</div>
<div class="flex-between gap-2">
<span>反对 ≥</span>
<el-input-number
v-model="value.min_trample"
class="w-25!"
size="small"
controls-position="right"
align="left"
:min="0"
:value-on-clear="0"
step-strictly
/>
</div>
</div>
</template>
</MkTableFilter>
<MkTableFilter v-model="selectedFeedback" mode="single" label="用户反馈" :options="feedbackOptions" @change="handleFeedbackChange" />
</template>
<template #default="{ row }">
<span v-if="!row.star_num && !row.trample_num">-</span>
Expand Down
Loading
Loading