From 92c8cf90ed4982f7cb2fd1eb603e181b647f959d Mon Sep 17 00:00:00 2001
From: Kirigaya <1193466151@qq.com>
Date: Wed, 2 Jul 2025 23:02:09 +0800
Subject: [PATCH] support tomology detection
---
.../main-panel/tool/auto-detector.vue | 52 ++++--
.../src/components/main-panel/tool/diagram.ts | 161 ++++++++++++------
.../components/main-panel/tool/diagram.vue | 1 +
3 files changed, 145 insertions(+), 69 deletions(-)
diff --git a/renderer/src/components/main-panel/tool/auto-detector.vue b/renderer/src/components/main-panel/tool/auto-detector.vue
index 1e9f578..ebb4143 100644
--- a/renderer/src/components/main-panel/tool/auto-detector.vue
+++ b/renderer/src/components/main-panel/tool/auto-detector.vue
@@ -25,31 +25,53 @@
diff --git a/renderer/src/components/main-panel/tool/diagram.ts b/renderer/src/components/main-panel/tool/diagram.ts
index bcd620f..f95bb80 100644
--- a/renderer/src/components/main-panel/tool/diagram.ts
+++ b/renderer/src/components/main-panel/tool/diagram.ts
@@ -1,4 +1,8 @@
import type { ElkNode } from 'elkjs/lib/elk-api';
+import { TaskLoop } from '../chat/core/task-loop';
+import type { Reactive } from 'vue';
+import type { ChatStorage } from '../chat/chat-box/chat';
+import { ElMessage } from 'element-plus';
export interface Edge {
id: string;
@@ -77,65 +81,114 @@ export function invalidConnectionDetector(state: DiagramState, d: Node): CanConn
}
}
+/**
+ * @description 拓扑排序,输出每一层可以并行调度的节点id数组
+ * @returns string[][] 每一层可以并行调度的节点id数组
+ */
+export function topoSortParallel(state: DiagramState): string[][] {
+ // 统计每个节点的入度
+ const inDegree: Record = {};
+ state.nodes.forEach(node => {
+ inDegree[node.id] = 0;
+ });
+ state.edges.forEach(edge => {
+ const tgt = edge.targets[0];
+ if (tgt in inDegree) {
+ inDegree[tgt]++;
+ }
+ });
-// export async function generateAIMockData(params: any) {
-// if (!currentTool.value?.inputSchema) return;
-// aiMockLoading.value = true;
-// try {
-// const loop = new TaskLoop({ maxEpochs: 1 });
-// const usePrompt = prompt || `please call the tool ${currentTool.value.name} to make some test`;
-// const chatStorage = {
-// messages: [],
-// settings: {
-// temperature: 0.6,
-// systemPrompt: '',
-// enableTools: [{
-// name: currentTool.value.name,
-// description: currentTool.value.description,
-// inputSchema: currentTool.value.inputSchema,
-// enabled: true
-// }],
-// enableWebSearch: false,
-// contextLength: 5,
-// enableXmlWrapper: enableXmlWrapper.value,
-// parallelToolCalls: false
-// }
-// } as ChatStorage;
+ // 初始化队列,收集所有入度为0的节点
+ const result: string[][] = [];
+ let queue: string[] = Object.keys(inDegree).filter(id => inDegree[id] === 0);
-// loop.setMaxEpochs(1);
+ const visited = new Set();
-// let aiMockJson: any = undefined;
+ while (queue.length > 0) {
+ // 当前层可以并行的节点
+ result.push([...queue]);
+ const nextQueue: string[] = [];
+ for (const id of queue) {
+ visited.add(id);
+ // 遍历所有以当前节点为源的边,减少目标节点的入度
+ state.edges.forEach(edge => {
+ if (edge.sources[0] === id) {
+ const tgt = edge.targets[0];
+ inDegree[tgt]--;
+ // 如果目标节点入度为0且未访问过,加入下一层
+ if (inDegree[tgt] === 0 && !visited.has(tgt)) {
+ nextQueue.push(tgt);
+ }
+ }
+ });
+ }
+ queue = nextQueue;
+ }
-// loop.registerOnToolCall(toolCall => {
-// console.log(toolCall);
+ // 检查是否有环
+ if (visited.size !== state.nodes.length) {
+ throw new Error('图中存在环,无法进行拓扑排序');
+ }
+
+ return result;
+}
+
+export async function makeNodeTest(dataView: Reactive, enableXmlWrapper: boolean, prompt: string | null = null) {
+ if (!dataView.tool.inputSchema) {
+ return;
+ }
+
+ dataView.loading = true;
+
+ try {
+ const loop = new TaskLoop({ maxEpochs: 1 });
+ const usePrompt = prompt || `please call the tool ${dataView.too.name} to make some test`;
+ const chatStorage = {
+ messages: [],
+ settings: {
+ temperature: 0.6,
+ systemPrompt: '',
+ enableTools: [{
+ name: dataView.too.name,
+ description: dataView.too.description,
+ inputSchema: dataView.too.inputSchema,
+ enabled: true
+ }],
+ enableWebSearch: false,
+ contextLength: 5,
+ enableXmlWrapper,
+ parallelToolCalls: false
+ }
+ } as ChatStorage;
+
+ loop.setMaxEpochs(1);
+
+ let aiMockJson: any = undefined;
+
+ loop.registerOnToolCall(toolCall => {
+ console.log(toolCall);
-// if (toolCall.function?.name === currentTool.value?.name) {
-// try {
-// const toolArgs = JSON.parse(toolCall.function?.arguments || '{}');
-// aiMockJson = toolArgs;
-// } catch (e) {
-// ElMessage.error('AI 生成的 JSON 解析错误');
-// }
-// } else {
-// ElMessage.error('AI 调用了未知的工具');
-// }
-// loop.abort();
-// return toolCall;
-// });
+ if (toolCall.function?.name === dataView.too?.name) {
+ try {
+ const toolArgs = JSON.parse(toolCall.function?.arguments || '{}');
+ aiMockJson = toolArgs;
+ } catch (e) {
+ // ElMessage.error('AI 生成的 JSON 解析错误');
+ }
+ } else {
+ // ElMessage.error('AI 调用了未知的工具');
+ }
+ loop.abort();
+ return toolCall;
+ });
-// loop.registerOnError(error => {
-// ElMessage.error(error + '');
-// });
+ loop.registerOnError(error => {
+ ElMessage.error(error + '');
+ });
-// await loop.start(chatStorage, usePrompt);
+ await loop.start(chatStorage, usePrompt);
-// if (aiMockJson && typeof aiMockJson === 'object') {
-// Object.keys(aiMockJson).forEach(key => {
-// tabStorage.formData[key] = aiMockJson[key];
-// });
-// formRef.value?.clearValidate?.();
-// }
-// } finally {
-// aiMockLoading.value = false;
-// }
-// };
\ No newline at end of file
+ } finally {
+ dataView.loading = false;
+ }
+};
\ No newline at end of file
diff --git a/renderer/src/components/main-panel/tool/diagram.vue b/renderer/src/components/main-panel/tool/diagram.vue
index dbc7554..bf83e46 100644
--- a/renderer/src/components/main-panel/tool/diagram.vue
+++ b/renderer/src/components/main-panel/tool/diagram.vue
@@ -390,6 +390,7 @@ function resetConnections() {
const context = inject('context') as any;
context.reset = resetConnections;
+context.state = state;
onMounted(() => {
nextTick(drawDiagram);