Merge branch 'feature/parallel-tool-test' of https://github.com/LSTM-Kirigaya/openmcp-client into feature/parallel-tool-test
This commit is contained in:
commit
a45f65816e
@ -161,6 +161,125 @@ export function topoSortParallel(state: DiagramState): string[][] {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function makeParallelTest(
|
||||||
|
dataViews: Reactive<NodeDataView>[],
|
||||||
|
enableXmlWrapper: boolean,
|
||||||
|
prompt: string | null = null,
|
||||||
|
context: DiagramContext
|
||||||
|
) {
|
||||||
|
if (dataViews.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置所有节点状态为运行中
|
||||||
|
const createAt = Date.now();
|
||||||
|
dataViews.forEach(dataView => {
|
||||||
|
dataView.status = 'running';
|
||||||
|
dataView.createAt = createAt;
|
||||||
|
});
|
||||||
|
context.render();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const loop = new TaskLoop({ maxEpochs: 1 });
|
||||||
|
|
||||||
|
// 构建所有工具的信息
|
||||||
|
const allTools = dataViews.map(dataView => ({
|
||||||
|
name: dataView.tool.name,
|
||||||
|
description: dataView.tool.description,
|
||||||
|
inputSchema: dataView.tool.inputSchema,
|
||||||
|
enabled: true
|
||||||
|
}));
|
||||||
|
|
||||||
|
// 构建测试提示词,包含所有工具
|
||||||
|
const toolNames = dataViews.map(dv => dv.tool.name).join(', ');
|
||||||
|
const usePrompt = (prompt || 'please call the tools {tool} to make some test').replace('{tool}', toolNames);
|
||||||
|
|
||||||
|
const chatStorage = {
|
||||||
|
messages: [],
|
||||||
|
settings: {
|
||||||
|
temperature: 0.6,
|
||||||
|
systemPrompt: '',
|
||||||
|
enableTools: allTools,
|
||||||
|
enableWebSearch: false,
|
||||||
|
contextLength: 5,
|
||||||
|
enableXmlWrapper,
|
||||||
|
parallelToolCalls: true // 开启并行工具调用
|
||||||
|
}
|
||||||
|
} as ChatStorage;
|
||||||
|
|
||||||
|
loop.setMaxEpochs(1);
|
||||||
|
|
||||||
|
// 记录工具调用信息,用于匹配工具调用结果
|
||||||
|
const toolCallMap: Map<string, Reactive<NodeDataView>> = new Map(); // toolCallId -> dataView
|
||||||
|
let completedToolsCount = 0;
|
||||||
|
|
||||||
|
loop.registerOnToolCall(toolCall => {
|
||||||
|
// 找到对应的dataView
|
||||||
|
const dataView = dataViews.find(dv => dv.tool.name === toolCall.function?.name);
|
||||||
|
if (dataView) {
|
||||||
|
dataView.function = toolCall.function;
|
||||||
|
dataView.llmTimecost = Date.now() - createAt;
|
||||||
|
context.render();
|
||||||
|
|
||||||
|
// 记录工具调用ID与dataView的映射
|
||||||
|
if (toolCall.id) {
|
||||||
|
toolCallMap.set(toolCall.id, dataView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return toolCall;
|
||||||
|
});
|
||||||
|
|
||||||
|
loop.registerOnToolCalled(toolCalled => {
|
||||||
|
// 这里我们需要改变策略,因为没有工具调用ID信息
|
||||||
|
// 对于并行调用,我们可以根据工具调用的顺序来匹配结果
|
||||||
|
// 简单的策略:找到第一个仍在运行状态的工具
|
||||||
|
const runningView = dataViews.find(dv => dv.status === 'running');
|
||||||
|
if (runningView) {
|
||||||
|
runningView.toolcallTimecost = Date.now() - createAt - (runningView.llmTimecost || 0);
|
||||||
|
|
||||||
|
if (toolCalled.state === MessageState.Success) {
|
||||||
|
runningView.status = 'success';
|
||||||
|
runningView.result = toolCalled.content;
|
||||||
|
} else {
|
||||||
|
runningView.status = 'error';
|
||||||
|
runningView.result = toolCalled.content;
|
||||||
|
}
|
||||||
|
|
||||||
|
completedToolsCount++;
|
||||||
|
context.render();
|
||||||
|
|
||||||
|
// 如果所有工具都完成了,终止循环
|
||||||
|
if (completedToolsCount >= dataViews.length) {
|
||||||
|
loop.abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return toolCalled;
|
||||||
|
});
|
||||||
|
|
||||||
|
loop.registerOnError(error => {
|
||||||
|
dataViews.forEach(dataView => {
|
||||||
|
if (dataView.status === 'running') {
|
||||||
|
dataView.status = 'error';
|
||||||
|
dataView.result = error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
context.render();
|
||||||
|
});
|
||||||
|
|
||||||
|
await loop.start(chatStorage, usePrompt);
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
const finishAt = Date.now();
|
||||||
|
dataViews.forEach(dataView => {
|
||||||
|
dataView.finishAt = finishAt;
|
||||||
|
if (dataView.status === 'running') {
|
||||||
|
dataView.status = 'success';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
context.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function makeNodeTest(
|
export async function makeNodeTest(
|
||||||
dataView: Reactive<NodeDataView>,
|
dataView: Reactive<NodeDataView>,
|
||||||
enableXmlWrapper: boolean,
|
enableXmlWrapper: boolean,
|
||||||
|
@ -37,6 +37,13 @@
|
|||||||
color: enableXmlWrapper ? 'var(--main-color)' : undefined
|
color: enableXmlWrapper ? 'var(--main-color)' : undefined
|
||||||
}">XML</span>
|
}">XML</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div style="display: flex; align-items: center; margin-bottom: 8px;">
|
||||||
|
<el-switch v-model="enableParallelTest" style="margin-right: 8px;" />
|
||||||
|
<span :style="{
|
||||||
|
opacity: enableParallelTest ? 1 : 0.7,
|
||||||
|
color: enableParallelTest ? 'var(--main-color)' : undefined
|
||||||
|
}">{{ t('parallel-test') }}</span>
|
||||||
|
</div>
|
||||||
<div style="text-align: right;">
|
<div style="text-align: right;">
|
||||||
<el-button size="small" @click="testFormVisible = false">{{ t("cancel") }}</el-button>
|
<el-button size="small" @click="testFormVisible = false">{{ t("cancel") }}</el-button>
|
||||||
<el-button size="small" type="primary" @click="onTestConfirm">
|
<el-button size="small" type="primary" @click="onTestConfirm">
|
||||||
@ -70,7 +77,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, nextTick, provide, ref } from 'vue';
|
import { computed, nextTick, provide, ref } from 'vue';
|
||||||
import Diagram from './diagram.vue';
|
import Diagram from './diagram.vue';
|
||||||
import { makeNodeTest, topoSortParallel, type DiagramContext, type DiagramState } from './diagram';
|
import { makeNodeTest, makeParallelTest, topoSortParallel, type DiagramContext, type DiagramState } from './diagram';
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
|
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
@ -137,43 +144,60 @@ if (autoDetectDiagram) {
|
|||||||
// 新增:自检参数表单相关
|
// 新增:自检参数表单相关
|
||||||
const testFormVisible = ref(false);
|
const testFormVisible = ref(false);
|
||||||
const enableXmlWrapper = ref(false);
|
const enableXmlWrapper = ref(false);
|
||||||
|
const enableParallelTest = ref(false);
|
||||||
const testPrompt = ref('please call the tool {tool} to make some test');
|
const testPrompt = ref('please call the tool {tool} to make some test');
|
||||||
|
|
||||||
async function onTestConfirm() {
|
async function onTestConfirm() {
|
||||||
testFormVisible.value = false;
|
testFormVisible.value = false;
|
||||||
// 这里可以将 enableXmlWrapper.value 和 testPrompt.value 传递给自检逻辑
|
|
||||||
const state = context.state;
|
const state = context.state;
|
||||||
|
|
||||||
|
|
||||||
tabStorage.autoDetectDiagram!.views = [];
|
tabStorage.autoDetectDiagram!.views = [];
|
||||||
|
|
||||||
if (state) {
|
if (state) {
|
||||||
const dispatches = topoSortParallel(state);
|
if (enableParallelTest.value) {
|
||||||
for (const nodeIds of dispatches) {
|
// 并行测试模式:一次性测试所有工具
|
||||||
for (const id of nodeIds) {
|
const allViews = Array.from(state.dataView.values());
|
||||||
const view = state.dataView.get(id);
|
await makeParallelTest(allViews, enableXmlWrapper.value, testPrompt.value, context);
|
||||||
if (view) {
|
|
||||||
await makeNodeTest(view, enableXmlWrapper.value, testPrompt.value, context)
|
// 保存结果
|
||||||
tabStorage.autoDetectDiagram!.views!.push({
|
allViews.forEach(view => {
|
||||||
tool: view.tool,
|
tabStorage.autoDetectDiagram!.views!.push({
|
||||||
status: view.status,
|
tool: view.tool,
|
||||||
function: view.function,
|
status: view.status,
|
||||||
result: view.result,
|
function: view.function,
|
||||||
createAt: view.createAt,
|
result: view.result,
|
||||||
finishAt: view.finishAt,
|
createAt: view.createAt,
|
||||||
llmTimecost: view.llmTimecost,
|
finishAt: view.finishAt,
|
||||||
toolcallTimecost: view.toolcallTimecost,
|
llmTimecost: view.llmTimecost,
|
||||||
});
|
toolcallTimecost: view.toolcallTimecost,
|
||||||
|
});
|
||||||
context.render();
|
});
|
||||||
|
} else {
|
||||||
|
// 串行测试模式:按拓扑顺序逐个测试
|
||||||
|
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 {
|
} else {
|
||||||
ElMessage.error('error');
|
ElMessage.error('error');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const resetPopoverVisible = ref(false);
|
const resetPopoverVisible = ref(false);
|
||||||
|
@ -157,6 +157,7 @@
|
|||||||
"feedback": "Feedback",
|
"feedback": "Feedback",
|
||||||
"waiting-mcp-server": "Waiting for MCP server response",
|
"waiting-mcp-server": "Waiting for MCP server response",
|
||||||
"parallel-tool-calls": "Allow model to call multiple tools in single response",
|
"parallel-tool-calls": "Allow model to call multiple tools in single response",
|
||||||
|
"parallel-test": "Parallel Test",
|
||||||
"proxy-server": "Proxy Server",
|
"proxy-server": "Proxy Server",
|
||||||
"update-model-list": "Update Model List",
|
"update-model-list": "Update Model List",
|
||||||
"preset-env-sync.success": "Preset environment variables synced",
|
"preset-env-sync.success": "Preset environment variables synced",
|
||||||
|
@ -157,6 +157,7 @@
|
|||||||
"feedback": "反馈",
|
"feedback": "反馈",
|
||||||
"waiting-mcp-server": "等待 MCP 服务器响应",
|
"waiting-mcp-server": "等待 MCP 服务器响应",
|
||||||
"parallel-tool-calls": "允许模型在单轮回复中调用多个工具",
|
"parallel-tool-calls": "允许模型在单轮回复中调用多个工具",
|
||||||
|
"parallel-test": "并行测试",
|
||||||
"proxy-server": "代理服务器",
|
"proxy-server": "代理服务器",
|
||||||
"update-model-list": "更新模型列表",
|
"update-model-list": "更新模型列表",
|
||||||
"preset-env-sync.success": "预设环境变量同步完成",
|
"preset-env-sync.success": "预设环境变量同步完成",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user