feat: Add MinIO and graph database status handling with localization updates

- Introduced a new boolean field for MinIO status in the system info API response.
- Updated localization files in English, Russian, and Chinese to include warnings and instructions related to MinIO and graph database configurations.
- Enhanced the KBAdvancedSettings component to display alerts and disable options based on the status of MinIO and graph database.
- Implemented logic to check system status on component mount and adjust settings accordingly.
This commit is contained in:
wizardchen
2025-11-17 11:38:02 +08:00
parent 667239a625
commit da5f71dfd3
9 changed files with 136 additions and 21 deletions

View File

@@ -8,6 +8,7 @@ export interface SystemInfo {
keyword_index_engine?: string
vector_store_engine?: string
graph_database_engine?: string
minio_enabled?: boolean
}
export interface ToolDefinition {

View File

@@ -847,6 +847,7 @@ export default {
minio: 'MinIO',
cos: 'Tencent Cloud COS'
},
minioDisabledWarning: 'MinIO is not enabled. Automatically switched to Tencent Cloud COS. To use MinIO, please enable it in system configuration first.',
minio: {
bucketLabel: 'Bucket Name',
bucketDescription: 'Name of the MinIO bucket (required)',
@@ -887,7 +888,9 @@ export default {
promptPlaceholder: 'Enter prompt text',
tagsLabel: 'Tags',
tagsDescription: 'Predefined entity tags (separate multiple tags with commas)',
tagsPlaceholder: 'Enter a tag and press Enter'
tagsPlaceholder: 'Enter a tag and press Enter',
disabledWarning: 'Graph database is not enabled. Please enable the graph database first to use this feature.',
howToEnable: 'How to enable'
}
}
},

View File

@@ -938,6 +938,7 @@ export default {
minio: 'MinIO',
cos: 'Tencent Cloud COS'
},
minioDisabledWarning: 'MinIO не включен. Автоматически переключено на Tencent Cloud COS. Чтобы использовать MinIO, сначала включите его в конфигурации системы.',
minio: {
bucketLabel: 'Имя Bucket',
bucketDescription: 'Название бакета MinIO (обязательно)',
@@ -978,7 +979,9 @@ export default {
promptPlaceholder: 'Введите текст подсказки',
tagsLabel: 'Теги',
tagsDescription: 'Предопределённые теги сущностей (несколько тегов разделяйте запятыми)',
tagsPlaceholder: 'Введите тег и нажмите Enter'
tagsPlaceholder: 'Введите тег и нажмите Enter',
disabledWarning: 'Графовая база данных не включена. Пожалуйста, сначала включите графовую базу данных, чтобы использовать эту функцию.',
howToEnable: 'Как включить'
}
}
},

View File

@@ -1171,6 +1171,7 @@ export default {
minio: "MinIO",
cos: "腾讯云 COS",
},
minioDisabledWarning: "MinIO 未启用,已自动切换到腾讯云 COS。如需使用 MinIO请先在系统配置中启用 MinIO。",
minio: {
bucketLabel: "Bucket 名称",
bucketDescription: "MinIO 存储桶名称(必填)",
@@ -1212,6 +1213,8 @@ export default {
tagsLabel: "标签",
tagsDescription: "预定义的实体标签(多个标签用逗号分隔)",
tagsPlaceholder: "输入标签后按回车",
disabledWarning: "图数据库未启用,无法使用知识图谱功能。请先启用图数据库后再使用此功能。",
howToEnable: "查看如何开启",
},
},
},

View File

@@ -424,7 +424,7 @@
<!-- 导入模式选择 -->
<div class="import-form-item">
<label class="import-form-label required">{{ $t('knowledgeEditor.faqImport.modeLabel') }}</label>
<t-radio-group v-model="importState.mode" variant="default-filled" class="import-radio-group">
<t-radio-group v-model="importState.mode" class="import-radio-group">
<t-radio-button value="append">{{ $t('knowledgeEditor.faqImport.appendMode') }}</t-radio-button>
<t-radio-button value="replace">{{ $t('knowledgeEditor.faqImport.replaceMode') }}</t-radio-button>
</t-radio-group>

View File

@@ -50,10 +50,19 @@
<div class="setting-info">
<label>{{ $t('knowledgeEditor.advanced.multimodal.storageTypeLabel') }} <span class="required">*</span></label>
<p class="desc">{{ $t('knowledgeEditor.advanced.multimodal.storageTypeDescription') }}</p>
<!-- Warning message when MinIO is not enabled -->
<t-alert
v-if="!isMinioEnabled"
theme="warning"
:message="$t('knowledgeEditor.advanced.multimodal.minioDisabledWarning')"
style="margin-top: 8px;"
/>
</div>
<div class="setting-control">
<t-radio-group v-model="localMultimodal.storageType" @change="handleStorageTypeChange">
<t-radio value="minio">{{ $t('knowledgeEditor.advanced.multimodal.storageTypeOptions.minio') }}</t-radio>
<t-radio value="minio" :disabled="!isMinioEnabled">
{{ $t('knowledgeEditor.advanced.multimodal.storageTypeOptions.minio') }}
</t-radio>
<t-radio value="cos">{{ $t('knowledgeEditor.advanced.multimodal.storageTypeOptions.cos') }}</t-radio>
</t-radio-group>
</div>
@@ -206,18 +215,32 @@
<div class="setting-info">
<label>{{ $t('knowledgeEditor.advanced.graph.label') }}</label>
<p class="desc">{{ $t('knowledgeEditor.advanced.graph.description') }}</p>
<!-- Warning message when graph database is not enabled -->
<t-alert
v-if="!isGraphDatabaseEnabled"
theme="warning"
:message="$t('knowledgeEditor.advanced.graph.disabledWarning')"
style="margin-top: 8px;"
>
<template #operation>
<t-link theme="primary" @click="handleOpenSystemInfo">
{{ $t('knowledgeEditor.advanced.graph.howToEnable') }}
</t-link>
</template>
</t-alert>
</div>
<div class="setting-control">
<t-switch
v-model="localNodeExtract.enabled"
@change="handleNodeExtractToggle"
:disabled="!isGraphDatabaseEnabled"
size="large"
/>
</div>
</div>
<!-- Knowledge graph configuration -->
<div v-if="localNodeExtract.enabled" class="subsection">
<div v-if="localNodeExtract.enabled && isGraphDatabaseEnabled" class="subsection">
<div class="subsection-header">
<h4>{{ $t('knowledgeEditor.advanced.graph.configTitle') }}</h4>
</div>
@@ -258,9 +281,10 @@
</template>
<script setup lang="ts">
import { ref, watch } from 'vue'
import { ref, watch, onMounted } from 'vue'
import ModelSelector from '@/components/ModelSelector.vue'
import { useUIStore } from '@/stores/ui'
import { getSystemInfo } from '@/api/system'
const uiStore = useUIStore()
@@ -306,6 +330,60 @@ const localMultimodal = ref<MultimodalConfig>({ ...props.multimodal })
const localNodeExtract = ref<NodeExtractConfig>({ ...props.nodeExtract })
const vllmSelectorRef = ref()
const isGraphDatabaseEnabled = ref(false)
const isMinioEnabled = ref(false)
// Check system status on mount
onMounted(async () => {
try {
const systemInfo = await getSystemInfo()
// Check graph database status
if (systemInfo.data?.graph_database_engine) {
// Check if graph database is enabled
// Enabled if it's "Neo4j" or any other non-empty value that's not a disabled indicator
const engine = systemInfo.data.graph_database_engine.trim()
const disabledIndicators = ['未启用', '未配置', 'Unknown', 'Неизвестно', '']
isGraphDatabaseEnabled.value = !disabledIndicators.includes(engine) && engine.length > 0
// If graph database is disabled, also disable node extract
if (!isGraphDatabaseEnabled.value && localNodeExtract.value.enabled) {
localNodeExtract.value.enabled = false
emit('update:nodeExtract', localNodeExtract.value)
}
} else {
// No graph database engine info, assume disabled
isGraphDatabaseEnabled.value = false
if (localNodeExtract.value.enabled) {
localNodeExtract.value.enabled = false
emit('update:nodeExtract', localNodeExtract.value)
}
}
// Check MinIO status
isMinioEnabled.value = systemInfo.data?.minio_enabled === true
// If MinIO is not enabled and storage type is minio, switch to cos
if (!isMinioEnabled.value && localMultimodal.value.storageType === 'minio') {
localMultimodal.value.storageType = 'cos'
emit('update:multimodal', localMultimodal.value)
}
} catch (error) {
console.error('Failed to fetch system info:', error)
// Default to disabled if we can't fetch the info
isGraphDatabaseEnabled.value = false
isMinioEnabled.value = false
if (localNodeExtract.value.enabled) {
localNodeExtract.value.enabled = false
emit('update:nodeExtract', localNodeExtract.value)
}
// If MinIO status unknown and storage type is minio, switch to cos
if (localMultimodal.value.storageType === 'minio') {
localMultimodal.value.storageType = 'cos'
emit('update:multimodal', localMultimodal.value)
}
}
})
// Watch for prop changes
watch(() => props.multimodal, (newVal) => {
@@ -340,6 +418,10 @@ const handleMultimodalToggle = () => {
// Handle storage type change
const handleStorageTypeChange = () => {
// Prevent switching to minio if it's not enabled
if (localMultimodal.value.storageType === 'minio' && !isMinioEnabled.value) {
localMultimodal.value.storageType = 'cos'
}
emit('update:multimodal', localMultimodal.value)
}
@@ -356,9 +438,19 @@ const handleAddModel = (subSection: string) => {
// Handle knowledge graph toggle
const handleNodeExtractToggle = () => {
// Prevent enabling if graph database is not enabled
if (!isGraphDatabaseEnabled.value) {
localNodeExtract.value.enabled = false
return
}
emit('update:nodeExtract', localNodeExtract.value)
}
// Open system info page to show how to enable graph database
const handleOpenSystemInfo = () => {
uiStore.openSettings('system')
}
// Handle configuration change
const handleConfigChange = () => {
emit('update:multimodal', localMultimodal.value)

View File

@@ -2283,6 +2283,10 @@ func (s *knowledgeService) SearchFAQEntries(ctx context.Context,
entries = append(entries, entry)
}
slices.SortFunc(entries, func(a, b *types.FAQEntry) int {
return int(b.Score - a.Score)
})
return entries, nil
}

View File

@@ -407,15 +407,12 @@ func (s *knowledgeBaseService) HybridSearch(ctx context.Context,
// Check if we need iterative retrieval for FAQ with separate indexing
// Only use iterative retrieval if we don't have enough unique chunks after first deduplication
needsIterativeRetrieval := len(deduplicatedChunks) < params.MatchCount &&
kb.Type == types.KnowledgeBaseTypeFAQ &&
kb.FAQConfig != nil &&
kb.FAQConfig.QuestionIndexMode == types.FAQQuestionIndexModeSeparate
kb.Type == types.KnowledgeBaseTypeFAQ && len(matchResults) >= params.MatchCount
if needsIterativeRetrieval {
logger.Infof(ctx, "Not enough unique chunks (%d < %d), using iterative retrieval for FAQ with separate indexing",
logger.Infof(ctx, "Not enough unique chunks (%d < %d), using iterative retrieval for FAQ",
len(deduplicatedChunks), params.MatchCount)
// Use iterative retrieval to get more unique chunks (with negative question filtering inside)
deduplicatedChunks = s.iterativeRetrieveWithDeduplication(ctx, retrieveEngine, retrieveParams, params.MatchCount, params.QueryText, kb.Type == types.KnowledgeBaseTypeFAQ)
deduplicatedChunks = s.iterativeRetrieveWithDeduplication(ctx, retrieveEngine, retrieveParams, params.MatchCount, params.QueryText)
} else if kb.Type == types.KnowledgeBaseTypeFAQ {
// Filter by negative questions if not using iterative retrieval
deduplicatedChunks = s.filterByNegativeQuestions(ctx, deduplicatedChunks, params.QueryText)
@@ -438,7 +435,6 @@ func (s *knowledgeBaseService) iterativeRetrieveWithDeduplication(ctx context.Co
retrieveParams []types.RetrieveParams,
matchCount int,
queryText string,
isFAQ bool,
) []*types.IndexWithScore {
maxIterations := 5
currentTopK := matchCount
@@ -492,14 +488,12 @@ func (s *knowledgeBaseService) iterativeRetrieveWithDeduplication(ctx context.Co
chunksSlice = append(chunksSlice, chunk)
}
// Filter by negative questions if this is a FAQ knowledge base
if isFAQ {
chunksSlice = s.filterByNegativeQuestions(ctx, chunksSlice, queryText)
// Update uniqueChunks map with filtered results
uniqueChunks = make(map[string]*types.IndexWithScore, len(chunksSlice))
for _, chunk := range chunksSlice {
uniqueChunks[chunk.ChunkID] = chunk
}
// Filter by negative questions
chunksSlice = s.filterByNegativeQuestions(ctx, chunksSlice, queryText)
// Update uniqueChunks map with filtered results
uniqueChunks = make(map[string]*types.IndexWithScore, len(chunksSlice))
for _, chunk := range chunksSlice {
uniqueChunks[chunk.ChunkID] = chunk
}
logger.Infof(ctx, "After iteration %d: retrieved %d results, found %d unique chunks after filtering (target: %d)",

View File

@@ -30,6 +30,7 @@ type GetSystemInfoResponse struct {
KeywordIndexEngine string `json:"keyword_index_engine,omitempty"`
VectorStoreEngine string `json:"vector_store_engine,omitempty"`
GraphDatabaseEngine string `json:"graph_database_engine,omitempty"`
MinioEnabled bool `json:"minio_enabled,omitempty"`
}
// 编译时注入的版本信息
@@ -53,6 +54,9 @@ func (h *SystemHandler) GetSystemInfo(c *gin.Context) {
// Get graph database engine from NEO4J_ENABLE
graphDatabaseEngine := h.getGraphDatabaseEngine()
// Get MinIO enabled status
minioEnabled := h.isMinioEnabled()
response := GetSystemInfoResponse{
Version: Version,
CommitID: CommitID,
@@ -61,6 +65,7 @@ func (h *SystemHandler) GetSystemInfo(c *gin.Context) {
KeywordIndexEngine: keywordIndexEngine,
VectorStoreEngine: vectorStoreEngine,
GraphDatabaseEngine: graphDatabaseEngine,
MinioEnabled: minioEnabled,
}
logger.Info(ctx, "System info retrieved successfully")
@@ -131,3 +136,13 @@ func (h *SystemHandler) getGraphDatabaseEngine() string {
}
return "未启用"
}
// isMinioEnabled checks if MinIO is enabled
func (h *SystemHandler) isMinioEnabled() bool {
// Check if all required MinIO environment variables are set
endpoint := os.Getenv("MINIO_ENDPOINT")
accessKeyID := os.Getenv("MINIO_ACCESS_KEY_ID")
secretAccessKey := os.Getenv("MINIO_SECRET_ACCESS_KEY")
return endpoint != "" && accessKeyID != "" && secretAccessKey != ""
}