mirror of
https://github.com/1Panel-dev/MaxKB.git
synced 2025-12-26 01:33:05 +00:00
refactor: 优化知识来源显示
--story=1016652 --user=王孝刚 【南区】应用对话回复支持显示引用分段来知识库的文档来源等信息 https://www.tapd.cn/57709429/s/1616642
This commit is contained in:
parent
936e55d3a3
commit
8240eb14bf
|
|
@ -19,7 +19,7 @@ class ParagraphPipelineModel:
|
|||
|
||||
def __init__(self, _id: str, document_id: str, dataset_id: str, content: str, title: str, status: str,
|
||||
is_active: bool, comprehensive_score: float, similarity: float, dataset_name: str, document_name: str,
|
||||
hit_handling_method: str, directly_return_similarity: float):
|
||||
hit_handling_method: str, directly_return_similarity: float, meta: dict = None):
|
||||
self.id = _id
|
||||
self.document_id = document_id
|
||||
self.dataset_id = dataset_id
|
||||
|
|
@ -33,6 +33,7 @@ class ParagraphPipelineModel:
|
|||
self.document_name = document_name
|
||||
self.hit_handling_method = hit_handling_method
|
||||
self.directly_return_similarity = directly_return_similarity
|
||||
self.meta = meta
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
|
|
@ -46,7 +47,8 @@ class ParagraphPipelineModel:
|
|||
'comprehensive_score': self.comprehensive_score,
|
||||
'similarity': self.similarity,
|
||||
'dataset_name': self.dataset_name,
|
||||
'document_name': self.document_name
|
||||
'document_name': self.document_name,
|
||||
'meta': self.meta,
|
||||
}
|
||||
|
||||
class builder:
|
||||
|
|
@ -58,6 +60,7 @@ class ParagraphPipelineModel:
|
|||
self.dataset_name = None
|
||||
self.hit_handling_method = None
|
||||
self.directly_return_similarity = 0.9
|
||||
self.meta = {}
|
||||
|
||||
def add_paragraph(self, paragraph):
|
||||
if isinstance(paragraph, Paragraph):
|
||||
|
|
@ -97,6 +100,10 @@ class ParagraphPipelineModel:
|
|||
self.similarity = similarity
|
||||
return self
|
||||
|
||||
def add_meta(self, meta: dict):
|
||||
self.meta = meta
|
||||
return self
|
||||
|
||||
def build(self):
|
||||
return ParagraphPipelineModel(str(self.paragraph.get('id')), str(self.paragraph.get('document_id')),
|
||||
str(self.paragraph.get('dataset_id')),
|
||||
|
|
@ -104,7 +111,8 @@ class ParagraphPipelineModel:
|
|||
self.paragraph.get('status'),
|
||||
self.paragraph.get('is_active'),
|
||||
self.comprehensive_score, self.similarity, self.dataset_name,
|
||||
self.document_name, self.hit_handling_method, self.directly_return_similarity)
|
||||
self.document_name, self.hit_handling_method, self.directly_return_similarity,
|
||||
self.meta)
|
||||
|
||||
|
||||
class IBaseChatPipelineStep:
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ class BaseSearchDatasetStep(ISearchDatasetStep):
|
|||
.add_document_name(paragraph.get('document_name'))
|
||||
.add_hit_handling_method(paragraph.get('hit_handling_method'))
|
||||
.add_directly_return_similarity(paragraph.get('directly_return_similarity'))
|
||||
.add_meta(paragraph.get('meta'))
|
||||
.build())
|
||||
|
||||
@staticmethod
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ SELECT
|
|||
paragraph.*,
|
||||
dataset."name" AS "dataset_name",
|
||||
"document"."name" AS "document_name",
|
||||
"document"."meta" AS "meta",
|
||||
"document"."hit_handling_method" AS "hit_handling_method",
|
||||
"document"."directly_return_similarity" as "directly_return_similarity"
|
||||
FROM
|
||||
|
|
|
|||
|
|
@ -9,15 +9,28 @@
|
|||
</div>
|
||||
<div class="mt-8" v-if="!isWorkFlow(props.type)">
|
||||
<el-space wrap>
|
||||
<el-button
|
||||
v-for="(dataset, index) in data.dataset_list"
|
||||
:key="index"
|
||||
size="small"
|
||||
class="source_dataset-button"
|
||||
@click="openParagraph(data, dataset.id)"
|
||||
>
|
||||
<span class="ellipsis" :title="dataset.name"> {{ dataset.name }}</span>
|
||||
</el-button>
|
||||
<div v-for="(paragraph, index) in uniqueParagraphList" :key="index">
|
||||
<el-icon class="mr-4" :size="25">
|
||||
<img :src="getIconPath(paragraph.document_name)" style="width: 90%" alt="" />
|
||||
</el-icon>
|
||||
<span
|
||||
v-if="!paragraph.source_url"
|
||||
class="ellipsis"
|
||||
:title="paragraph?.document_name?.trim()"
|
||||
>
|
||||
{{ paragraph?.document_name }}
|
||||
</span>
|
||||
<a
|
||||
v-else
|
||||
@click="openLink(paragraph.source_url)"
|
||||
class="ellipsis"
|
||||
:title="paragraph?.document_name?.trim()"
|
||||
>
|
||||
<span :title="paragraph?.document_name?.trim()">
|
||||
{{ paragraph?.document_name }}
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</el-space>
|
||||
</div>
|
||||
|
||||
|
|
@ -42,7 +55,7 @@
|
|||
<ExecutionDetailDialog ref="ExecutionDetailDialogRef" />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { computed, ref } from 'vue'
|
||||
import ParagraphSourceDialog from './ParagraphSourceDialog.vue'
|
||||
import ExecutionDetailDialog from './ExecutionDetailDialog.vue'
|
||||
import { isWorkFlow } from '@/utils/application'
|
||||
|
|
@ -57,6 +70,15 @@ const props = defineProps({
|
|||
default: ''
|
||||
}
|
||||
})
|
||||
const iconMap: { [key: string]: string } = {
|
||||
doc: '../../assets/doc-icon.svg',
|
||||
docx: '../../assets/docx-icon.svg',
|
||||
pdf: '../../assets/pdf-icon.svg',
|
||||
md: '../../assets/md-icon.svg',
|
||||
txt: '../../assets/txt-icon.svg',
|
||||
xls: '../../assets/xls-icon.svg',
|
||||
xlsx: '../../assets/xlsx-icon.svg'
|
||||
}
|
||||
|
||||
const ParagraphSourceDialogRef = ref()
|
||||
const ExecutionDetailDialogRef = ref()
|
||||
|
|
@ -66,6 +88,40 @@ function openParagraph(row: any, id?: string) {
|
|||
function openExecutionDetail(row: any) {
|
||||
ExecutionDetailDialogRef.value.open(row)
|
||||
}
|
||||
const uniqueParagraphList = computed(() => {
|
||||
const seen = new Set()
|
||||
return (
|
||||
props.data.paragraph_list?.filter((paragraph: any) => {
|
||||
const key = paragraph.document_name.trim()
|
||||
if (seen.has(key)) {
|
||||
return false
|
||||
}
|
||||
seen.add(key)
|
||||
// 判断如果 meta 属性不是 {} 需要json解析 转对象
|
||||
if (paragraph.meta && typeof paragraph.meta === 'string') {
|
||||
paragraph.meta = JSON.parse(paragraph.meta)
|
||||
paragraph.source_url = paragraph.meta.source_url
|
||||
}
|
||||
return true
|
||||
}) || []
|
||||
)
|
||||
})
|
||||
|
||||
function getIconPath(documentName: string) {
|
||||
const extension = documentName.split('.').pop()?.toLowerCase()
|
||||
if (!documentName || !extension) return new URL(`${iconMap['doc']}`, import.meta.url).href
|
||||
if (iconMap && extension && iconMap[extension]) {
|
||||
return new URL(`${iconMap[extension]}`, import.meta.url).href
|
||||
}
|
||||
return new URL(`${iconMap['doc']}`, import.meta.url).href
|
||||
}
|
||||
function openLink(url: string) {
|
||||
// 如果url不是以/结尾,加上/
|
||||
if (url && !url.endsWith('/')) {
|
||||
url += '/'
|
||||
}
|
||||
window.open(url, '_blank')
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.source_dataset-button {
|
||||
|
|
|
|||
|
|
@ -18,10 +18,48 @@
|
|||
<template #footer>
|
||||
<div class="footer-content flex-between">
|
||||
<el-text class="flex align-center" style="width: 70%">
|
||||
<el-icon class="mr-4">
|
||||
<Document />
|
||||
<el-icon class="mr-4" :size="25">
|
||||
<img
|
||||
src="@/assets/doc-icon.svg"
|
||||
style="width: 90%"
|
||||
alt=""
|
||||
v-if="data?.document_name?.includes('doc')"
|
||||
/>
|
||||
<img
|
||||
src="@/assets/docx-icon.svg"
|
||||
style="width: 90%"
|
||||
alt=""
|
||||
v-else-if="data?.document_name?.includes('docx')"
|
||||
/>
|
||||
<img
|
||||
src="@/assets/pdf-icon.svg"
|
||||
style="width: 90%"
|
||||
alt=""
|
||||
v-else-if="data?.document_name?.includes('pdf')"
|
||||
/>
|
||||
<img
|
||||
src="@/assets/md-icon.svg"
|
||||
style="width: 90%"
|
||||
alt=""
|
||||
v-else-if="data?.document_name?.includes('md')"
|
||||
/>
|
||||
<img
|
||||
src="@/assets/xls-icon.svg"
|
||||
style="width: 90%"
|
||||
alt=""
|
||||
v-else-if="data?.document_name?.includes('xls')"
|
||||
/>
|
||||
<img
|
||||
src="@/assets/txt-icon.svg"
|
||||
style="width: 90%"
|
||||
alt=""
|
||||
v-else-if="data?.document_name?.includes('txt')"
|
||||
/>
|
||||
<img src="@/assets/doc-icon.svg" style="width: 90%" alt="" v-else />
|
||||
</el-icon>
|
||||
<span class="ellipsis" :title="data?.document_name?.trim()"> {{ data?.document_name.trim() }}</span>
|
||||
<span class="ellipsis" :title="data?.document_name?.trim()">
|
||||
{{ data?.document_name.trim() }}</span
|
||||
>
|
||||
</el-text>
|
||||
<div class="flex align-center" style="line-height: 32px">
|
||||
<AppAvatar class="mr-8 avatar-blue" shape="square" :size="18">
|
||||
|
|
@ -45,6 +83,23 @@ const props = defineProps({
|
|||
default: 0
|
||||
}
|
||||
})
|
||||
const iconMap: { [key: string]: string } = {
|
||||
doc: '../../assets/doc-icon.svg',
|
||||
docx: '../../assets/docx-icon.svg',
|
||||
pdf: '../../assets/pdf-icon.svg',
|
||||
md: '../../assets/md-icon.svg',
|
||||
txt: '../../assets/txt-icon.svg',
|
||||
xls: '../../assets/xls-icon.svg',
|
||||
xlsx: '../../assets/xlsx-icon.svg'
|
||||
}
|
||||
function getIconPath(documentName: string) {
|
||||
const extension = documentName.split('.').pop()?.toLowerCase()
|
||||
if (!documentName || !extension) return new URL(`${iconMap['doc']}`, import.meta.url).href
|
||||
if (iconMap && extension && iconMap[extension]) {
|
||||
return new URL(`${iconMap[extension]}`, import.meta.url).href
|
||||
}
|
||||
return new URL(`${iconMap['doc']}`, import.meta.url).href
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.paragraph-source-card-height {
|
||||
|
|
|
|||
Loading…
Reference in New Issue