mirror of
https://github.com/1Panel-dev/MaxKB.git
synced 2025-12-26 10:12:51 +00:00
feat: 工作流编排
This commit is contained in:
parent
ac659676f7
commit
504fe77080
|
|
@ -18,147 +18,133 @@
|
|||
v-if="item.type === WorkflowType.Question || item.type === WorkflowType.AiChat"
|
||||
>{{ item?.message_tokens + item?.answer_tokens }} tokens</span
|
||||
>
|
||||
<span class="mr-16 color-secondary">{{ item?.run_time?.toFixed(2) }} s</span>
|
||||
<el-icon class="success" :size="16"><CircleCheck /></el-icon>
|
||||
<span class="mr-16 color-secondary">{{ item?.run_time?.toFixed(2) || 0.0 }} s</span>
|
||||
<el-icon class="success" :size="16" v-if="item.status === 200"
|
||||
><CircleCheck
|
||||
/></el-icon>
|
||||
<el-icon class="danger" :size="16" v-else><CircleClose /></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
<el-collapse-transition>
|
||||
<div class="mt-12" v-if="current === index">
|
||||
<!-- 开始 -->
|
||||
<template v-if="item.type === WorkflowType.Start">
|
||||
<div class="card-never border-r-4">
|
||||
<h5 class="p-8-12">参数输入</h5>
|
||||
<div class="p-8-12 border-t-dashed lighter">{{ item.question || '-' }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 知识库检索 -->
|
||||
<template v-if="item.type == WorkflowType.SearchDataset">
|
||||
<div class="card-never border-r-4">
|
||||
<h5 class="p-8-12">检索内容</h5>
|
||||
<div class="p-8-12 border-t-dashed lighter">{{ item.question || '-' }}</div>
|
||||
</div>
|
||||
<div class="card-never border-r-4 mt-8">
|
||||
<h5 class="p-8-12">检索结果</h5>
|
||||
<div class="p-8-12 border-t-dashed lighter">
|
||||
<template v-if="item.paragraph_list?.length > 0">
|
||||
<template
|
||||
v-for="(paragraph, paragraphIndex) in item.paragraph_list"
|
||||
:key="paragraphIndex"
|
||||
>
|
||||
<CardBox
|
||||
shadow="never"
|
||||
:title="paragraph.title || '-'"
|
||||
class="paragraph-source-card cursor mb-8"
|
||||
:class="paragraph.is_active ? '' : 'disabled'"
|
||||
:showIcon="false"
|
||||
<template v-if="item.status === 200">
|
||||
<!-- 开始 -->
|
||||
<template v-if="item.type === WorkflowType.Start">
|
||||
<div class="card-never border-r-4">
|
||||
<h5 class="p-8-12">参数输入</h5>
|
||||
<div class="p-8-12 border-t-dashed lighter">{{ item.question || '-' }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 知识库检索 -->
|
||||
<template v-if="item.type == WorkflowType.SearchDataset">
|
||||
<div class="card-never border-r-4">
|
||||
<h5 class="p-8-12">检索内容</h5>
|
||||
<div class="p-8-12 border-t-dashed lighter">{{ item.question || '-' }}</div>
|
||||
</div>
|
||||
<div class="card-never border-r-4 mt-8">
|
||||
<h5 class="p-8-12">检索结果</h5>
|
||||
<div class="p-8-12 border-t-dashed lighter">
|
||||
<template v-if="item.paragraph_list?.length > 0">
|
||||
<template
|
||||
v-for="(paragraph, paragraphIndex) in item.paragraph_list"
|
||||
:key="paragraphIndex"
|
||||
>
|
||||
<template #icon>
|
||||
<AppAvatar class="mr-12 avatar-light" :size="22">
|
||||
{{ paragraphIndex + 1 + '' }}</AppAvatar
|
||||
>
|
||||
</template>
|
||||
<div class="active-button primary">
|
||||
{{ paragraph.similarity?.toFixed(3) }}
|
||||
</div>
|
||||
<template #description>
|
||||
<el-scrollbar height="150">
|
||||
<MdPreview
|
||||
ref="editorRef"
|
||||
editorId="preview-only"
|
||||
:modelValue="paragraph.content"
|
||||
/>
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
<template #footer>
|
||||
<div class="footer-content flex-between">
|
||||
<el-text>
|
||||
<el-icon>
|
||||
<Document />
|
||||
</el-icon>
|
||||
{{ paragraph?.document_name }}
|
||||
</el-text>
|
||||
<div class="flex align-center">
|
||||
<AppAvatar class="mr-8" shape="square" :size="18">
|
||||
<img
|
||||
src="@/assets/icon_document.svg"
|
||||
style="width: 58%"
|
||||
alt=""
|
||||
/>
|
||||
</AppAvatar>
|
||||
|
||||
<span class="ellipsis"> {{ paragraph?.dataset_name }}</span>
|
||||
</div>
|
||||
<CardBox
|
||||
shadow="never"
|
||||
:title="paragraph.title || '-'"
|
||||
class="paragraph-source-card cursor mb-8"
|
||||
:class="paragraph.is_active ? '' : 'disabled'"
|
||||
:showIcon="false"
|
||||
>
|
||||
<template #icon>
|
||||
<AppAvatar class="mr-12 avatar-light" :size="22">
|
||||
{{ paragraphIndex + 1 + '' }}</AppAvatar
|
||||
>
|
||||
</template>
|
||||
<div class="active-button primary">
|
||||
{{ paragraph.similarity?.toFixed(3) }}
|
||||
</div>
|
||||
</template>
|
||||
</CardBox>
|
||||
</template>
|
||||
</template>
|
||||
<template v-else> - </template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 判断器 -->
|
||||
<template v-if="item.type == WorkflowType.Condition">
|
||||
<div class="card-never border-r-4">
|
||||
<h5 class="p-8-12">判断结果</h5>
|
||||
<div class="p-8-12 border-t-dashed lighter">
|
||||
{{ item.branch_name || '-' }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<!-- AI 对话 / 问题优化-->
|
||||
<template
|
||||
v-if="item.type == WorkflowType.AiChat || item.type == WorkflowType.Question"
|
||||
>
|
||||
<div class="card-never border-r-4">
|
||||
<h5 class="p-8-12">角色设定 (System)</h5>
|
||||
<div class="p-8-12 border-t-dashed lighter">
|
||||
{{ item.system || '-' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-never border-r-4 mt-8">
|
||||
<h5 class="p-8-12">历史记录</h5>
|
||||
<div class="p-8-12 border-t-dashed lighter">
|
||||
<template v-if="item.history_message?.length > 0">
|
||||
<p
|
||||
class="mt-4 mb-4"
|
||||
v-for="(history, historyIndex) in item.history_message"
|
||||
:key="historyIndex"
|
||||
>
|
||||
<span class="color-secondary mr-4">{{ history.role }}:</span
|
||||
><span>{{ history.content }}</span>
|
||||
</p>
|
||||
</template>
|
||||
<template v-else> - </template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-never border-r-4 mt-8">
|
||||
<h5 class="p-8-12">本次对话</h5>
|
||||
<div class="p-8-12 border-t-dashed lighter pre-line">
|
||||
{{ item.question || '-' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-never border-r-4 mt-8">
|
||||
<h5 class="p-8-12">AI 回答</h5>
|
||||
<div class="p-8-12 border-t-dashed lighter">
|
||||
<MdPreview
|
||||
v-if="item.answer"
|
||||
ref="editorRef"
|
||||
editorId="preview-only"
|
||||
:modelValue="item.answer"
|
||||
style="background: none"
|
||||
/>
|
||||
<template v-else> - </template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #description>
|
||||
<el-scrollbar height="150">
|
||||
<MdPreview
|
||||
ref="editorRef"
|
||||
editorId="preview-only"
|
||||
:modelValue="paragraph.content"
|
||||
/>
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
<template #footer>
|
||||
<div class="footer-content flex-between">
|
||||
<el-text>
|
||||
<el-icon>
|
||||
<Document />
|
||||
</el-icon>
|
||||
{{ paragraph?.document_name }}
|
||||
</el-text>
|
||||
<div class="flex align-center">
|
||||
<AppAvatar class="mr-8" shape="square" :size="18">
|
||||
<img
|
||||
src="@/assets/icon_document.svg"
|
||||
style="width: 58%"
|
||||
alt=""
|
||||
/>
|
||||
</AppAvatar>
|
||||
|
||||
<!-- 指定回复 -->
|
||||
<template v-if="item.type === WorkflowType.Reply">
|
||||
<div class="card-never border-r-4">
|
||||
<h5 class="p-8-12">回复内容</h5>
|
||||
<div class="p-8-12 border-t-dashed lighter">
|
||||
<el-scrollbar height="150">
|
||||
<span class="ellipsis"> {{ paragraph?.dataset_name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</CardBox>
|
||||
</template>
|
||||
</template>
|
||||
<template v-else> - </template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 判断器 -->
|
||||
<template v-if="item.type == WorkflowType.Condition">
|
||||
<div class="card-never border-r-4">
|
||||
<h5 class="p-8-12">判断结果</h5>
|
||||
<div class="p-8-12 border-t-dashed lighter">
|
||||
{{ item.branch_name || '-' }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<!-- AI 对话 / 问题优化-->
|
||||
<template
|
||||
v-if="item.type == WorkflowType.AiChat || item.type == WorkflowType.Question"
|
||||
>
|
||||
<div class="card-never border-r-4">
|
||||
<h5 class="p-8-12">角色设定 (System)</h5>
|
||||
<div class="p-8-12 border-t-dashed lighter">
|
||||
{{ item.system || '-' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-never border-r-4 mt-8">
|
||||
<h5 class="p-8-12">历史记录</h5>
|
||||
<div class="p-8-12 border-t-dashed lighter">
|
||||
<template v-if="item.history_message?.length > 0">
|
||||
<p
|
||||
class="mt-4 mb-4"
|
||||
v-for="(history, historyIndex) in item.history_message"
|
||||
:key="historyIndex"
|
||||
>
|
||||
<span class="color-secondary mr-4">{{ history.role }}:</span
|
||||
><span>{{ history.content }}</span>
|
||||
</p>
|
||||
</template>
|
||||
<template v-else> - </template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-never border-r-4 mt-8">
|
||||
<h5 class="p-8-12">本次对话</h5>
|
||||
<div class="p-8-12 border-t-dashed lighter pre-line">
|
||||
{{ item.question || '-' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-never border-r-4 mt-8">
|
||||
<h5 class="p-8-12">AI 回答</h5>
|
||||
<div class="p-8-12 border-t-dashed lighter">
|
||||
<MdPreview
|
||||
v-if="item.answer"
|
||||
ref="editorRef"
|
||||
|
|
@ -167,8 +153,33 @@
|
|||
style="background: none"
|
||||
/>
|
||||
<template v-else> - </template>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 指定回复 -->
|
||||
<template v-if="item.type === WorkflowType.Reply">
|
||||
<div class="card-never border-r-4">
|
||||
<h5 class="p-8-12">回复内容</h5>
|
||||
<div class="p-8-12 border-t-dashed lighter">
|
||||
<el-scrollbar height="150">
|
||||
<MdPreview
|
||||
v-if="item.answer"
|
||||
ref="editorRef"
|
||||
editorId="preview-only"
|
||||
:modelValue="item.answer"
|
||||
style="background: none"
|
||||
/>
|
||||
<template v-else> - </template>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="card-never border-r-4">
|
||||
<h5 class="p-8-12">错误日志</h5>
|
||||
<div class="p-8-12 border-t-dashed lighter">{{ item.err_message || '-' }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
export enum SearchMode {
|
||||
embedding = '向量检索',
|
||||
keywords = '全文检索',
|
||||
blend = '混合检索'
|
||||
}
|
||||
|
|
@ -90,10 +90,7 @@
|
|||
</el-dropdown-menu>
|
||||
<div class="breadcrumb__footer border-t" style="padding: 8px 11px; min-width: 200px">
|
||||
<template v-if="isApplication">
|
||||
<div
|
||||
class="w-full text-left cursor"
|
||||
@click="router.push({ path: '/application/create' })"
|
||||
>
|
||||
<div class="w-full text-left cursor" @click="openCreateDialog">
|
||||
<el-button link>
|
||||
<el-icon class="mr-4"><Plus /></el-icon> 创建应用
|
||||
</el-button>
|
||||
|
|
@ -115,11 +112,13 @@
|
|||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
<CreateApplicationDialog ref="CreateApplicationDialogRef" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { onBeforeRouteLeave, useRouter, useRoute } from 'vue-router'
|
||||
import CreateApplicationDialog from '@/views/application/component/CreateApplicationDialog.vue'
|
||||
import { isAppIcon, isWorkFlow } from '@/utils/application'
|
||||
import useStore from '@/stores'
|
||||
const { common, dataset, application } = useStore()
|
||||
|
|
@ -134,6 +133,7 @@ onBeforeRouteLeave((to, from) => {
|
|||
common.saveBreadcrumb(null)
|
||||
})
|
||||
|
||||
const CreateApplicationDialogRef = ref()
|
||||
const list = ref<any[]>([])
|
||||
const loading = ref(false)
|
||||
|
||||
|
|
@ -154,6 +154,11 @@ const isDataset = computed(() => {
|
|||
const { meta } = route as any
|
||||
return meta?.activeMenu.includes('dataset')
|
||||
})
|
||||
|
||||
function openCreateDialog() {
|
||||
CreateApplicationDialogRef.value.open()
|
||||
}
|
||||
|
||||
function changeMenu(id: string) {
|
||||
const lastMatched = route.matched[route.matched.length - 1]
|
||||
if (lastMatched) {
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@
|
|||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="!isWorkflow"
|
||||
v-if="!isWorkflowType"
|
||||
:label="$t('views.application.applicationForm.dialogues.noReferencesAction')"
|
||||
>
|
||||
<el-form
|
||||
|
|
@ -180,6 +180,7 @@
|
|||
import { ref, watch, reactive } from 'vue'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import { isWorkFlow } from '@/utils/application'
|
||||
import { t } from '@/locales'
|
||||
const emit = defineEmits(['refresh'])
|
||||
|
||||
|
|
@ -228,7 +229,7 @@ const noReferencesRules = reactive<FormRules<any>>({
|
|||
const dialogVisible = ref<boolean>(false)
|
||||
const loading = ref(false)
|
||||
|
||||
const isWorkflow = ref(false)
|
||||
const isWorkflowType = ref(false)
|
||||
|
||||
watch(dialogVisible, (bool) => {
|
||||
if (!bool) {
|
||||
|
|
@ -251,7 +252,7 @@ watch(dialogVisible, (bool) => {
|
|||
})
|
||||
|
||||
const open = (data: any, type?: string) => {
|
||||
isWorkflow.value = type === 'workflow'
|
||||
isWorkflowType.value = isWorkFlow(type)
|
||||
form.value = { ...form.value, ...cloneDeep(data) }
|
||||
noReferencesform.value[form.value.no_references_setting.status] =
|
||||
form.value.no_references_setting.value
|
||||
|
|
@ -259,15 +260,21 @@ const open = (data: any, type?: string) => {
|
|||
}
|
||||
|
||||
const submit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
await formEl.validate((valid, fields) => {
|
||||
if (valid) {
|
||||
form.value.no_references_setting.value =
|
||||
noReferencesform.value[form.value.no_references_setting.status]
|
||||
emit('refresh', form.value)
|
||||
dialogVisible.value = false
|
||||
}
|
||||
})
|
||||
if (isWorkflowType.value) {
|
||||
delete form.value['no_references_setting']
|
||||
emit('refresh', form.value)
|
||||
dialogVisible.value = false
|
||||
} else {
|
||||
if (!formEl) return
|
||||
await formEl.validate((valid, fields) => {
|
||||
if (valid) {
|
||||
form.value.no_references_setting.value =
|
||||
noReferencesform.value[form.value.no_references_setting.status]
|
||||
emit('refresh', form.value)
|
||||
dialogVisible.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function changeHandle(val: string) {
|
||||
|
|
|
|||
|
|
@ -66,7 +66,9 @@
|
|||
<el-row>
|
||||
<el-col :span="12" class="color-secondary lighter">检索模式</el-col>
|
||||
<el-col :span="12" class="lighter">
|
||||
{{ form_data.dataset_setting.search_mode }}</el-col
|
||||
{{
|
||||
SearchMode[form_data.dataset_setting.search_mode as keyof typeof SearchMode]
|
||||
}}</el-col
|
||||
>
|
||||
<el-col :span="12" class="color-secondary lighter"> 相似度高于</el-col>
|
||||
<el-col :span="12" class="lighter">
|
||||
|
|
@ -108,10 +110,10 @@ import NodeContainer from '@/workflow/common/NodeContainer.vue'
|
|||
import NodeCascader from '@/workflow/common/NodeCascader.vue'
|
||||
import AddDatasetDialog from '@/views/application/component/AddDatasetDialog.vue'
|
||||
import ParamSettingDialog from '@/views/application/component/ParamSettingDialog.vue'
|
||||
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { relatedObject } from '@/utils/utils'
|
||||
import { SearchMode } from '@/enums/application'
|
||||
import useStore from '@/stores'
|
||||
const { dataset, application, user } = useStore()
|
||||
const {
|
||||
|
|
@ -156,7 +158,7 @@ function refreshParam(data: any) {
|
|||
}
|
||||
|
||||
const openParamSettingDialog = () => {
|
||||
ParamSettingDialogRef.value?.open(form_data.value.dataset_setting, 'workflow')
|
||||
ParamSettingDialogRef.value?.open(form_data.value.dataset_setting, 'WORK_FLOW')
|
||||
}
|
||||
|
||||
function removeDataset(id: any) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue