feat(web): 增加EL表达式转换
This commit is contained in:
84
service-web/client/test.ts
Normal file
84
service-web/client/test.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
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))
|
||||
Reference in New Issue
Block a user