diff --git a/frontend/src/i18n/locales/en-US.ts b/frontend/src/i18n/locales/en-US.ts
index 8114ee2b2..9a8c4e32d 100644
--- a/frontend/src/i18n/locales/en-US.ts
+++ b/frontend/src/i18n/locales/en-US.ts
@@ -271,6 +271,56 @@ export default {
saveFailed: 'Failed to save configuration: {message}'
}
},
+ graphSettings: {
+ title: 'Knowledge Graph Configuration',
+ description: 'Configure entity-relationship extraction to automatically build knowledge graphs from text',
+ enableLabel: 'Enable Entity-Relationship Extraction',
+ enableDescription: 'Automatically extract entities and relationships from text when enabled',
+ tagsLabel: 'Relationship Types',
+ tagsDescription: 'Define relationship type tags to extract, separated by commas',
+ tagsPlaceholder: 'Enter relationship types, e.g., works_at, colleague, friend',
+ generateRandomTags: 'Generate Random Tags',
+ sampleTextLabel: 'Sample Text',
+ sampleTextDescription: 'Sample text for testing entity-relationship extraction',
+ sampleTextPlaceholder: 'Enter text containing entities and relationships...',
+ generateRandomText: 'Generate Random Text',
+ entityListLabel: 'Entity List',
+ entityListDescription: 'Entities and their attributes extracted from text',
+ nodeNamePlaceholder: 'Enter entity name',
+ attributePlaceholder: 'Enter attribute value',
+ addAttribute: 'Add Attribute',
+ manageEntitiesLabel: 'Manage Entities',
+ manageEntitiesDescription: 'Add or remove entity nodes',
+ addEntity: 'Add Entity',
+ relationListLabel: 'Relationship List',
+ relationListDescription: 'Define relationship connections between entities',
+ selectEntity: 'Select Entity',
+ selectRelationType: 'Select Relationship Type',
+ manageRelationsLabel: 'Manage Relationships',
+ manageRelationsDescription: 'Add or remove relationships between entities',
+ addRelation: 'Add Relationship',
+ extractActionsLabel: 'Extraction Actions',
+ extractActionsDescription: 'Perform entity-relationship extraction or manage sample data',
+ startExtraction: 'Start Extraction',
+ extracting: 'Extracting...',
+ defaultExample: 'Default Example',
+ clearExample: 'Clear Example',
+ completeModelConfig: 'Please complete model configuration first',
+ tagsGenerated: 'Tags generated successfully',
+ tagsGenerateFailed: 'Failed to generate tags',
+ textGenerated: 'Text generated successfully',
+ textGenerateFailed: 'Failed to generate text',
+ pleaseInputText: 'Please enter sample text first',
+ extractSuccess: 'Entity-relationship extraction successful',
+ extractFailed: 'Entity-relationship extraction failed',
+ exampleLoaded: 'Example loaded',
+ exampleCleared: 'Example cleared',
+ saveSuccess: 'Graph configuration saved',
+ saveFailed: 'Failed to save configuration: {message}',
+ errors: {
+ unknown: 'Unknown error',
+ },
+ },
initialization: {
title: 'Initialization',
welcome: 'Welcome to WeKnora',
@@ -770,7 +820,8 @@ export default {
models: 'Model Configuration',
chunking: 'Chunking Settings',
advanced: 'Advanced Settings',
- faq: 'FAQ Settings'
+ faq: 'FAQ Settings',
+ graph: 'Knowledge Graph'
},
basic: {
title: 'Basic Information',
@@ -936,7 +987,7 @@ export default {
},
advanced: {
title: 'Advanced Settings',
- description: 'Configure multimodal and knowledge graph features',
+ description: 'Configure multimodal features',
multimodal: {
label: 'Multimodal Feature',
description: 'Enable understanding of multimodal content such as images and videos',
@@ -981,40 +1032,6 @@ export default {
pathPrefixDescription: 'Optional prefix for stored file paths',
pathPrefixPlaceholder: 'Enter path prefix'
}
- },
- graph: {
- label: 'Knowledge Graph Extraction',
- description: 'Automatically extract entities and relationships to build a knowledge graph',
- configTitle: 'Graph Configuration',
- promptLabel: 'Prompt Text',
- promptDescription: 'Prompt used to guide the model when extracting entities and relationships',
- promptPlaceholder: 'Enter prompt text',
- tagsLabel: 'Tags',
- tagsDescription: 'Predefined entity tags (separate multiple tags with commas)',
- tagsPlaceholder: 'Enter a tag and press Enter',
- nodesLabel: 'Node Configuration',
- nodesDescription: 'Define entity node types and attributes to extract',
- addNode: 'Add Node',
- nodeNameLabel: 'Node Name',
- nodeNamePlaceholder: 'e.g., Person, Organization',
- nodeChunksLabel: 'Associated Chunks',
- nodeChunksPlaceholder: 'Enter chunk ID and press Enter',
- nodeAttributesLabel: 'Node Attributes',
- nodeAttributesPlaceholder: 'Enter attribute name and press Enter',
- relationsLabel: 'Relation Configuration',
- relationsDescription: 'Define relationship types between nodes',
- addRelation: 'Add Relation',
- relationNode1Label: 'Source Node',
- relationNode1Placeholder: 'e.g., Person',
- relationNode2Label: 'Target Node',
- relationNode2Placeholder: 'e.g., Organization',
- relationTypeLabel: 'Relation Type',
- relationTypePlaceholder: 'e.g., WORKS_FOR, BELONGS_TO',
- deleteNode: 'Delete Node',
- deleteRelation: 'Delete Relation',
- disabledWarning:
- 'Graph database is not enabled. Please follow the "Enable Knowledge Graph" guide before using this feature.',
- howToEnable: 'View guide'
}
}
},
diff --git a/frontend/src/i18n/locales/ru-RU.ts b/frontend/src/i18n/locales/ru-RU.ts
index c98e33688..3b64857bc 100644
--- a/frontend/src/i18n/locales/ru-RU.ts
+++ b/frontend/src/i18n/locales/ru-RU.ts
@@ -270,6 +270,56 @@ export default {
saveFailed: 'Не удалось сохранить настройки: {message}'
}
},
+ graphSettings: {
+ title: 'Настройки графа знаний',
+ description: 'Настройте извлечение сущностей и отношений для автоматического построения графа знаний из текста',
+ enableLabel: 'Включить извлечение сущностей и отношений',
+ enableDescription: 'Автоматически извлекать сущности и отношения из текста при включении',
+ tagsLabel: 'Типы отношений',
+ tagsDescription: 'Определите теги типов отношений для извлечения, разделённые запятыми',
+ tagsPlaceholder: 'Введите типы отношений, например: работает_в, коллега, друг',
+ generateRandomTags: 'Сгенерировать случайные теги',
+ sampleTextLabel: 'Образец текста',
+ sampleTextDescription: 'Образец текста для тестирования извлечения сущностей и отношений',
+ sampleTextPlaceholder: 'Введите текст, содержащий сущности и отношения...',
+ generateRandomText: 'Сгенерировать случайный текст',
+ entityListLabel: 'Список сущностей',
+ entityListDescription: 'Сущности и их атрибуты, извлечённые из текста',
+ nodeNamePlaceholder: 'Введите имя сущности',
+ attributePlaceholder: 'Введите значение атрибута',
+ addAttribute: 'Добавить атрибут',
+ manageEntitiesLabel: 'Управление сущностями',
+ manageEntitiesDescription: 'Добавить или удалить узлы сущностей',
+ addEntity: 'Добавить сущность',
+ relationListLabel: 'Список отношений',
+ relationListDescription: 'Определите связи отношений между сущностями',
+ selectEntity: 'Выберите сущность',
+ selectRelationType: 'Выберите тип отношения',
+ manageRelationsLabel: 'Управление отношениями',
+ manageRelationsDescription: 'Добавить или удалить отношения между сущностями',
+ addRelation: 'Добавить отношение',
+ extractActionsLabel: 'Действия извлечения',
+ extractActionsDescription: 'Выполнить извлечение сущностей и отношений или управлять образцами данных',
+ startExtraction: 'Начать извлечение',
+ extracting: 'Извлечение...',
+ defaultExample: 'Пример по умолчанию',
+ clearExample: 'Очистить пример',
+ completeModelConfig: 'Пожалуйста, сначала завершите настройку модели',
+ tagsGenerated: 'Теги успешно сгенерированы',
+ tagsGenerateFailed: 'Не удалось сгенерировать теги',
+ textGenerated: 'Текст успешно сгенерирован',
+ textGenerateFailed: 'Не удалось сгенерировать текст',
+ pleaseInputText: 'Пожалуйста, сначала введите образец текста',
+ extractSuccess: 'Извлечение сущностей и отношений выполнено успешно',
+ extractFailed: 'Не удалось извлечь сущности и отношения',
+ exampleLoaded: 'Пример загружен',
+ exampleCleared: 'Пример очищен',
+ saveSuccess: 'Настройки графа сохранены',
+ saveFailed: 'Не удалось сохранить настройки: {message}',
+ errors: {
+ unknown: 'Неизвестная ошибка',
+ },
+ },
initialization: {
title: 'Инициализация',
welcome: 'Добро пожаловать в WeKnora',
@@ -858,7 +908,8 @@ export default {
models: 'Конфигурация моделей',
chunking: 'Настройки разбиения',
advanced: 'Дополнительные настройки',
- faq: 'FAQ настройки'
+ faq: 'FAQ настройки',
+ graph: 'Граф знаний'
},
basic: {
title: 'Основная информация',
@@ -1021,7 +1072,7 @@ export default {
},
advanced: {
title: 'Расширенные настройки',
- description: 'Настройте мультимодальные возможности и извлечение знаний',
+ description: 'Настройте мультимодальные возможности',
multimodal: {
label: 'Мультимодальная функция',
description: 'Включите понимание мультимедийного контента, такого как изображения и видео',
@@ -1066,19 +1117,6 @@ export default {
pathPrefixDescription: 'Необязательный префикс для путей хранения файлов',
pathPrefixPlaceholder: 'Введите префикс пути'
}
- },
- graph: {
- label: 'Извлечение знаний',
- description: 'Автоматически извлекайте сущности и связи для построения графа знаний',
- configTitle: 'Настройки графа',
- promptLabel: 'Текст подсказки',
- promptDescription: 'Текст подсказки, который помогает модели извлекать сущности и связи',
- promptPlaceholder: 'Введите текст подсказки',
- tagsLabel: 'Теги',
- tagsDescription: 'Предопределённые теги сущностей (несколько тегов разделяйте запятыми)',
- tagsPlaceholder: 'Введите тег и нажмите Enter',
- disabledWarning: 'Графовая база данных не включена. Пожалуйста, сначала включите графовую базу данных, чтобы использовать эту функцию.',
- howToEnable: 'Как включить'
}
}
},
diff --git a/frontend/src/i18n/locales/zh-CN.ts b/frontend/src/i18n/locales/zh-CN.ts
index 8c71d7056..78bfea272 100644
--- a/frontend/src/i18n/locales/zh-CN.ts
+++ b/frontend/src/i18n/locales/zh-CN.ts
@@ -373,6 +373,56 @@ export default {
saveFailed: "保存配置失败: {message}",
},
},
+ graphSettings: {
+ title: "知识图谱配置",
+ description: "配置实体关系提取功能,自动从文本中提取实体和关系构建知识图谱",
+ enableLabel: "启用实体关系提取",
+ enableDescription: "开启后将自动从文本中提取实体和关系",
+ tagsLabel: "关系类型",
+ tagsDescription: "定义要提取的关系类型标签,多个标签用逗号分隔",
+ tagsPlaceholder: "输入关系类型,如:工作于、同事、朋友等",
+ generateRandomTags: "生成随机标签",
+ sampleTextLabel: "示例文本",
+ sampleTextDescription: "用于测试实体关系提取的示例文本",
+ sampleTextPlaceholder: "输入一段包含实体和关系的文本...",
+ generateRandomText: "生成随机文本",
+ entityListLabel: "实体列表",
+ entityListDescription: "从文本中提取的实体及其属性",
+ nodeNamePlaceholder: "输入实体名称",
+ attributePlaceholder: "输入属性值",
+ addAttribute: "添加属性",
+ manageEntitiesLabel: "管理实体",
+ manageEntitiesDescription: "添加或删除实体节点",
+ addEntity: "添加实体",
+ relationListLabel: "关系列表",
+ relationListDescription: "定义实体之间的关系连接",
+ selectEntity: "选择实体",
+ selectRelationType: "选择关系类型",
+ manageRelationsLabel: "管理关系",
+ manageRelationsDescription: "添加或删除实体间的关系",
+ addRelation: "添加关系",
+ extractActionsLabel: "提取操作",
+ extractActionsDescription: "执行实体关系提取或管理示例数据",
+ startExtraction: "开始提取",
+ extracting: "提取中...",
+ defaultExample: "默认示例",
+ clearExample: "清除示例",
+ completeModelConfig: "请先完成模型配置",
+ tagsGenerated: "标签生成成功",
+ tagsGenerateFailed: "标签生成失败",
+ textGenerated: "文本生成成功",
+ textGenerateFailed: "文本生成失败",
+ pleaseInputText: "请先输入示例文本",
+ extractSuccess: "实体关系提取成功",
+ extractFailed: "实体关系提取失败",
+ exampleLoaded: "示例已加载",
+ exampleCleared: "示例已清除",
+ saveSuccess: "图谱配置已保存",
+ saveFailed: "保存配置失败: {message}",
+ errors: {
+ unknown: "未知错误",
+ },
+ },
initialization: {
title: "初始化",
welcome: "欢迎使用WeKnora",
@@ -1094,6 +1144,7 @@ export default {
chunking: "分块设置",
advanced: "高级设置",
faq: "FAQ 设置",
+ graph: "知识图谱",
},
basic: {
title: "基本信息",
@@ -1260,7 +1311,7 @@ export default {
},
advanced: {
title: "高级设置",
- description: "配置多模态、知识图谱等高级功能",
+ description: "配置多模态等高级功能",
multimodal: {
label: "多模态功能",
description: "启用图片、视频等多模态内容的理解能力",
@@ -1307,40 +1358,6 @@ export default {
pathPrefixPlaceholder: "请输入路径前缀",
},
},
- graph: {
- label: "知识图谱提取",
- description: "从文档中自动提取实体和关系,构建知识图谱",
- configTitle: "图谱配置",
- promptLabel: "提示文本",
- promptDescription: "用于引导模型提取实体和关系的提示文本",
- promptPlaceholder: "请输入提示文本",
- tagsLabel: "标签",
- tagsDescription: "预定义的实体标签(多个标签用逗号分隔)",
- tagsPlaceholder: "输入标签后按回车",
- nodesLabel: "节点配置",
- nodesDescription: "定义需要提取的实体节点类型及其属性",
- addNode: "添加节点",
- nodeNameLabel: "节点名称",
- nodeNamePlaceholder: "如:Person、Organization",
- nodeChunksLabel: "关联文档块",
- nodeChunksPlaceholder: "输入 chunk ID 后按回车",
- nodeAttributesLabel: "节点属性",
- nodeAttributesPlaceholder: "输入属性名后按回车",
- relationsLabel: "关系配置",
- relationsDescription: "定义节点之间的关系类型",
- addRelation: "添加关系",
- relationNode1Label: "起始节点",
- relationNode1Placeholder: "如:Person",
- relationNode2Label: "目标节点",
- relationNode2Placeholder: "如:Organization",
- relationTypeLabel: "关系类型",
- relationTypePlaceholder: "如:WORKS_FOR、BELONGS_TO",
- deleteNode: "删除节点",
- deleteRelation: "删除关系",
- disabledWarning:
- "图数据库未启用,无法使用知识图谱功能。请先根据《开启知识图谱功能指南》完成配置。",
- howToEnable: "查看启用说明",
- },
},
},
input: {
diff --git a/frontend/src/views/knowledge/KnowledgeBaseEditorModal.vue b/frontend/src/views/knowledge/KnowledgeBaseEditorModal.vue
index 6d0a736ea..40206cddc 100644
--- a/frontend/src/views/knowledge/KnowledgeBaseEditorModal.vue
+++ b/frontend/src/views/knowledge/KnowledgeBaseEditorModal.vue
@@ -128,16 +128,24 @@
/>
+
+
+
+
+
@@ -169,6 +177,7 @@ import { useUIStore } from '@/stores/ui'
import KBModelConfig from './settings/KBModelConfig.vue'
import KBChunkingSettings from './settings/KBChunkingSettings.vue'
import KBAdvancedSettings from './settings/KBAdvancedSettings.vue'
+import GraphSettings from './settings/GraphSettings.vue'
import { useI18n } from 'vue-i18n'
const uiStore = useUIStore()
@@ -204,6 +213,7 @@ const navItems = computed(() => {
} else {
items.push(
{ key: 'chunking', icon: 'file-copy', label: t('knowledgeEditor.sidebar.chunking') },
+ { key: 'graph', icon: 'chart-bubble', label: t('knowledgeEditor.sidebar.graph') },
{ key: 'advanced', icon: 'setting', label: t('knowledgeEditor.sidebar.advanced') }
)
}
@@ -272,13 +282,12 @@ const initFormData = (type: 'document' | 'faq' = 'document') => {
pathPrefix: ''
}
},
- nodeExtractConfig: {
+ extractConfig: {
enabled: false,
text: '',
tags: [] as string[],
nodes: [] as Array<{
name: string
- chunks: string[]
attributes: string[]
}>,
relations: [] as Array<{
@@ -286,7 +295,7 @@ const initFormData = (type: 'document' | 'faq' = 'document') => {
node2: string
type: string
}>
- }
+ },
}
}
@@ -358,13 +367,16 @@ const loadKBData = async () => {
pathPrefix: kb.cos_config?.path_prefix || ''
}
},
- nodeExtractConfig: {
+ extractConfig: {
enabled: kb.extract_config?.enabled || false,
text: kb.extract_config?.text || '',
tags: kb.extract_config?.tags || [],
- nodes: kb.extract_config?.nodes || [],
+ nodes: (kb.extract_config?.nodes || []).map((node: any) => ({
+ name: node.name,
+ attributes: node.attributes || []
+ })),
relations: kb.extract_config?.relations || []
- }
+ },
}
} catch (error) {
console.error('Failed to load knowledge base data:', error)
@@ -396,7 +408,7 @@ const handleMultimodalUpdate = (config: any) => {
const handleNodeExtractUpdate = (config: any) => {
if (formData.value) {
- formData.value.nodeExtractConfig = { ...config }
+ formData.value.extractConfig = { ...config }
}
}
@@ -492,13 +504,13 @@ const buildSubmitData = () => {
}
// 添加知识图谱配置
- if (formData.value.nodeExtractConfig.enabled) {
+ if (formData.value.extractConfig.enabled) {
data.extract_config = {
enabled: true,
- text: formData.value.nodeExtractConfig.text,
- tags: formData.value.nodeExtractConfig.tags,
- nodes: formData.value.nodeExtractConfig.nodes,
- relations: formData.value.nodeExtractConfig.relations
+ text: formData.value.extractConfig.text,
+ tags: formData.value.extractConfig.tags,
+ nodes: formData.value.extractConfig.nodes,
+ relations: formData.value.extractConfig.relations
}
}
@@ -584,8 +596,8 @@ const handleSubmit = async () => {
enabled: data.extract_config?.enabled || false,
text: data.extract_config?.text || '',
tags: data.extract_config?.tags || [],
- nodes: [],
- relations: []
+ nodes: data.extract_config?.nodes || [],
+ relations: data.extract_config?.relations || []
}
}
diff --git a/frontend/src/views/knowledge/settings/GraphSettings.vue b/frontend/src/views/knowledge/settings/GraphSettings.vue
new file mode 100644
index 000000000..28c78b9fe
--- /dev/null
+++ b/frontend/src/views/knowledge/settings/GraphSettings.vue
@@ -0,0 +1,746 @@
+
+
+
+
+
+
+
+
+
+
{{ t('graphSettings.enableDescription') }}
+
+
+
+
+
+
+
+
+
+
+
{{ t('graphSettings.tagsDescription') }}
+
+
+
+
+ {{ t('graphSettings.generateRandomTags') }}
+
+
+
+
+
+ {{ t('graphSettings.completeModelConfig') }}
+
+
+
+
+
+
+
+
+
{{ t('graphSettings.sampleTextDescription') }}
+
+
+
+
+ {{ t('graphSettings.generateRandomText') }}
+
+
+
+
+
+ {{ t('graphSettings.completeModelConfig') }}
+
+
+
+
+
+
+
+
+
{{ t('graphSettings.entityListDescription') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('graphSettings.addAttribute') }}
+
+
+
+
+
+
+
+
+
+
+
+
{{ t('graphSettings.manageEntitiesDescription') }}
+
+
+
+ {{ t('graphSettings.addEntity') }}
+
+
+
+
+
+
+
+
+
{{ t('graphSettings.relationListDescription') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ t('graphSettings.manageRelationsDescription') }}
+
+
+
+ {{ t('graphSettings.addRelation') }}
+
+
+
+
+
+
+
+
+
{{ t('graphSettings.extractActionsDescription') }}
+
+
+
+
+ {{ extracting ? t('graphSettings.extracting') : t('graphSettings.startExtraction') }}
+
+
+ {{ t('graphSettings.defaultExample') }}
+
+
+ {{ t('graphSettings.clearExample') }}
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/views/knowledge/settings/KBAdvancedSettings.vue b/frontend/src/views/knowledge/settings/KBAdvancedSettings.vue
index 71ec0fba9..a59f5ff90 100644
--- a/frontend/src/views/knowledge/settings/KBAdvancedSettings.vue
+++ b/frontend/src/views/knowledge/settings/KBAdvancedSettings.vue
@@ -210,198 +210,6 @@
-
-
-
-
-
{{ $t('knowledgeEditor.advanced.graph.description') }}
-
-
-
- {{ $t('knowledgeEditor.advanced.graph.disabledWarning') }}
-
- {{ $t('knowledgeEditor.advanced.graph.howToEnable') }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
{{ $t('knowledgeEditor.advanced.graph.promptDescription') }}
-
-
-
-
-
-
-
-
-
-
{{ $t('knowledgeEditor.advanced.graph.tagsDescription') }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ $t('knowledgeEditor.advanced.graph.addNode') }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ $t('knowledgeEditor.advanced.graph.addRelation') }}
-
-
@@ -433,25 +241,8 @@ interface MultimodalConfig {
}
}
-interface NodeExtractConfig {
- enabled: boolean
- text: string
- tags: string[]
- nodes: Array<{
- name: string
- chunks: string[]
- attributes: string[]
- }>
- relations: Array<{
- node1: string
- node2: string
- type: string
- }>
-}
-
interface Props {
multimodal: MultimodalConfig
- nodeExtract: NodeExtractConfig
allModels?: any[]
}
@@ -459,18 +250,11 @@ const props = defineProps()
const emit = defineEmits<{
'update:multimodal': [value: MultimodalConfig]
- 'update:nodeExtract': [value: NodeExtractConfig]
}>()
const localMultimodal = ref({ ...props.multimodal })
-const localNodeExtract = ref({
- ...props.nodeExtract,
- nodes: props.nodeExtract.nodes || [],
- relations: props.nodeExtract.relations || []
-})
const vllmSelectorRef = ref()
-const isGraphDatabaseEnabled = ref(false)
const isMinioEnabled = ref(false)
// Check system status on mount
@@ -478,28 +262,6 @@ 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
@@ -511,12 +273,7 @@ onMounted(async () => {
} 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'
@@ -530,14 +287,6 @@ watch(() => props.multimodal, (newVal) => {
localMultimodal.value = { ...newVal }
}, { deep: true })
-watch(() => props.nodeExtract, (newVal) => {
- localNodeExtract.value = {
- ...newVal,
- nodes: newVal.nodes || [],
- relations: newVal.relations || []
- }
-}, { deep: true })
-
// Handle multimodal toggle
const handleMultimodalToggle = () => {
// Reset related configuration when multimodal is disabled
@@ -580,67 +329,9 @@ const handleAddModel = (subSection: string) => {
uiStore.openSettings('models', subSection)
}
-const graphGuideUrl =
- import.meta.env.VITE_KG_GUIDE_URL ||
- 'https://github.com/Tencent/WeKnora/blob/main/docs/%E5%BC%80%E5%90%AF%E7%9F%A5%E8%AF%86%E5%9B%BE%E8%B0%B1%E5%8A%9F%E8%83%BD.md'
-
-// 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 guide documentation to show how to enable graph database
-const handleOpenGraphGuide = () => {
- window.open(graphGuideUrl, '_blank', 'noopener')
-}
-
// Handle configuration change
const handleConfigChange = () => {
emit('update:multimodal', localMultimodal.value)
- emit('update:nodeExtract', localNodeExtract.value)
-}
-
-// Add a new node
-const addNode = () => {
- if (!localNodeExtract.value.nodes) {
- localNodeExtract.value.nodes = []
- }
- localNodeExtract.value.nodes.push({
- name: '',
- chunks: [],
- attributes: []
- })
- handleConfigChange()
-}
-
-// Remove a node
-const removeNode = (index: number) => {
- localNodeExtract.value.nodes.splice(index, 1)
- handleConfigChange()
-}
-
-// Add a new relation
-const addRelation = () => {
- if (!localNodeExtract.value.relations) {
- localNodeExtract.value.relations = []
- }
- localNodeExtract.value.relations.push({
- node1: '',
- node2: '',
- type: ''
- })
- handleConfigChange()
-}
-
-// Remove a relation
-const removeRelation = (index: number) => {
- localNodeExtract.value.relations.splice(index, 1)
- handleConfigChange()
}
// The allModels prop keeps model options in sync
@@ -716,11 +407,6 @@ const removeRelation = (index: number) => {
align-items: center;
}
-.graph-guide-link {
- display: inline-block;
- margin-top: 8px;
-}
-
.subsection {
padding: 16px 20px;
margin: 12px 0 0 0;
diff --git a/internal/handler/initialization.go b/internal/handler/initialization.go
index 65d594446..27c7fbb11 100644
--- a/internal/handler/initialization.go
+++ b/internal/handler/initialization.go
@@ -319,13 +319,19 @@ func (h *InitializationHandler) UpdateKBConfig(c *gin.Context) {
}
kb.ExtractConfig = &types.ExtractConfig{
+ Enabled: req.NodeExtract.Enabled,
Text: req.NodeExtract.Text,
Tags: req.NodeExtract.Tags,
Nodes: nodes,
Relations: relations,
}
} else {
- kb.ExtractConfig = nil
+ kb.ExtractConfig = &types.ExtractConfig{Enabled: false}
+ }
+ if err := validateExtractConfig(kb.ExtractConfig); err != nil {
+ logger.Error(ctx, "Invalid extract configuration", err)
+ c.Error(err)
+ return
}
// 保存更新后的知识库
@@ -1811,13 +1817,7 @@ func (h *InitializationHandler) ExtractTextRelations(c *gin.Context) {
result, err := h.extractRelationsFromText(ctx, req.Text, req.Tags, req.LLMConfig)
if err != nil {
logger.Error(ctx, "文本关系提取失败", err)
- c.JSON(http.StatusOK, gin.H{
- "success": true,
- "data": gin.H{
- "success": false,
- "message": err.Error(),
- },
- })
+ c.Error(errors.NewInternalServerError("文本关系提取失败: " + err.Error()))
return
}
@@ -1885,13 +1885,7 @@ func (h *InitializationHandler) FabriText(c *gin.Context) {
result, err := h.fabriText(ctx, req.Tags, req.LLMConfig)
if err != nil {
logger.Error(ctx, "生成示例文本失败", err)
- c.JSON(http.StatusOK, gin.H{
- "success": true,
- "data": gin.H{
- "success": false,
- "message": err.Error(),
- },
- })
+ c.Error(errors.NewInternalServerError("生成示例文本失败: " + err.Error()))
return
}
diff --git a/internal/handler/knowledgebase.go b/internal/handler/knowledgebase.go
index 86e10b749..653f5d6ea 100644
--- a/internal/handler/knowledgebase.go
+++ b/internal/handler/knowledgebase.go
@@ -3,6 +3,7 @@ package handler
import (
"context"
"net/http"
+ "strconv"
"github.com/Tencent/WeKnora/internal/errors"
"github.com/Tencent/WeKnora/internal/logger"
@@ -78,6 +79,11 @@ func (h *KnowledgeBaseHandler) CreateKnowledgeBase(c *gin.Context) {
c.Error(errors.NewBadRequestError("Invalid request parameters").WithDetails(err.Error()))
return
}
+ if err := validateExtractConfig(req.ExtractConfig); err != nil {
+ logger.Error(ctx, "Invalid extract configuration", err)
+ c.Error(err)
+ return
+ }
logger.Infof(ctx, "Creating knowledge base, name: %s", req.Name)
// Create knowledge base using the service
@@ -267,3 +273,70 @@ func (h *KnowledgeBaseHandler) CopyKnowledgeBase(c *gin.Context) {
"message": "Knowledge base copy successfully",
})
}
+
+// validateExtractConfig validates the graph configuration parameters
+func validateExtractConfig(config *types.ExtractConfig) error {
+ logger.Errorf(context.Background(), "Validating extract configuration: %+v", config)
+ if config == nil {
+ return nil
+ }
+ if !config.Enabled {
+ *config = types.ExtractConfig{Enabled: false}
+ return nil
+ }
+ // Validate text field
+ if config.Text == "" {
+ return errors.NewBadRequestError("text cannot be empty")
+ }
+
+ // Validate tags field
+ if len(config.Tags) == 0 {
+ return errors.NewBadRequestError("tags cannot be empty")
+ }
+ for i, tag := range config.Tags {
+ if tag == "" {
+ return errors.NewBadRequestError("tag cannot be empty at index " + strconv.Itoa(i))
+ }
+ }
+
+ // Validate nodes
+ if len(config.Nodes) == 0 {
+ return errors.NewBadRequestError("nodes cannot be empty")
+ }
+ nodeNames := make(map[string]bool)
+ for i, node := range config.Nodes {
+ if node.Name == "" {
+ return errors.NewBadRequestError("node name cannot be empty at index " + strconv.Itoa(i))
+ }
+ // Check for duplicate node names
+ if nodeNames[node.Name] {
+ return errors.NewBadRequestError("duplicate node name: " + node.Name)
+ }
+ nodeNames[node.Name] = true
+ }
+
+ if len(config.Relations) == 0 {
+ return errors.NewBadRequestError("relations cannot be empty")
+ }
+ // Validate relations
+ for i, relation := range config.Relations {
+ if relation.Node1 == "" {
+ return errors.NewBadRequestError("relation node1 cannot be empty at index " + strconv.Itoa(i))
+ }
+ if relation.Node2 == "" {
+ return errors.NewBadRequestError("relation node2 cannot be empty at index " + strconv.Itoa(i))
+ }
+ if relation.Type == "" {
+ return errors.NewBadRequestError("relation type cannot be empty at index " + strconv.Itoa(i))
+ }
+ // Check if referenced nodes exist
+ if !nodeNames[relation.Node1] {
+ return errors.NewBadRequestError("relation references non-existent node1: " + relation.Node1)
+ }
+ if !nodeNames[relation.Node2] {
+ return errors.NewBadRequestError("relation references non-existent node2: " + relation.Node2)
+ }
+ }
+
+ return nil
+}
diff --git a/internal/types/knowledgebase.go b/internal/types/knowledgebase.go
index 3df1215e5..525efa806 100644
--- a/internal/types/knowledgebase.go
+++ b/internal/types/knowledgebase.go
@@ -197,10 +197,10 @@ func (c *VLMConfig) Scan(value interface{}) error {
type ExtractConfig struct {
Enabled bool `yaml:"enabled" json:"enabled"`
- Text string `yaml:"text" json:"text"`
- Tags []string `yaml:"tags" json:"tags"`
- Nodes []*GraphNode `yaml:"nodes" json:"nodes"`
- Relations []*GraphRelation `yaml:"relations" json:"relations"`
+ Text string `yaml:"text" json:"text,omitempty"`
+ Tags []string `yaml:"tags" json:"tags,omitempty"`
+ Nodes []*GraphNode `yaml:"nodes" json:"nodes,omitempty"`
+ Relations []*GraphRelation `yaml:"relations" json:"relations,omitempty"`
}
// Value implements the driver.Valuer interface, used to convert ExtractConfig to database value