85 lines
5.6 KiB
TypeScript
85 lines
5.6 KiB
TypeScript
type Node = {
|
|
id: string
|
|
type: 'start' | 'end' | 'normal'
|
|
}
|
|
|
|
type Edge = {
|
|
source: string
|
|
target: string
|
|
}
|
|
|
|
// language=JSON
|
|
let data = JSON.parse('{\n "nodes": [\n {\n "id": "A",\n "type": "start",\n "position": {\n "x": 8,\n "y": 272\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 75\n },\n "selected": false,\n "dragging": false\n },\n {\n "id": "F",\n "type": "end",\n "position": {\n "x": 1439.5556937134281,\n "y": 282.2797340760818\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 75\n },\n "selected": false,\n "dragging": false\n },\n {\n "id": "C",\n "type": "normal",\n "position": {\n "x": 902.6781018665707,\n "y": 115.31234529524048\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 75\n },\n "selected": false,\n "dragging": false\n },\n {\n "id": "B",\n "type": "normal",\n "position": {\n "x": 338,\n "y": 287\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 75\n },\n "selected": false,\n "dragging": false\n },\n {\n "id": "E",\n "type": "normal",\n "position": {\n "x": 1086.6322978498904,\n "y": 371.3061114283591\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 75\n },\n "selected": true,\n "dragging": false\n },\n {\n "id": "D",\n "type": "normal",\n "position": {\n "x": 700.0944461714178,\n "y": 369.84258971430364\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 75\n },\n "selected": false,\n "dragging": false\n }\n ],\n "edges": [\n {\n "source": "A",\n "target": "B",\n "id": "xy-edge__A-B"\n },\n {\n "source": "B",\n "target": "C",\n "id": "xy-edge__B-C"\n },\n {\n "source": "C",\n "target": "F",\n "id": "xy-edge__C-F"\n },\n {\n "source": "D",\n "target": "E",\n "id": "xy-edge__D-E"\n },\n {\n "source": "B",\n "target": "D",\n "id": "xy-edge__B-D"\n },\n {\n "source": "E",\n "target": "F",\n "id": "xy-edge__E-F"\n }\n ],\n "data": {\n "A": {\n "inputs": {\n "name": {\n "type": "text"\n },\n "description": {\n "type": "text",\n "description": "文件描述"\n }\n }\n },\n "C": {\n "model": "qwen3",\n "outputs": {\n "text": {\n "type": "string"\n }\n },\n "systemPrompt": "你是个沙雕"\n },\n "B": {\n "count": 3,\n "score": 0.75,\n "knowledgeId": 3585368238960640,\n "query": "hello world"\n },\n "E": {\n "type": "python",\n "content": "code=\'hello\'\\nprint(code)"\n },\n "D": {\n "model": "qwen3",\n "outputs": {\n "text": {\n "type": "string"\n }\n },\n "systemPrompt": "你是个聪明人"\n }\n }\n}')
|
|
|
|
// THEN(A, B, WHEN(C, THEN(D, E)), F)
|
|
|
|
function buildEL(nodes: Node[], edges: Edge[]): string {
|
|
// Build adjacency list and in-degree map
|
|
const adjList = new Map<string, string[]>()
|
|
const inDegree = new Map<string, number>()
|
|
for (const node of nodes) {
|
|
adjList.set(node.id, [])
|
|
inDegree.set(node.id, 0)
|
|
}
|
|
for (const edge of edges) {
|
|
adjList.get(edge.source)!.push(edge.target)
|
|
inDegree.set(edge.target, inDegree.get(edge.target)! + 1)
|
|
}
|
|
|
|
// Compute levels (longest path from start)
|
|
const levelMap = new Map<string, number>()
|
|
|
|
function computeLevel(nodeId: string): number {
|
|
if (levelMap.has(nodeId)) return levelMap.get(nodeId)!
|
|
const preds = edges.filter(e => e.target === nodeId).map(e => e.source)
|
|
const level = preds.length === 0 ? 0 : Math.max(...preds.map(p => computeLevel(p))) + 1
|
|
levelMap.set(nodeId, level)
|
|
return level
|
|
}
|
|
|
|
for (const node of nodes) computeLevel(node.id)
|
|
|
|
// Group nodes by level
|
|
const maxLevel = Math.max(...Array.from(levelMap.values()))
|
|
const levels: string[][] = Array.from({length: maxLevel + 1}, () => [])
|
|
for (const node of nodes) levels[levelMap.get(node.id)!].push(node.id)
|
|
|
|
// Build EL expression
|
|
const expressions: string[] = []
|
|
for (let i = 0; i <= maxLevel; i++) {
|
|
const nodesAtLevel = levels[i]
|
|
if (nodesAtLevel.length === 0) continue
|
|
|
|
// Identify serial chains starting from this level
|
|
const serialChains: string[] = []
|
|
for (const nodeId of nodesAtLevel) {
|
|
let chain = [nodeId]
|
|
let current = nodeId
|
|
while (adjList.get(current)?.length === 1) {
|
|
const next = adjList.get(current)![0]
|
|
if (inDegree.get(next) === 1 && levelMap.get(next) === i + chain.length) {
|
|
chain.push(next)
|
|
current = next
|
|
} else break
|
|
}
|
|
if (chain.length > 1) {
|
|
serialChains.push(`THEN(${chain.join(', ')})`)
|
|
// Remove processed nodes from their levels
|
|
for (let j = 1; j < chain.length; j++) {
|
|
const level = levelMap.get(chain[j])!
|
|
levels[level] = levels[level].filter(n => n !== chain[j])
|
|
}
|
|
} else {
|
|
serialChains.push(nodeId)
|
|
}
|
|
}
|
|
|
|
// Combine chains or nodes at this level
|
|
expressions.push(serialChains.length > 1 ? `WHEN(${serialChains.join(', ')})` : serialChains[0])
|
|
}
|
|
|
|
return `THEN(${expressions.join(', ')})`
|
|
}
|
|
|
|
console.log(buildEL(data.nodes, data.edges))
|