2025-07-06 16:47:41 +08:00

212 lines
6.6 KiB
Vue

<template>
<el-dialog v-model="showDialog" width="800px" class="no-padding-dialog">
<template #header>
<div style="display: flex; align-items: center;">
<span>Tool Diagram</span>
&ensp;
<!-- 重置按钮弹出下拉列表 -->
<el-popover placement="bottom" width="180" trigger="click" v-model:visible="resetPopoverVisible">
<template #reference>
<el-button size="small" type="primary">
{{ t("preset") }}
</el-button>
</template>
<div style="display: flex; gap: 8px;">
<el-button size="small" @click="tomoPreset('serial')">
<span class="iconfont icon-serial"></span>
</el-button>
<el-button size="small" @click="tomoPreset('parallel')">
<span class="iconfont icon-parallel"></span>
</el-button>
</div>
</el-popover>
<!-- 原有自检程序弹出表单 -->
<el-popover placement="top" width="350" trigger="click" v-model:visible="testFormVisible">
<template #reference>
<el-button size="small" type="primary">
{{ t('start-auto-detect') }}
</el-button>
</template>
<!-- ...原有自检表单内容... -->
<el-input type="textarea" v-model="testPrompt" :rows="2" style="margin-bottom: 8px;"
placeholder="请输入 prompt" />
<div style="display: flex; align-items: center; margin-bottom: 8px;">
<el-switch v-model="enableXmlWrapper" style="margin-right: 8px;" />
<span :style="{
opacity: enableXmlWrapper ? 1 : 0.7,
color: enableXmlWrapper ? 'var(--main-color)' : undefined
}">XML</span>
</div>
<div style="text-align: right;">
<el-button size="small" @click="testFormVisible = false">{{ t("cancel") }}</el-button>
<el-button size="small" type="primary" @click="onTestConfirm">
{{ t("confirm") }}
</el-button>
</div>
</el-popover>
</div>
</template>
<el-scrollbar height="80vh">
<Diagram :tab-id="props.tabId" />
</el-scrollbar>
<div class="caption" v-if="showCaption">
{{ caption }}
</div>
<div v-else>
<span class="caption">
<el-tooltip
placement="top"
effect="light"
:content="t('self-detect-caption')"
>
<span class="iconfont icon-about"></span>
</el-tooltip>
</span>
</div>
</el-dialog>
</template>
<script setup lang="ts">
import { computed, nextTick, provide, ref } from 'vue';
import Diagram from './diagram.vue';
import { makeNodeTest, topoSortParallel, type DiagramContext, type DiagramState } from './diagram';
import { ElMessage } from 'element-plus';
import { useI18n } from 'vue-i18n';
import type { ToolStorage } from '../tools';
import { tabs } from '../../panel';
const { t } = useI18n();
const caption = ref('');
const showCaption = ref(false);
const props = defineProps({
modelValue: {
type: Boolean,
default: false
},
tabId: {
type: Number,
required: true
}
});
const emit = defineEmits(['update:modelValue']);
const showDialog = computed({
get: () => props.modelValue,
set: v => emit('update:modelValue', v)
});
function setCaption(text: string) {
caption.value = text;
if (caption.value) {
nextTick(() => {
showCaption.value = true;
});
} else {
nextTick(() => {
showCaption.value = false;
});
}
}
const context: DiagramContext = {
preset: () => { },
render: () => { },
state: undefined,
setCaption
};
provide('context', context);
const tab = tabs.content[props.tabId];
const tabStorage = tab.storage as ToolStorage;
const autoDetectDiagram = tabStorage.autoDetectDiagram;
if (autoDetectDiagram) {
// ...
} else {
tabStorage.autoDetectDiagram = {
edges: [],
views: []
};
}
// 新增:自检参数表单相关
const testFormVisible = ref(false);
const enableXmlWrapper = ref(false);
const testPrompt = ref('please call the tool {tool} to make some test');
async function onTestConfirm() {
testFormVisible.value = false;
// 这里可以将 enableXmlWrapper.value 和 testPrompt.value 传递给自检逻辑
const state = context.state;
tabStorage.autoDetectDiagram!.views = [];
if (state) {
const dispatches = topoSortParallel(state);
for (const nodeIds of dispatches) {
for (const id of nodeIds) {
const view = state.dataView.get(id);
if (view) {
await makeNodeTest(view, enableXmlWrapper.value, testPrompt.value, context)
tabStorage.autoDetectDiagram!.views!.push({
tool: view.tool,
status: view.status,
function: view.function,
result: view.result,
createAt: view.createAt,
finishAt: view.finishAt,
llmTimecost: view.llmTimecost,
toolcallTimecost: view.toolcallTimecost,
});
context.render();
}
}
}
} else {
ElMessage.error('error');
}
}
const resetPopoverVisible = ref(false);
function tomoPreset(type: string) {
resetPopoverVisible.value = false;
context.preset?.(type);
}
</script>
<style>
.no-padding-dialog {
margin-top: 30px !important;
width: 90vw !important;
}
.no-padding-dialog .caption {
position: absolute;
right: 30px;
bottom: 10px;
margin: 0 auto;
width: fit-content;
min-height: 32px;
background: rgba(245, 247, 250, 0.05);
border-radius: 8px;
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.06);
color: var(--main-color);
font-size: 15px;
display: flex;
align-items: center;
justify-content: center;
padding: 6px 16px;
z-index: 10;
transition: background 0.2s;
}
</style>