support tomology detection

This commit is contained in:
锦恢 2025-07-02 23:02:09 +08:00
parent 4a210eaa82
commit 92c8cf90ed
3 changed files with 145 additions and 69 deletions

View File

@ -25,31 +25,53 @@
<script setup lang="ts"> <script setup lang="ts">
import { nextTick, provide, ref } from 'vue'; import { nextTick, provide, ref } from 'vue';
import Diagram from './diagram.vue'; import Diagram from './diagram.vue';
import { topoSortParallel, type DiagramState } from './diagram';
import { ElMessage } from 'element-plus';
const showDiagram = ref(true); const showDiagram = ref(true);
const caption = ref(''); const caption = ref('');
const showCaption = ref(false); const showCaption = ref(false);
const context = { function setCaption(text: string) {
reset: () => {}, caption.value = text;
setCaption: (text: string) => { if (caption.value) {
caption.value = text; nextTick(() => {
if (caption.value) { showCaption.value = true;
nextTick(() => { });
showCaption.value = true; } else {
}); nextTick(() => {
} else { showCaption.value = false;
nextTick(() => { });
showCaption.value = false;
});
}
} }
}
interface DiagramContext {
reset: () => void,
state?: DiagramState,
setCaption: (value: string) => void
}
const context: DiagramContext = {
reset: () => {},
state: undefined,
setCaption
}; };
provide('context', context); provide('context', context);
function startTest() { async function startTest() {
const state = context.state;
if (state) {
const dispatches = topoSortParallel(state);
// for (const layer of dispatches) {
// await Promise.all(
// layer.map(nodeId => state.nodes[nodeId].run())
// );
// }
} else {
ElMessage.error('error');
}
} }
</script> </script>

View File

@ -1,4 +1,8 @@
import type { ElkNode } from 'elkjs/lib/elk-api'; 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 { export interface Edge {
id: string; 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<string, number> = {};
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) { // 初始化队列收集所有入度为0的节点
// if (!currentTool.value?.inputSchema) return; const result: string[][] = [];
// aiMockLoading.value = true; let queue: string[] = Object.keys(inDegree).filter(id => inDegree[id] === 0);
// 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;
// loop.setMaxEpochs(1); const visited = new Set<string>();
// 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<any>, 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) { if (toolCall.function?.name === dataView.too?.name) {
// try { try {
// const toolArgs = JSON.parse(toolCall.function?.arguments || '{}'); const toolArgs = JSON.parse(toolCall.function?.arguments || '{}');
// aiMockJson = toolArgs; aiMockJson = toolArgs;
// } catch (e) { } catch (e) {
// ElMessage.error('AI 生成的 JSON 解析错误'); // ElMessage.error('AI 生成的 JSON 解析错误');
// } }
// } else { } else {
// ElMessage.error('AI 调用了未知的工具'); // ElMessage.error('AI 调用了未知的工具');
// } }
// loop.abort(); loop.abort();
// return toolCall; return toolCall;
// }); });
// loop.registerOnError(error => { loop.registerOnError(error => {
// ElMessage.error(error + ''); ElMessage.error(error + '');
// }); });
// await loop.start(chatStorage, usePrompt); await loop.start(chatStorage, usePrompt);
// if (aiMockJson && typeof aiMockJson === 'object') { } finally {
// Object.keys(aiMockJson).forEach(key => { dataView.loading = false;
// tabStorage.formData[key] = aiMockJson[key]; }
// }); };
// formRef.value?.clearValidate?.();
// }
// } finally {
// aiMockLoading.value = false;
// }
// };

View File

@ -390,6 +390,7 @@ function resetConnections() {
const context = inject('context') as any; const context = inject('context') as any;
context.reset = resetConnections; context.reset = resetConnections;
context.state = state;
onMounted(() => { onMounted(() => {
nextTick(drawDiagram); nextTick(drawDiagram);