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
3 changes: 2 additions & 1 deletion ui/src/views/system/chat-management/users/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,8 @@ onMounted(() => loadChatUsers())

<el-table-column prop="user_group_names" width="180" label="用户组">
<template #default="{ row }">
<MkTagGroup :tags="row.user_group_names" />
<MkTagGroup v-if="row.user_group_names?.length" :tags="row.user_group_names" />
<span v-else>-</span>
</template>
</el-table-column>

Expand Down
3 changes: 2 additions & 1 deletion ui/src/views/system/identity/groups/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,8 @@ onMounted(() => {
<el-table-column prop="username" label="用户名" min-width="198" show-overflow-tooltip />
<el-table-column label="角色" min-width="198">
<template #default="{ row }">
<MkTagGroup :tags="row.roles" />
<MkTagGroup v-if="row.roles?.length" :tags="row.roles" />
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column prop="source" label="用户来源" min-width="198" show-overflow-tooltip>
Expand Down
63 changes: 49 additions & 14 deletions ui/src/workflow-canvas/nodes/data-source-web-node/details/index.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,31 @@
<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import DetailContainer from '@/workflow-canvas/execution-details/DetailContainer.vue'
import BaseHeader from '@/workflow-canvas/execution-details/BaseHeader.vue'
import type { ExecutionNodeDetail } from '@/workflow-canvas/execution-details/types'
import { getFileIconUrl } from '@/utils/icon'

defineOptions({ name: 'DataSourceWebNodeDetail' })

defineProps<{
const props = defineProps<{
data: ExecutionNodeDetail
}>()

interface WebDocument {
name?: string
content?: string
meta?: {
source_type?: string
}
}

// 切换文档时仅展示当前内容,执行详情更新后默认选中首个文档。
const documents = computed<WebDocument[]>(() => props.data.document_list ?? [])
const selectedDocumentIndex = ref(0)
const selectedDocument = computed(() => documents.value[selectedDocumentIndex.value] ?? documents.value[0])
watch(documents, () => {
selectedDocumentIndex.value = 0
})
</script>

<template>
Expand All @@ -31,19 +49,36 @@ defineProps<{
</div>

<div class="mk-gray-card-sm">
<h6 class="mb-2">输出参数</h6>
<!-- // TODO:输出参数 高度问题-->
<el-scrollbar max-height="400">
<div class="space-y-2">
<template v-for="(file_content, index) in data.document_list" :key="index">
<el-card shadow="never">
<h6 class="mb-1">{{ file_content.name }}</h6>
<MdPreview v-if="file_content.content" :model-value="file_content.content" no-img-zoom-in />
<template v-else>-</template>
</el-card>
</template>
</div>
</el-scrollbar>
<h6 class="mb-2">
输出参数
<span class="text-N600 font-normal">(每个文档仅支持预览 500 字)</span>
</h6>
<MkHorizontalScroll v-if="documents.length" class="mb-2">
<template v-for="(file, index) in documents" :key="index">
<!-- 选择要查看的文档 -->
<el-card
shadow="hover"
class="small w-45 shrink-0 cursor-pointer"
:class="{ 'border-primary! bg-primary/10!': selectedDocumentIndex === index }"
@click="selectedDocumentIndex = index"
>
<div class="flex-align-center gap-2">
<img v-if="file.meta?.source_type === 'web'" src="@/assets/file-type/web-link-icon.svg" alt="" width="24" />
<img v-else :src="getFileIconUrl(file.name || '')" alt="" width="24" />
<span class="min-w-0 truncate" :title="file.name">{{ file.name || '-' }}</span>
</div>
</el-card>
</template>
</MkHorizontalScroll>

<el-card v-if="selectedDocument" shadow="never">
<el-scrollbar max-height="400" class="-mr-4" view-class="pr-4">
<MdPreview v-if="selectedDocument.content" :model-value="selectedDocument.content.slice(0, 500)" no-img-zoom-in />
<template v-else>-</template>
</el-scrollbar>
<div class="text-N600 mt-2">500 字符</div>
</el-card>
<template v-else>-</template>
</div>
</DetailContainer>
</template>
6 changes: 6 additions & 0 deletions ui/src/workflow-canvas/nodes/document-split-node/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,9 @@ export const splitPatternOptions = [
value: '(?<!\\n)\\n\\n(?!\\n)',
},
]
// 分段策略展示文案,配置选项与执行详情共用。
export const DOCUMENT_SPLIT_STRATEGY_LABELS: Readonly<Record<string, string>> = {
auto: '智能分段(推荐)',
custom: '高级分段',
qa: 'QA 问答对',
}
92 changes: 68 additions & 24 deletions ui/src/workflow-canvas/nodes/document-split-node/details/index.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
<script setup lang="ts">
import { computed, ref } from 'vue'
import { computed, ref, watch } from 'vue'
import DetailContainer from '@/workflow-canvas/execution-details/DetailContainer.vue'
import BaseHeader from '@/workflow-canvas/execution-details/BaseHeader.vue'
import type { ExecutionNodeDetail } from '@/workflow-canvas/execution-details/types'
import { getFileIconUrl } from '@/utils/icon'
import { DOCUMENT_SPLIT_STRATEGY_LABELS } from '../constant'

defineOptions({ name: 'DocumentSplitNodeDetail' })

Expand All @@ -18,19 +20,29 @@ interface SplitParagraph {
interface SplitDocument {
name?: string
paragraphs?: SplitParagraph[]
meta?: {
source_type?: string
}
}

const documentList = computed<SplitDocument[]>(() => (props.data?.paragraph_list as SplitDocument[] | undefined) || [])
const paragraphList = computed<SplitDocument[]>(
() => (props.data?.paragraph_list as SplitDocument[] | undefined) || (props.data?.document_list as SplitDocument[] | undefined) || [],
)

// 输入内容取输入文档名;无 document_list 时回退用分段结果里的文档名
const inputNames = computed(() =>
((props.data?.document_list as SplitDocument[] | undefined) || documentList.value)
((props.data?.document_list as SplitDocument[] | undefined) || paragraphList.value)
.map((doc) => doc.name)
.filter(Boolean)
.join('、'),
)

const activeTab = ref(0)
// 文档切换后展示对应分段,详情数据更新时默认选中首个文档。
const selectedDocumentIndex = ref(0)
const selectedParagraphs = computed(() => (paragraphList.value[selectedDocumentIndex.value]?.paragraphs ?? []).slice(0, 5))
watch(paragraphList, () => {
selectedDocumentIndex.value = 0
})
</script>

<template>
Expand All @@ -43,32 +55,64 @@ const activeTab = ref(0)
<div class="mk-gray-card-sm">
<h6 class="mb-2">输入参数</h6>
<div class="space-y-2">
<div><span class="text-N600">分段规则:</span>{{ data.split_strategy || '-' }}</div>
<div><span class="text-N600">分段长度:</span>{{ data.chunk_size ?? '-' }}</div>
<div class="truncate" :title="inputNames"><span class="text-N600">输入内容:</span>{{ inputNames || '-' }}</div>
<div>
<span class="text-N600">分段策略:</span>
{{ DOCUMENT_SPLIT_STRATEGY_LABELS[data.split_strategy] ?? (data.split_strategy || '-') }}
</div>
<div>
<span class="text-N600">子分块长度:</span>
{{ data.chunk_size ?? '-' }}
</div>
<div v-if="data.split_strategy === 'custom'">
<span class="text-N600">分段标识:</span>
{{ data.patterns ?? '-' }}
</div>
<div v-if="data.split_strategy === 'custom'">
<span class="text-N600">分段长度:</span>
{{ data.limit ?? '-' }}
</div>
<div class="truncate" :title="inputNames">
<span class="text-N600">输入内容:</span>
{{ inputNames || '-' }}
</div>
</div>
</div>

<!-- 输出参数 -->
<div class="mk-gray-card-sm">
<h6 class="mb-2">输出参数 <span class="text-N600 font-normal">(每个文档仅展示前 5 个分段)</span></h6>
<h6 class="mb-2">
输出参数
<span class="text-N600 font-normal">(每个文档仅展示前 5 个分段)</span>
</h6>
<MkHorizontalScroll v-if="paragraphList.length" class="mb-2">
<template v-for="(file, index) in paragraphList" :key="index">
<!-- 选择要查看的文档 -->
<el-card
shadow="hover"
class="small w-45 shrink-0 cursor-pointer"
:class="{ 'border-primary! bg-primary/10!': selectedDocumentIndex === index }"
@click="selectedDocumentIndex = index"
>
<div class="flex-align-center gap-2">
<img v-if="file.meta?.source_type === 'web'" src="@/assets/file-type/web-link-icon.svg" alt="" width="24" />
<img v-else :src="getFileIconUrl(file.name || '')" alt="" width="24" />
<span class="min-w-0 truncate" :title="file.name">{{ file.name || '-' }}</span>
</div>
</el-card>
</template>
</MkHorizontalScroll>
<div class="space-y-2">
<el-tabs v-if="documentList.length > 0" v-model="activeTab" class="paragraph-tabs">
<el-tab-pane v-for="(doc, docIndex) in documentList" :key="docIndex" :label="doc.name" :name="docIndex">
<template v-if="(doc.paragraphs || []).length > 0">
<template v-for="(paragraph, paragraphIndex) in doc.paragraphs" :key="paragraphIndex">
<el-card shadow="never">
<div class="truncate" :title="paragraph.title">#{{ paragraphIndex + 1 }} {{ paragraph.title || '-' }}</div>
<el-scrollbar max-height="150" class="mt-1">
<MdPreview :model-value="paragraph.content" no-img-zoom-in />
</el-scrollbar>
<div class="mt-1 border-t border-dashed pt-1 text-N600">字符:{{ (paragraph.content || '').length }}</div>
</el-card>
</template>
</template>
<div v-else class="text-N600">-</div>
</el-tab-pane>
</el-tabs>
<template v-if="selectedParagraphs.length > 0">
<template v-for="(paragraph, paragraphIndex) in selectedParagraphs" :key="paragraphIndex">
<el-card shadow="never">
<h4 class="truncate mb-2" :title="paragraph.title">{{ paragraph.title || '-' }}</h4>
<el-scrollbar max-height="400" class="-mr-4" view-class="pr-4">
<MdPreview :model-value="paragraph.content" no-img-zoom-in />
</el-scrollbar>
<div class="text-N600 mt-2">{{ (paragraph.content || '').length }} 字符</div>
</el-card>
</template>
</template>
<template v-else>-</template>
</div>
</div>
Expand Down
18 changes: 11 additions & 7 deletions ui/src/workflow-canvas/nodes/document-split-node/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import NodeContainer from '@/workflow-canvas/core/node-container/index.vue'
import NodeCascader from '@/workflow-canvas/component/NodeCascader.vue'
import { createAnchorGuard } from '@/workflow-canvas/core/utils'
import type { WorkflowNodeModel } from '@/workflow-canvas/core/workflow-node'
import { splitPatternOptions } from './constant'
import { DOCUMENT_SPLIT_STRATEGY_LABELS, splitPatternOptions } from './constant'

defineOptions({ name: 'WorkflowDocumentSplitNode' })

Expand Down Expand Up @@ -94,20 +94,21 @@ onBeforeUnmount(() => anchorGuard.reset())
<!-- 分段策略 -->
<el-form-item label="分段策略" prop="split_strategy" :rules="{ required: true, message: '请选择分段策略', trigger: 'change' }">
<el-select v-model="formData.split_strategy" placeholder="请选择分段策略" :teleported="false" @change="clearHiddenValidation">
<el-option label="智能分段(推荐)" value="auto" />
<el-option label="高级分段" value="custom" />
<el-option label="QA 问答对" value="qa" />
<el-option :label="DOCUMENT_SPLIT_STRATEGY_LABELS.auto" value="auto" />
<el-option :label="DOCUMENT_SPLIT_STRATEGY_LABELS.custom" value="custom" />
<el-option :label="DOCUMENT_SPLIT_STRATEGY_LABELS.qa" value="qa" />
</el-select>
</el-form-item>
<!-- 子分块长度 -->
<el-form-item
class="mk-hide-asterisk"
:prop="formData.chunk_size_type === 'referencing' ? 'chunk_size_reference' : 'chunk_size'"
:rules="formData.chunk_size_type === 'referencing' ? [{ type: 'array', required: true, message: '请选择引用变量', trigger: 'change' }] : []"
>
<template #label>
<div class="flex-between w-full gap-2">
<span class="flex-align-center gap-1">
<span>子分块长度</span>
<span :class="formData.chunk_size_type === 'referencing' ? 'mk-required' : ''">子分块长度</span>
<MkTooltip effect="dark" placement="right">
<template #content>
核心目标是平衡检索精度与召回效率
Expand Down Expand Up @@ -149,14 +150,15 @@ onBeforeUnmount(() => anchorGuard.reset())

<!-- 分段标识 -->
<el-form-item
class="mk-hide-asterisk"
v-if="formData.split_strategy === 'custom'"
:prop="formData.patterns_type === 'referencing' ? 'patterns_reference' : 'patterns'"
:rules="formData.patterns_type === 'referencing' ? [{ type: 'array', required: true, message: '请选择引用变量', trigger: 'change' }] : []"
>
<template #label>
<div class="flex-between w-full gap-2">
<div class="flex-align-center gap-1">
<span>分段标识</span>
<span :class="formData.patterns_type === 'referencing' ? 'mk-required' : ''">分段标识</span>
<MkTooltip effect="dark" content="按照所选符号先后顺序做递归分割,分割结果超出分段长度将截取至分段长度。" placement="right">
<MkIcon name="icon_info_outlined" class="text-N600!" />
</MkTooltip>
Expand Down Expand Up @@ -193,13 +195,14 @@ onBeforeUnmount(() => anchorGuard.reset())

<!-- 分段长度 -->
<el-form-item
class="mk-hide-asterisk"
v-if="formData.split_strategy === 'custom'"
:prop="formData.limit_type === 'referencing' ? 'limit_reference' : 'limit'"
:rules="formData.limit_type === 'referencing' ? [{ type: 'array', required: true, message: '请选择引用变量', trigger: 'change' }] : []"
>
<template #label>
<div class="flex-between w-full gap-2">
<span>分段长度</span>
<span :class="formData.limit_type === 'referencing' ? 'mk-required' : ''">分段长度</span>
<el-select
v-model="formData.limit_type"
size="small"
Expand All @@ -220,6 +223,7 @@ onBeforeUnmount(() => anchorGuard.reset())
:max="100000"
:value-on-clear="0"
controls-position="right"
align="left"
class="w-full!"
:step="1"
:step-strictly="true"
Expand Down
Loading