diff --git a/dashboard/frontend/package-lock.json b/dashboard/frontend/package-lock.json index 32c5e08c..4518f1eb 100644 --- a/dashboard/frontend/package-lock.json +++ b/dashboard/frontend/package-lock.json @@ -10,7 +10,8 @@ "dependencies": { "react": "^18.3.1", "react-dom": "^18.3.1", - "react-router-dom": "^6.28.0" + "react-router-dom": "^6.28.0", + "reactflow": "^11.11.4" }, "devDependencies": { "@types/react": "^18.3.18", @@ -1047,6 +1048,108 @@ "node": ">= 8" } }, + "node_modules/@reactflow/background": { + "version": "11.3.14", + "resolved": "https://mirrors.tencent.com/npm/@reactflow/background/-/background-11.3.14.tgz", + "integrity": "sha512-Gewd7blEVT5Lh6jqrvOgd4G6Qk17eGKQfsDXgyRSqM+CTwDqRldG2LsWN4sNeno6sbqVIC2fZ+rAUBFA9ZEUDA==", + "license": "MIT", + "dependencies": { + "@reactflow/core": "11.11.4", + "classcat": "^5.0.3", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/controls": { + "version": "11.2.14", + "resolved": "https://mirrors.tencent.com/npm/@reactflow/controls/-/controls-11.2.14.tgz", + "integrity": "sha512-MiJp5VldFD7FrqaBNIrQ85dxChrG6ivuZ+dcFhPQUwOK3HfYgX2RHdBua+gx+40p5Vw5It3dVNp/my4Z3jF0dw==", + "license": "MIT", + "dependencies": { + "@reactflow/core": "11.11.4", + "classcat": "^5.0.3", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/core": { + "version": "11.11.4", + "resolved": "https://mirrors.tencent.com/npm/@reactflow/core/-/core-11.11.4.tgz", + "integrity": "sha512-H4vODklsjAq3AMq6Np4LE12i1I4Ta9PrDHuBR9GmL8uzTt2l2jh4CiQbEMpvMDcp7xi4be0hgXj+Ysodde/i7Q==", + "license": "MIT", + "dependencies": { + "@types/d3": "^7.4.0", + "@types/d3-drag": "^3.0.1", + "@types/d3-selection": "^3.0.3", + "@types/d3-zoom": "^3.0.1", + "classcat": "^5.0.3", + "d3-drag": "^3.0.0", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/minimap": { + "version": "11.7.14", + "resolved": "https://mirrors.tencent.com/npm/@reactflow/minimap/-/minimap-11.7.14.tgz", + "integrity": "sha512-mpwLKKrEAofgFJdkhwR5UQ1JYWlcAAL/ZU/bctBkuNTT1yqV+y0buoNVImsRehVYhJwffSWeSHaBR5/GJjlCSQ==", + "license": "MIT", + "dependencies": { + "@reactflow/core": "11.11.4", + "@types/d3-selection": "^3.0.3", + "@types/d3-zoom": "^3.0.1", + "classcat": "^5.0.3", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/node-resizer": { + "version": "2.2.14", + "resolved": "https://mirrors.tencent.com/npm/@reactflow/node-resizer/-/node-resizer-2.2.14.tgz", + "integrity": "sha512-fwqnks83jUlYr6OHcdFEedumWKChTHRGw/kbCxj0oqBd+ekfs+SIp4ddyNU0pdx96JIm5iNFS0oNrmEiJbbSaA==", + "license": "MIT", + "dependencies": { + "@reactflow/core": "11.11.4", + "classcat": "^5.0.4", + "d3-drag": "^3.0.0", + "d3-selection": "^3.0.0", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/node-toolbar": { + "version": "1.3.14", + "resolved": "https://mirrors.tencent.com/npm/@reactflow/node-toolbar/-/node-toolbar-1.3.14.tgz", + "integrity": "sha512-rbynXQnH/xFNu4P9H+hVqlEUafDCkEoCy0Dg9mG22Sg+rY/0ck6KkrAQrYrTgXusd+cEJOMK0uOOFCK2/5rSGQ==", + "license": "MIT", + "dependencies": { + "@reactflow/core": "11.11.4", + "classcat": "^5.0.3", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, "node_modules/@remix-run/router": { "version": "1.23.0", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz", @@ -1416,6 +1519,259 @@ "@babel/types": "^7.28.2" } }, + "node_modules/@types/d3": { + "version": "7.4.3", + "resolved": "https://mirrors.tencent.com/npm/@types/d3/-/d3-7.4.3.tgz", + "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", + "license": "MIT", + "dependencies": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://mirrors.tencent.com/npm/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", + "license": "MIT" + }, + "node_modules/@types/d3-axis": { + "version": "3.0.6", + "resolved": "https://mirrors.tencent.com/npm/@types/d3-axis/-/d3-axis-3.0.6.tgz", + "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-brush": { + "version": "3.0.6", + "resolved": "https://mirrors.tencent.com/npm/@types/d3-brush/-/d3-brush-3.0.6.tgz", + "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-chord": { + "version": "3.0.6", + "resolved": "https://mirrors.tencent.com/npm/@types/d3-chord/-/d3-chord-3.0.6.tgz", + "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://mirrors.tencent.com/npm/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-contour": { + "version": "3.0.6", + "resolved": "https://mirrors.tencent.com/npm/@types/d3-contour/-/d3-contour-3.0.6.tgz", + "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", + "license": "MIT", + "dependencies": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://mirrors.tencent.com/npm/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==", + "license": "MIT" + }, + "node_modules/@types/d3-dispatch": { + "version": "3.0.7", + "resolved": "https://mirrors.tencent.com/npm/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz", + "integrity": "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==", + "license": "MIT" + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://mirrors.tencent.com/npm/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-dsv": { + "version": "3.0.7", + "resolved": "https://mirrors.tencent.com/npm/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://mirrors.tencent.com/npm/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-fetch": { + "version": "3.0.7", + "resolved": "https://mirrors.tencent.com/npm/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", + "license": "MIT", + "dependencies": { + "@types/d3-dsv": "*" + } + }, + "node_modules/@types/d3-force": { + "version": "3.0.10", + "resolved": "https://mirrors.tencent.com/npm/@types/d3-force/-/d3-force-3.0.10.tgz", + "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==", + "license": "MIT" + }, + "node_modules/@types/d3-format": { + "version": "3.0.4", + "resolved": "https://mirrors.tencent.com/npm/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==", + "license": "MIT" + }, + "node_modules/@types/d3-geo": { + "version": "3.1.0", + "resolved": "https://mirrors.tencent.com/npm/@types/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-hierarchy": { + "version": "3.1.7", + "resolved": "https://mirrors.tencent.com/npm/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", + "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://mirrors.tencent.com/npm/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://mirrors.tencent.com/npm/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-polygon": { + "version": "3.0.2", + "resolved": "https://mirrors.tencent.com/npm/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", + "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==", + "license": "MIT" + }, + "node_modules/@types/d3-quadtree": { + "version": "3.0.6", + "resolved": "https://mirrors.tencent.com/npm/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==", + "license": "MIT" + }, + "node_modules/@types/d3-random": { + "version": "3.0.3", + "resolved": "https://mirrors.tencent.com/npm/@types/d3-random/-/d3-random-3.0.3.tgz", + "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://mirrors.tencent.com/npm/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://mirrors.tencent.com/npm/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==", + "license": "MIT" + }, + "node_modules/@types/d3-selection": { + "version": "3.0.11", + "resolved": "https://mirrors.tencent.com/npm/@types/d3-selection/-/d3-selection-3.0.11.tgz", + "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==", + "license": "MIT" + }, + "node_modules/@types/d3-shape": { + "version": "3.1.7", + "resolved": "https://mirrors.tencent.com/npm/@types/d3-shape/-/d3-shape-3.1.7.tgz", + "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://mirrors.tencent.com/npm/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-time-format": { + "version": "4.0.3", + "resolved": "https://mirrors.tencent.com/npm/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://mirrors.tencent.com/npm/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "node_modules/@types/d3-transition": { + "version": "3.0.9", + "resolved": "https://mirrors.tencent.com/npm/@types/d3-transition/-/d3-transition-3.0.9.tgz", + "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://mirrors.tencent.com/npm/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "license": "MIT", + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -1423,6 +1779,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://mirrors.tencent.com/npm/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "license": "MIT" + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -1434,14 +1796,14 @@ "version": "15.7.15", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/@types/react": { "version": "18.3.26", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.26.tgz", "integrity": "sha512-RFA/bURkcKzx/X9oumPG9Vp3D3JUgus/d0b67KB0t5S/raciymilkOa66olh78MUI92QLbEJevO7rvqU/kjwKA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -1899,6 +2261,12 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/classcat": { + "version": "5.0.5", + "resolved": "https://mirrors.tencent.com/npm/classcat/-/classcat-5.0.5.tgz", + "integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==", + "license": "MIT" + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1952,9 +2320,109 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true, + "devOptional": true, "license": "MIT" }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://mirrors.tencent.com/npm/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://mirrors.tencent.com/npm/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://mirrors.tencent.com/npm/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://mirrors.tencent.com/npm/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://mirrors.tencent.com/npm/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://mirrors.tencent.com/npm/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://mirrors.tencent.com/npm/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://mirrors.tencent.com/npm/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://mirrors.tencent.com/npm/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -3012,6 +3480,24 @@ "react-dom": ">=16.8" } }, + "node_modules/reactflow": { + "version": "11.11.4", + "resolved": "https://mirrors.tencent.com/npm/reactflow/-/reactflow-11.11.4.tgz", + "integrity": "sha512-70FOtJkUWH3BAOsN+LU9lCrKoKbtOPnz2uq0CV2PLdNSwxTXOhCbsZr50GmZ+Rtw3jx8Uv7/vBFtCGixLfd4Og==", + "license": "MIT", + "dependencies": { + "@reactflow/background": "11.3.14", + "@reactflow/controls": "11.2.14", + "@reactflow/core": "11.11.4", + "@reactflow/minimap": "11.7.14", + "@reactflow/node-resizer": "2.2.14", + "@reactflow/node-toolbar": "1.3.14" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -3274,6 +3760,15 @@ "punycode": "^2.1.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://mirrors.tencent.com/npm/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/vite": { "version": "5.4.20", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.20.tgz", @@ -3379,6 +3874,34 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zustand": { + "version": "4.5.7", + "resolved": "https://mirrors.tencent.com/npm/zustand/-/zustand-4.5.7.tgz", + "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.2.2" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } } } } diff --git a/dashboard/frontend/package.json b/dashboard/frontend/package.json index d40dc03b..030688c2 100644 --- a/dashboard/frontend/package.json +++ b/dashboard/frontend/package.json @@ -12,7 +12,8 @@ "dependencies": { "react": "^18.3.1", "react-dom": "^18.3.1", - "react-router-dom": "^6.28.0" + "react-router-dom": "^6.28.0", + "reactflow": "^11.11.4" }, "devDependencies": { "@types/react": "^18.3.18", @@ -26,4 +27,4 @@ "typescript": "^5.9.3", "vite": "^5.4.11" } -} \ No newline at end of file +} diff --git a/dashboard/frontend/src/App.tsx b/dashboard/frontend/src/App.tsx index 675f34ea..fb2a6c65 100644 --- a/dashboard/frontend/src/App.tsx +++ b/dashboard/frontend/src/App.tsx @@ -5,6 +5,7 @@ import LandingPage from './pages/LandingPage' import MonitoringPage from './pages/MonitoringPage' import ConfigPage from './pages/ConfigPage' import PlaygroundPage from './pages/PlaygroundPage' +import TopologyPage from './pages/TopologyPage' import { ConfigSection } from './components/ConfigNav' const App: React.FC = () => { @@ -105,6 +106,17 @@ const App: React.FC = () => { } /> + setConfigSection(section as ConfigSection)} + > + + + } + /> ) diff --git a/dashboard/frontend/src/components/ConfigNav.tsx b/dashboard/frontend/src/components/ConfigNav.tsx index 28099fa6..117294c4 100644 --- a/dashboard/frontend/src/components/ConfigNav.tsx +++ b/dashboard/frontend/src/components/ConfigNav.tsx @@ -6,6 +6,7 @@ export type ConfigSection = | 'prompt-guard' | 'similarity-cache' | 'intelligent-routing' + | 'topology' | 'tools-selection' | 'observability' | 'classification-api' @@ -41,6 +42,12 @@ const ConfigNav: React.FC = ({ activeSection, onSectionChange }) title: 'Intelligent Routing', description: 'Classify BERT, categories & reasoning' }, + { + id: 'topology' as ConfigSection, + icon: 'πŸ—ΊοΈ', + title: 'Topology', + description: 'Visualize routing chain-of-thought' + }, { id: 'tools-selection' as ConfigSection, icon: 'πŸ”§', diff --git a/dashboard/frontend/src/components/Layout.tsx b/dashboard/frontend/src/components/Layout.tsx index fb3b397f..1b548f35 100644 --- a/dashboard/frontend/src/components/Layout.tsx +++ b/dashboard/frontend/src/components/Layout.tsx @@ -56,16 +56,26 @@ const Layout: React.FC = ({ children, configSection, onConfigSectio { id: 'prompt-guard', icon: 'πŸ›‘οΈ', title: 'Prompt Guard' }, { id: 'similarity-cache', icon: '⚑', title: 'Similarity Cache' }, { id: 'intelligent-routing', icon: '🧠', title: 'Intelligent Routing' }, + { id: 'topology', icon: 'πŸ—ΊοΈ', title: 'Topology' }, { id: 'tools-selection', icon: 'πŸ”§', title: 'Tools Selection' }, { id: 'observability', icon: 'πŸ‘οΈ', title: 'Observability' }, { id: 'classification-api', icon: 'πŸ”Œ', title: 'Classification API' } ].map((section) => ( = ({ children, configSection, onConfigSectio + }> + model_config?: { + [key: string]: { + reasoning_family?: string + } + } +} + +const TopologyPage: React.FC = () => { + const [loading, setLoading] = useState(true) + const [error, setError] = useState(null) + const [nodes, setNodes, onNodesChange] = useNodesState([]) + const [edges, setEdges, onEdgesChange] = useEdgesState([]) + + useEffect(() => { + fetchConfig() + }, []) + + const fetchConfig = async () => { + try { + setLoading(true) + // Try the dashboard backend endpoint first + const response = await fetch('/api/router/config/all') + if (!response.ok) { + throw new Error(`Failed to fetch config: ${response.statusText}`) + } + const data = await response.json() + generateTopology(data) + setError(null) + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to load configuration') + console.error('Error fetching config:', err) + } finally { + setLoading(false) + } + } + + const generateTopology = (configData: ConfigData) => { + const newNodes: Node[] = [] + const newEdges: Edge[] = [] + + // Layout parameters + const nodeWidth = 220 // Fixed node width to prevent text overflow + const horizontalSpacing = 150 // Spacing between nodes (from right edge to left edge) + const verticalSpacing = 100 + + // Unified node style with fixed width + const nodeStyle = { + padding: '14px 20px', + borderRadius: '8px', + minHeight: '80px', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + textAlign: 'center' as const, + width: `${nodeWidth}px`, + boxSizing: 'border-box' as const, + } + + let currentX = 50 // Starting position from the left + const baseY = 300 // Unified Y coordinate to keep nodes on the same horizontal line + + // 1. User Query Node (starting point) + newNodes.push({ + id: 'user-query', + type: 'input', + data: { + label: ( +
+
πŸ‘€ User Query
+
+ ) + }, + position: { x: currentX, y: baseY }, + sourcePosition: Position.Right, + style: { + ...nodeStyle, + background: '#4CAF50', + color: 'white', + border: '2px solid #45a049', + fontWeight: 'bold', + }, + }) + + currentX += nodeWidth + horizontalSpacing + + // 2. Prompt Guard (Jailbreak Detection) + const promptGuardEnabled = configData.prompt_guard?.enabled ?? false + const promptGuardModel = 'vLLM-SR-Jailbreak' + newNodes.push({ + id: 'prompt-guard', + data: { + label: ( +
+
πŸ›‘οΈ Prompt Guard
+
+ {promptGuardEnabled ? `βœ“ ${promptGuardModel}` : 'βœ— Disabled'} +
+
+ ), + }, + position: { x: currentX, y: baseY }, + sourcePosition: Position.Right, + targetPosition: Position.Left, + style: { + ...nodeStyle, + background: promptGuardEnabled ? '#FF9800' : '#757575', + color: 'white', + border: `2px solid ${promptGuardEnabled ? '#F57C00' : '#616161'}`, + }, + }) + newEdges.push({ + id: 'e-query-guard', + source: 'user-query', + target: 'prompt-guard', + animated: true, + style: { stroke: promptGuardEnabled ? '#FF9800' : '#999', strokeWidth: 2 }, + markerEnd: { type: MarkerType.ArrowClosed, color: promptGuardEnabled ? '#FF9800' : '#999' }, + }) + + currentX += nodeWidth + horizontalSpacing + + // 3. PII Detection + const piiEnabled = configData.classifier?.pii_model?.model_id ? true : false + const piiModel = 'vLLM-SR-PII' + newNodes.push({ + id: 'pii-detection', + data: { + label: ( +
+
πŸ”’ PII Detection
+
+ {piiEnabled ? `βœ“ ${piiModel}` : 'βœ— Disabled'} +
+
+ ), + }, + position: { x: currentX, y: baseY }, + sourcePosition: Position.Right, + targetPosition: Position.Left, + style: { + ...nodeStyle, + background: piiEnabled ? '#9C27B0' : '#757575', + color: 'white', + border: `2px solid ${piiEnabled ? '#7B1FA2' : '#616161'}`, + }, + }) + newEdges.push({ + id: 'e-guard-pii', + source: 'prompt-guard', + target: 'pii-detection', + animated: true, + style: { stroke: piiEnabled ? '#9C27B0' : '#999', strokeWidth: 2 }, + markerEnd: { type: MarkerType.ArrowClosed, color: piiEnabled ? '#9C27B0' : '#999' }, + }) + + currentX += nodeWidth + horizontalSpacing + + // 4. Semantic Cache + const cacheEnabled = configData.semantic_cache?.enabled ?? false + const cacheType = configData.semantic_cache?.backend_type || 'memory' + const cacheThreshold = configData.semantic_cache?.similarity_threshold || 0.8 + const cacheBertModel = 'vLLM-SR-Similarity' + newNodes.push({ + id: 'semantic-cache', + data: { + label: ( +
+
⚑ Semantic Cache
+
+ {cacheEnabled ? `βœ“ ${cacheBertModel}` : 'βœ— Disabled'} +
+
+ {cacheEnabled ? `${cacheType} (${cacheThreshold})` : ''} +
+
+ ), + }, + position: { x: currentX, y: baseY }, + sourcePosition: Position.Right, + targetPosition: Position.Left, + style: { + ...nodeStyle, + background: cacheEnabled ? '#00BCD4' : '#757575', + color: 'white', + border: `2px solid ${cacheEnabled ? '#0097A7' : '#616161'}`, + }, + }) + newEdges.push({ + id: 'e-pii-cache', + source: 'pii-detection', + target: 'semantic-cache', + animated: true, + style: { stroke: cacheEnabled ? '#00BCD4' : '#999', strokeWidth: 2 }, + markerEnd: { type: MarkerType.ArrowClosed, color: cacheEnabled ? '#00BCD4' : '#999' }, + }) + + currentX += nodeWidth + horizontalSpacing + + // 5. Classification Hub + const classificationModel = 'vLLM-SR-Classify' + const classificationThreshold = configData.classifier?.category_model?.threshold || 0.6 + + newNodes.push({ + id: 'classification', + data: { + label: ( +
+
🧠 Classification
+
+ βœ“ {classificationModel} +
+
+ threshold: {classificationThreshold} +
+
+ ) + }, + position: { x: currentX, y: baseY }, + sourcePosition: Position.Right, + targetPosition: Position.Left, + style: { + ...nodeStyle, + minHeight: '90px', + background: '#673AB7', + color: 'white', + border: '2px solid #512DA8', + fontWeight: 'bold', + }, + }) + newEdges.push({ + id: 'e-cache-classification', + source: 'semantic-cache', + target: 'classification', + animated: true, + style: { stroke: '#673AB7', strokeWidth: 2 }, + markerEnd: { type: MarkerType.ArrowClosed, color: '#673AB7' }, + }) + + // 7. Categories and Models - vertical layout + const categories = configData.categories || [] + currentX += nodeWidth + horizontalSpacing + const categoryX = currentX + const modelX = categoryX + nodeWidth + horizontalSpacing + + // Calculate total height for center alignment + const totalCategoriesHeight = categories.length * verticalSpacing + let categoryY = baseY - (totalCategoriesHeight / 2) + 50 + + categories.forEach((category) => { + const categoryId = `category-${category.name}` + const hasSystemPrompt = category.system_prompt && category.system_prompt.length > 0 + + newNodes.push({ + id: categoryId, + data: { + label: ( +
+
πŸ“ {category.name}
+
+ {hasSystemPrompt ? 'βœ“ System Prompt' : 'βœ— No Prompt'} +
+
+ ) + }, + position: { x: categoryX, y: categoryY }, + sourcePosition: Position.Right, + targetPosition: Position.Left, + style: { + background: '#3F51B5', + color: 'white', + border: '2px solid #303F9F', + fontSize: '12px', + padding: '10px 16px', + borderRadius: '6px', + minWidth: '140px', + minHeight: '60px', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }, + }) + + newEdges.push({ + id: `e-classification-${category.name}`, + source: 'classification', + target: categoryId, + style: { stroke: '#3F51B5', strokeWidth: 1.5 }, + markerEnd: { type: MarkerType.ArrowClosed, color: '#3F51B5' }, + }) + + // 7. Models for each category + const modelScores = category.model_scores || [] + modelScores.forEach((modelScore, modelIndex) => { + const modelId = `model-${category.name}-${modelScore.model.replace(/[^a-zA-Z0-9]/g, '-')}` + const modelYPos = categoryY + (modelIndex * 50) - ((modelScores.length - 1) * 25) + + const reasoningFamily = configData.model_config?.[modelScore.model]?.reasoning_family + const hasReasoning = modelScore.use_reasoning && reasoningFamily + const modelName = modelScore.model.split('/').pop() || modelScore.model + + newNodes.push({ + id: modelId, + type: 'output', + data: { + label: ( +
+
+ πŸ€– {modelName} +
+
+ Score: {modelScore.score.toFixed(2)} +
+
+ ), + }, + position: { x: modelX, y: modelYPos }, + targetPosition: Position.Left, + style: { + background: '#607D8B', + color: 'white', + border: '2px solid #455A64', + fontSize: '11px', + padding: '8px 12px', + borderRadius: '6px', + minWidth: '140px', + }, + }) + + // Use different line styles to indicate reasoning enabled + newEdges.push({ + id: `e-${categoryId}-${modelId}`, + source: categoryId, + target: modelId, + animated: !!hasReasoning, + style: { + stroke: hasReasoning ? '#E91E63' : '#607D8B', + strokeWidth: hasReasoning ? 3 : 2, + strokeDasharray: hasReasoning ? '0' : '5, 5', + }, + markerEnd: { + type: MarkerType.ArrowClosed, + color: hasReasoning ? '#E91E63' : '#607D8B', + width: hasReasoning ? 25 : 20, + height: hasReasoning ? 25 : 20, + }, + label: `${(modelScore.score * 100).toFixed(0)}%${hasReasoning ? ' 🧠' : ''}`, + labelStyle: { + fontSize: '11px', + fill: hasReasoning ? '#E91E63' : '#666', + fontWeight: hasReasoning ? 'bold' : 'normal', + }, + labelBgStyle: { fill: 'white', fillOpacity: 0.9 }, + }) + }) + + categoryY += verticalSpacing + }) + + setNodes(newNodes) + setEdges(newEdges) + } + + if (loading) { + return ( +
+
+
+

Loading topology...

+
+
+ ) + } + + if (error) { + return ( +
+
+ ⚠️ +

{error}

+ +
+
+ ) + } + + return ( +
+
+

πŸ—ΊοΈ Semantic Router Topology

+

+ Visualize the chain-of-thought flow from user query to model selection +

+ +
+
+ + + + { + const style = node.style as any + return style?.background || '#ccc' + }} + maskColor="rgba(0, 0, 0, 0.1)" + /> + +
+
+

Legend

+
+
+ + User Input +
+
+ + Security +
+
+ + Cache +
+
+ + Classification +
+
+ + Category +
+
+ + Model +
+
+
+ Reasoning (solid) +
+
+
+ Standard (dashed) +
+
+
+
+ ) +} + +export default TopologyPage + diff --git a/website/src/components/AcknowledgementsSection/data.json b/website/src/components/AcknowledgementsSection/data.json index 568cb9f6..c320ac02 100644 --- a/website/src/components/AcknowledgementsSection/data.json +++ b/website/src/components/AcknowledgementsSection/data.json @@ -6,7 +6,7 @@ "id": "vLLM", "name": "vLLM", "logo": "/img/acknowledgements/vllm-logo.png", - "url": "https://github.com/vllm-project/vllm", + "url": "https://github.com/vllm-project/semantic-router", "description": "A high-throughput and memory-efficient inference and serving engine for LLMs" },{ "id": "huggingFace-candle",