mirror of
https://github.com/Tencent/WeKnora.git
synced 2026-09-24 16:29:01 +08:00
feat: Enhance knowledge base functionality by adding document search capability and updating related API and UI components
This commit is contained in:
@@ -54,7 +54,7 @@ export function createManualKnowledge(kbId: string, data: { title: string; conte
|
||||
|
||||
export function listKnowledgeFiles(
|
||||
kbId: string,
|
||||
params: { page: number; page_size: number; tag_id?: string },
|
||||
params: { page: number; page_size: number; tag_id?: string; keyword?: string },
|
||||
) {
|
||||
const query = new URLSearchParams();
|
||||
query.append('page', String(params.page));
|
||||
@@ -62,6 +62,9 @@ export function listKnowledgeFiles(
|
||||
if (params.tag_id) {
|
||||
query.append('tag_id', params.tag_id);
|
||||
}
|
||||
if (params.keyword) {
|
||||
query.append('keyword', params.keyword);
|
||||
}
|
||||
const qs = query.toString();
|
||||
return get(`/api/v1/knowledge-bases/${kbId}/knowledge?${qs}`);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ export default function (knowledgeBaseId?: string) {
|
||||
file_type: ""
|
||||
});
|
||||
const getKnowled = (
|
||||
query: { page: number; page_size: number; tag_id?: string } = { page: 1, page_size: 35 },
|
||||
query: { page: number; page_size: number; tag_id?: string; keyword?: string } = { page: 1, page_size: 35 },
|
||||
kbId?: string,
|
||||
) => {
|
||||
const targetKbId = kbId || knowledgeBaseId;
|
||||
|
||||
@@ -182,6 +182,7 @@ export default {
|
||||
textMarkdownFormat: 'text, markdown format files, max 200K',
|
||||
dragFileNotText: 'Please drag files instead of text or links',
|
||||
searchPlaceholder: 'Search knowledge bases...',
|
||||
docSearchPlaceholder: 'Search document names...',
|
||||
noMatch: 'No matching knowledge base found',
|
||||
noKnowledge: 'No knowledge bases available',
|
||||
loadingFailed: 'Failed to load knowledge bases'
|
||||
|
||||
@@ -182,6 +182,7 @@ export default {
|
||||
textMarkdownFormat: 'Файлы text, markdown формата, не более 200 КБ',
|
||||
dragFileNotText: 'Пожалуйста, перетащите файлы, а не текст или ссылки',
|
||||
searchPlaceholder: 'Поиск по базам знаний...',
|
||||
docSearchPlaceholder: 'Поиск документов...',
|
||||
noMatch: 'Совпадающих баз знаний не найдено',
|
||||
noKnowledge: 'Нет доступных баз знаний',
|
||||
loadingFailed: 'Не удалось загрузить базы знаний'
|
||||
|
||||
@@ -185,6 +185,7 @@ export default {
|
||||
textMarkdownFormat: "text、markdown格式文件,不超过200K",
|
||||
dragFileNotText: "请拖拽文件而不是文本或链接",
|
||||
searchPlaceholder: "搜索知识库...",
|
||||
docSearchPlaceholder: "搜索文档名称...",
|
||||
noMatch: "未找到匹配的知识库",
|
||||
noKnowledge: "暂无可用知识库",
|
||||
loadingFailed: "加载知识库失败",
|
||||
|
||||
@@ -57,6 +57,8 @@ const tagHasMore = ref(false);
|
||||
const tagLoadingMore = ref(false);
|
||||
const tagTotal = ref(0);
|
||||
let tagSearchDebounce: ReturnType<typeof setTimeout> | null = null;
|
||||
let docSearchDebounce: ReturnType<typeof setTimeout> | null = null;
|
||||
const docSearchKeyword = ref('');
|
||||
type TagInputInstance = ComponentPublicInstance<{ focus: () => void; select: () => void }>;
|
||||
const tagDropdownOptions = computed(() => {
|
||||
const options = [
|
||||
@@ -147,6 +149,7 @@ const loadKnowledgeFiles = (kbIdValue: string) => {
|
||||
page: 1,
|
||||
page_size: pageSize,
|
||||
tag_id: selectedTagId.value || undefined,
|
||||
keyword: docSearchKeyword.value ? docSearchKeyword.value.trim() : undefined,
|
||||
},
|
||||
kbIdValue,
|
||||
);
|
||||
@@ -443,6 +446,20 @@ watch(tagSearchQuery, (newVal, oldVal) => {
|
||||
}, 300);
|
||||
});
|
||||
|
||||
// 监听文档搜索关键词变化
|
||||
watch(docSearchKeyword, (newVal, oldVal) => {
|
||||
if (newVal === oldVal) return;
|
||||
if (docSearchDebounce) {
|
||||
clearTimeout(docSearchDebounce);
|
||||
}
|
||||
docSearchDebounce = window.setTimeout(() => {
|
||||
if (kbId.value) {
|
||||
page = 1;
|
||||
loadKnowledgeFiles(kbId.value);
|
||||
}
|
||||
}, 300);
|
||||
});
|
||||
|
||||
// 监听文件上传事件
|
||||
const handleFileUploaded = (event: CustomEvent) => {
|
||||
const uploadedKbId = event.detail.kbId;
|
||||
@@ -819,7 +836,7 @@ const handleScroll = () => {
|
||||
if (scrollTop + clientHeight >= scrollHeight) {
|
||||
page++;
|
||||
if (cardList.value.length < total.value && page <= pageNum) {
|
||||
getKnowled({ page, page_size: pageSize, tag_id: selectedTagId.value || undefined });
|
||||
getKnowled({ page, page_size: pageSize, tag_id: selectedTagId.value || undefined, keyword: docSearchKeyword.value ? docSearchKeyword.value.trim() : undefined });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1108,6 +1125,20 @@ async function createNewSession(value: string): Promise<void> {
|
||||
</aside>
|
||||
<div class="tag-content">
|
||||
<div class="doc-card-area">
|
||||
<!-- 搜索栏 -->
|
||||
<div class="doc-search-bar">
|
||||
<t-input
|
||||
v-model.trim="docSearchKeyword"
|
||||
:placeholder="$t('knowledgeBase.docSearchPlaceholder')"
|
||||
clearable
|
||||
@clear="loadKnowledgeFiles(kbId)"
|
||||
@keydown.enter="loadKnowledgeFiles(kbId)"
|
||||
>
|
||||
<template #prefix-icon>
|
||||
<t-icon name="search" size="16px" />
|
||||
</template>
|
||||
</t-input>
|
||||
</div>
|
||||
<div
|
||||
class="doc-scroll-container"
|
||||
:class="{ 'is-empty': !cardList.length }"
|
||||
@@ -1750,6 +1781,25 @@ async function createNewSession(value: string): Promise<void> {
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.doc-search-bar {
|
||||
padding: 0px 0px 10px 0px;
|
||||
flex-shrink: 0;
|
||||
|
||||
:deep(.t-input) {
|
||||
font-size: 13px;
|
||||
background-color: #f7f9fc;
|
||||
border-color: #e5e9f2;
|
||||
border-radius: 6px;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&.t-is-focused {
|
||||
border-color: #4080ff;
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.doc-scroll-container {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
|
||||
@@ -62,6 +62,7 @@ func (r *knowledgeRepository) ListPagedKnowledgeByKnowledgeBaseID(
|
||||
kbID string,
|
||||
page *types.Pagination,
|
||||
tagID string,
|
||||
keyword string,
|
||||
) ([]*types.Knowledge, int64, error) {
|
||||
var knowledges []*types.Knowledge
|
||||
var total int64
|
||||
@@ -71,6 +72,9 @@ func (r *knowledgeRepository) ListPagedKnowledgeByKnowledgeBaseID(
|
||||
if tagID != "" {
|
||||
query = query.Where("tag_id = ?", tagID)
|
||||
}
|
||||
if keyword != "" {
|
||||
query = query.Where("file_name LIKE ?", "%"+keyword+"%")
|
||||
}
|
||||
|
||||
// Query total count first
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
@@ -83,6 +87,9 @@ func (r *knowledgeRepository) ListPagedKnowledgeByKnowledgeBaseID(
|
||||
if tagID != "" {
|
||||
dataQuery = dataQuery.Where("tag_id = ?", tagID)
|
||||
}
|
||||
if keyword != "" {
|
||||
dataQuery = dataQuery.Where("file_name LIKE ?", "%"+keyword+"%")
|
||||
}
|
||||
|
||||
if err := dataQuery.
|
||||
Order("created_at DESC").
|
||||
|
||||
@@ -331,7 +331,7 @@ func (s *agentService) getKnowledgeBaseInfos(ctx context.Context, kbIDs []string
|
||||
pageResult, err := s.knowledgeService.ListPagedKnowledgeByKnowledgeBaseID(ctx, kbID, &types.Pagination{
|
||||
Page: 1,
|
||||
PageSize: 10,
|
||||
}, "")
|
||||
}, "", "")
|
||||
|
||||
if err == nil && pageResult != nil {
|
||||
docCount = int(pageResult.Total)
|
||||
|
||||
@@ -699,10 +699,10 @@ func (s *knowledgeService) ListKnowledgeByKnowledgeBaseID(ctx context.Context,
|
||||
|
||||
// ListPagedKnowledgeByKnowledgeBaseID returns paginated knowledge entries in a knowledge base
|
||||
func (s *knowledgeService) ListPagedKnowledgeByKnowledgeBaseID(ctx context.Context,
|
||||
kbID string, page *types.Pagination, tagID string,
|
||||
kbID string, page *types.Pagination, tagID string, keyword string,
|
||||
) (*types.PageResult, error) {
|
||||
knowledges, total, err := s.repo.ListPagedKnowledgeByKnowledgeBaseID(ctx,
|
||||
ctx.Value(types.TenantIDContextKey).(uint64), kbID, page, tagID)
|
||||
ctx.Value(types.TenantIDContextKey).(uint64), kbID, page, tagID, keyword)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -232,7 +232,7 @@ func (h *InitializationHandler) UpdateKBConfig(c *gin.Context) {
|
||||
kbIdStr, &types.Pagination{
|
||||
Page: 1,
|
||||
PageSize: 1,
|
||||
}, "")
|
||||
}, "", "")
|
||||
if err == nil && knowledgeList != nil && knowledgeList.Total > 0 {
|
||||
logger.Error(ctx, "Cannot change embedding model when files exist")
|
||||
c.Error(errors.NewBadRequestError("知识库中已有文件,无法修改Embedding模型"))
|
||||
@@ -1176,7 +1176,7 @@ func (h *InitializationHandler) GetCurrentConfigByKB(c *gin.Context) {
|
||||
kbIdStr, &types.Pagination{
|
||||
Page: 1,
|
||||
PageSize: 1,
|
||||
}, "")
|
||||
}, "", "")
|
||||
hasFiles := err == nil && knowledgeList != nil && knowledgeList.Total > 0
|
||||
|
||||
// 构建配置响应
|
||||
|
||||
@@ -318,18 +318,20 @@ func (h *KnowledgeHandler) ListKnowledge(c *gin.Context) {
|
||||
}
|
||||
|
||||
tagID := c.Query("tag_id")
|
||||
keyword := c.Query("keyword")
|
||||
|
||||
logger.Infof(
|
||||
ctx,
|
||||
"Retrieving knowledge list under knowledge base, knowledge base ID: %s, tag_id: %s, page: %d, page size: %d",
|
||||
"Retrieving knowledge list under knowledge base, knowledge base ID: %s, tag_id: %s, keyword: %s, page: %d, page size: %d",
|
||||
secutils.SanitizeForLog(kbID),
|
||||
secutils.SanitizeForLog(tagID),
|
||||
secutils.SanitizeForLog(keyword),
|
||||
pagination.Page,
|
||||
pagination.PageSize,
|
||||
)
|
||||
|
||||
// Retrieve paginated knowledge entries
|
||||
result, err := h.kgService.ListPagedKnowledgeByKnowledgeBaseID(ctx, kbID, &pagination, tagID)
|
||||
result, err := h.kgService.ListPagedKnowledgeByKnowledgeBaseID(ctx, kbID, &pagination, tagID, keyword)
|
||||
if err != nil {
|
||||
logger.ErrorWithFields(ctx, err, nil)
|
||||
c.Error(errors.NewInternalServerError(err.Error()))
|
||||
|
||||
@@ -45,11 +45,13 @@ type KnowledgeService interface {
|
||||
ListKnowledgeByKnowledgeBaseID(ctx context.Context, kbID string) ([]*types.Knowledge, error)
|
||||
// ListPagedKnowledgeByKnowledgeBaseID lists all knowledge under a knowledge base with pagination.
|
||||
// When tagID is non-empty, results are filtered by tag_id.
|
||||
// When keyword is non-empty, results are filtered by file_name.
|
||||
ListPagedKnowledgeByKnowledgeBaseID(
|
||||
ctx context.Context,
|
||||
kbID string,
|
||||
page *types.Pagination,
|
||||
tagID string,
|
||||
keyword string,
|
||||
) (*types.PageResult, error)
|
||||
// DeleteKnowledge deletes knowledge by ID.
|
||||
DeleteKnowledge(ctx context.Context, id string) error
|
||||
@@ -112,8 +114,9 @@ type KnowledgeRepository interface {
|
||||
ListKnowledgeByKnowledgeBaseID(ctx context.Context, tenantID uint64, kbID string) ([]*types.Knowledge, error)
|
||||
// ListPagedKnowledgeByKnowledgeBaseID lists all knowledge in a knowledge base with pagination.
|
||||
// When tagID is non-empty, results are filtered by tag_id.
|
||||
// When keyword is non-empty, results are filtered by file_name.
|
||||
ListPagedKnowledgeByKnowledgeBaseID(ctx context.Context,
|
||||
tenantID uint64, kbID string, page *types.Pagination, tagID string,
|
||||
tenantID uint64, kbID string, page *types.Pagination, tagID string, keyword string,
|
||||
) ([]*types.Knowledge, int64, error)
|
||||
UpdateKnowledge(ctx context.Context, knowledge *types.Knowledge) error
|
||||
// UpdateKnowledgeBatch updates knowledge items in batch
|
||||
|
||||
Reference in New Issue
Block a user