feat: 添加一个无关痛痒的浏览器llm的支持,添加自动导入,

This commit is contained in:
539943419 committed 2025-03-12 15:03:19 +08:00
1 parent 81360c377c
commit 34d7c7df30
8 files changed
+870 -2

No files matched your search

+6
View File
@@ -30,3 +30,9 @@ test
stats.html
src-server/web/*
auto-imports.d.ts
components.d.ts
src/plugins/onnx_demo
+4
View File
@@ -24,6 +24,8 @@
"@codemirror/lang-vue": "^0.1.3",
"@codemirror/lang-xml": "^6.1.0",
"@codemirror/lang-yaml": "^6.1.1",
"@mediapipe/tasks-genai": "^0.10.22",
"@mlc-ai/web-llm": "^0.2.78",
"@types/sortablejs": "^1.15.8",
"@vicons/ionicons5": "^0.12.0",
"aes-js": "^3.1.2",
@@ -56,6 +58,8 @@
"prettier-plugin-vue": "^1.1.6",
"rollup-plugin-visualizer": "^5.12.0",
"typescript": "^5.5.3",
"unplugin-auto-import": "^19.1.1",
"unplugin-vue-components": "^28.4.1",
"vite": "^5.4.0",
"vite-plugin-singlefile": "^2.0.2",
"vue-tsc": "^2.0.29"
+281
View File
@@ -41,6 +41,12 @@ importers:
'@codemirror/lang-yaml':
specifier: ^6.1.1
version: 6.1.1(@codemirror/view@6.33.0)
'@mediapipe/tasks-genai':
specifier: ^0.10.22
version: 0.10.22
'@mlc-ai/web-llm':
specifier: ^0.2.78
version: 0.2.78
'@types/sortablejs':
specifier: ^1.15.8
version: 1.15.8
@@ -132,6 +138,12 @@ importers:
typescript:
specifier: ^5.5.3
version: 5.5.4
unplugin-auto-import:
specifier: ^19.1.1
version: 19.1.1
unplugin-vue-components:
specifier: ^28.4.1
version: 28.4.1(@babel/parser@7.25.3)(vue@3.5.0(typescript@5.5.4))
vite:
specifier: ^5.4.0
version: 5.4.0(@types/node@22.1.0)(sass@1.77.8)
@@ -427,6 +439,12 @@ packages:
resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==}
hasBin: true
'@mediapipe/tasks-genai@0.10.22':
resolution: {integrity: sha512-nAFOptwdeg0h3D2jLm4JDR+rTHu9l2Nd7em7HId/2hyUU77jvNFeqwTXC4CNm9oW9+jBUG/6l8wJdhg+T8SrTQ==}
'@mlc-ai/web-llm@0.2.78':
resolution: {integrity: sha512-ptqDNzHnfDyNZj7vjp9IaY5U/QDweXMe5wNzErOmRT1gqj8AaMvcqbj7HroPDzhXJGM7BZpDjANV5MhXhKOosA==}
'@rollup/rollup-android-arm-eabi@4.20.0':
resolution: {integrity: sha512-TSpWzflCc4VGAUJZlPpgAJE1+V60MePDQnBd7PPkpuEmOy8i87aL6tinFGKBFKuEDikYpig72QzdT3QPYIi+oA==}
cpu: [arm]
@@ -621,6 +639,11 @@ packages:
abbrev@1.1.1:
resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
acorn@8.14.1:
resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==}
engines: {node: '>=0.4.0'}
hasBin: true
aes-js@3.1.2:
resolution: {integrity: sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ==}
@@ -723,6 +746,12 @@ packages:
concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
confbox@0.1.8:
resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==}
confbox@0.2.1:
resolution: {integrity: sha512-hkT3yDPFbs95mNCy1+7qNKC6Pro+/ibzYxtM2iqEigpf0sVw+bg4Zh9/snjsBcf990vfIsg5+1U7VyiyBb3etg==}
console-control-strings@1.1.0:
resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==}
@@ -772,6 +801,15 @@ packages:
supports-color:
optional: true
debug@4.4.0:
resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
decompress-response@4.2.1:
resolution: {integrity: sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==}
engines: {node: '>=8'}
@@ -831,6 +869,10 @@ packages:
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
engines: {node: '>=6'}
escape-string-regexp@5.0.0:
resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
engines: {node: '>=12'}
esniff@2.0.1:
resolution: {integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==}
engines: {node: '>=0.10'}
@@ -838,6 +880,9 @@ packages:
estree-walker@2.0.2:
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
estree-walker@3.0.3:
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
event-emitter@0.3.5:
resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==}
@@ -847,9 +892,20 @@ packages:
evtd@0.2.4:
resolution: {integrity: sha512-qaeGN5bx63s/AXgQo8gj6fBkxge+OoLddLniox5qtLAEY5HSnuSlISXVPxnSae1dWblvTh4/HoMIB+mbMsvZzw==}
exsolve@1.0.4:
resolution: {integrity: sha512-xsZH6PXaER4XoV+NiT7JHp1bJodJVT+cxeSH1G0f0tlT0lJqYuHUP3bUx2HtfTDvOagMINYp8rsqusxud3RXhw==}
ext@1.7.0:
resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==}
fdir@6.4.3:
resolution: {integrity: sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==}
peerDependencies:
picomatch: ^3 || ^4
peerDependenciesMeta:
picomatch:
optional: true
fill-range@7.1.1:
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
engines: {node: '>=8'}
@@ -953,12 +1009,19 @@ packages:
resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==}
engines: {node: '>=8'}
js-tokens@9.0.1:
resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==}
lie@3.1.1:
resolution: {integrity: sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==}
linkify-it@5.0.0:
resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==}
local-pkg@1.1.1:
resolution: {integrity: sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==}
engines: {node: '>=14'}
localforage@1.10.0:
resolution: {integrity: sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==}
@@ -968,9 +1031,16 @@ packages:
lodash@4.17.21:
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
loglevel@1.9.2:
resolution: {integrity: sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==}
engines: {node: '>= 0.6.0'}
magic-string@0.30.11:
resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==}
magic-string@0.30.17:
resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
make-dir@3.1.0:
resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==}
engines: {node: '>=8'}
@@ -1022,9 +1092,15 @@ packages:
engines: {node: '>=10'}
hasBin: true
mlly@1.7.4:
resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==}
ms@2.1.2:
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
muggle-string@0.4.1:
resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
@@ -1088,6 +1164,9 @@ packages:
resolution: {integrity: sha512-Fl2z/BHvkTNvkuBzYTpTuirHZg6wW9z8+4SND/3mDTEcYbbNKWAy21dz9D3ePNNwrrK8pqZO5vLPZ1hLF6T7XA==}
engines: {node: '>=6'}
pathe@2.0.3:
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
pdfjs-dist@4.5.136:
resolution: {integrity: sha512-V1BALcAN/FmxBEShLxoP73PlQZAZtzlaNfRbRhJrKvXzjLC5VaIlBAQUJuWP8iaYUmIdmdLHmt3E2TBglxOm3w==}
engines: {node: '>=18'}
@@ -1099,6 +1178,16 @@ packages:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
picomatch@4.0.2:
resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
engines: {node: '>=12'}
pkg-types@1.3.1:
resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}
pkg-types@2.1.0:
resolution: {integrity: sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A==}
postcss@8.4.41:
resolution: {integrity: sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==}
engines: {node: ^10 || ^12 || >=14}
@@ -1130,6 +1219,9 @@ packages:
resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==}
engines: {node: '>=6'}
quansync@0.2.8:
resolution: {integrity: sha512-4+saucphJMazjt7iOM27mbFCk+D9dd/zmgMDCzRZ8MEoBfYp7lAvoN38et/phRQF6wOPMy/OROBGgoWeSKyluA==}
readable-stream@3.6.2:
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
engines: {node: '>= 6'}
@@ -1173,6 +1265,9 @@ packages:
engines: {node: '>=14.0.0'}
hasBin: true
scule@1.3.0:
resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==}
seemly@0.3.8:
resolution: {integrity: sha512-MW8Qs6vbzo0pHmDpFSYPna+lwpZ6Zk1ancbajw/7E8TKtHdV+1DfZZD+kKJEhG/cAoB/i+LiT+5msZOqj0DwRA==}
@@ -1222,6 +1317,9 @@ packages:
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
engines: {node: '>=8'}
strip-literal@3.0.0:
resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==}
style-mod@4.1.2:
resolution: {integrity: sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==}
@@ -1229,6 +1327,10 @@ packages:
resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==}
engines: {node: '>=10'}
tinyglobby@0.2.12:
resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==}
engines: {node: '>=12.0.0'}
to-fast-properties@2.0.0:
resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
engines: {node: '>=4'}
@@ -1257,9 +1359,49 @@ packages:
uc.micro@2.1.0:
resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==}
ufo@1.5.4:
resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==}
undici-types@6.13.0:
resolution: {integrity: sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==}
unimport@4.1.2:
resolution: {integrity: sha512-oVUL7PSlyVV3QRhsdcyYEMaDX8HJyS/CnUonEJTYA3//bWO+o/4gG8F7auGWWWkrrxBQBYOO8DKe+C53ktpRXw==}
engines: {node: '>=18.12.0'}
unplugin-auto-import@19.1.1:
resolution: {integrity: sha512-sCGZZrSR1Bc8RfN8Q0RUDxXtC20rdAt7UB4lDyq8MNtKVHiXXh+5af6Nz4JRp9Q+7HjnbgQfQox0TkEymjdUAQ==}
engines: {node: '>=14'}
peerDependencies:
'@nuxt/kit': ^3.2.2
'@vueuse/core': '*'
peerDependenciesMeta:
'@nuxt/kit':
optional: true
'@vueuse/core':
optional: true
unplugin-utils@0.2.4:
resolution: {integrity: sha512-8U/MtpkPkkk3Atewj1+RcKIjb5WBimZ/WSLhhR3w6SsIj8XJuKTacSP8g+2JhfSGw0Cb125Y+2zA/IzJZDVbhA==}
engines: {node: '>=18.12.0'}
unplugin-vue-components@28.4.1:
resolution: {integrity: sha512-niGSc0vJD9ueAnsqcfAldmtpkppZ09B6p2G1dL7X5S8KPdgbk1P+txPwaaDCe7N+eZh2VG1aAypLXkuJs3OSUg==}
engines: {node: '>=14'}
peerDependencies:
'@babel/parser': ^7.15.8
'@nuxt/kit': ^3.2.2
vue: 2 || 3
peerDependenciesMeta:
'@babel/parser':
optional: true
'@nuxt/kit':
optional: true
unplugin@2.2.0:
resolution: {integrity: sha512-m1ekpSwuOT5hxkJeZGRxO7gXbXT3gF26NjQ7GdVHoLoF8/nopLcd/QfPigpCy7i51oFHiRJg/CyHhj4vs2+KGw==}
engines: {node: '>=18.12.0'}
util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
@@ -1354,6 +1496,9 @@ packages:
webidl-conversions@3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
webpack-virtual-modules@0.6.2:
resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==}
whatwg-url@5.0.0:
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
@@ -1740,6 +1885,12 @@ snapshots:
- supports-color
optional: true
'@mediapipe/tasks-genai@0.10.22': {}
'@mlc-ai/web-llm@0.2.78':
dependencies:
loglevel: 1.9.2
'@rollup/rollup-android-arm-eabi@4.20.0':
optional: true
@@ -1931,6 +2082,8 @@ snapshots:
abbrev@1.1.1:
optional: true
acorn@8.14.1: {}
aes-js@3.1.2: {}
agent-base@6.0.2:
@@ -2061,6 +2214,10 @@ snapshots:
concat-map@0.0.1:
optional: true
confbox@0.1.8: {}
confbox@0.2.1: {}
console-control-strings@1.1.0:
optional: true
@@ -2105,6 +2262,10 @@ snapshots:
ms: 2.1.2
optional: true
debug@4.4.0:
dependencies:
ms: 2.1.3
decompress-response@4.2.1:
dependencies:
mimic-response: 2.1.0
@@ -2178,6 +2339,8 @@ snapshots:
escalade@3.2.0: {}
escape-string-regexp@5.0.0: {}
esniff@2.0.1:
dependencies:
d: 1.0.2
@@ -2187,6 +2350,10 @@ snapshots:
estree-walker@2.0.2: {}
estree-walker@3.0.3:
dependencies:
'@types/estree': 1.0.5
event-emitter@0.3.5:
dependencies:
d: 1.0.2
@@ -2196,10 +2363,16 @@ snapshots:
evtd@0.2.4: {}
exsolve@1.0.4: {}
ext@1.7.0:
dependencies:
type: 2.7.3
fdir@6.4.3(picomatch@4.0.2):
optionalDependencies:
picomatch: 4.0.2
fill-range@7.1.1:
dependencies:
to-regex-range: 5.0.1
@@ -2300,6 +2473,8 @@ snapshots:
dependencies:
is-docker: 2.2.1
js-tokens@9.0.1: {}
lie@3.1.1:
dependencies:
immediate: 3.0.6
@@ -2308,6 +2483,12 @@ snapshots:
dependencies:
uc.micro: 2.1.0
local-pkg@1.1.1:
dependencies:
mlly: 1.7.4
pkg-types: 2.1.0
quansync: 0.2.8
localforage@1.10.0:
dependencies:
lie: 3.1.1
@@ -2316,10 +2497,16 @@ snapshots:
lodash@4.17.21: {}
loglevel@1.9.2: {}
magic-string@0.30.11:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.0
magic-string@0.30.17:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.0
make-dir@3.1.0:
dependencies:
semver: 6.3.1
@@ -2376,9 +2563,18 @@ snapshots:
mkdirp@1.0.4:
optional: true
mlly@1.7.4:
dependencies:
acorn: 8.14.1
pathe: 2.0.3
pkg-types: 1.3.1
ufo: 1.5.4
ms@2.1.2:
optional: true
ms@2.1.3: {}
muggle-string@0.4.1: {}
naive-ui@2.39.0(vue@3.5.0(typescript@5.5.4)):
@@ -2453,6 +2649,8 @@ snapshots:
path2d@0.2.1:
optional: true
pathe@2.0.3: {}
pdfjs-dist@4.5.136:
optionalDependencies:
canvas: 2.11.2
@@ -2465,6 +2663,20 @@ snapshots:
picomatch@2.3.1: {}
picomatch@4.0.2: {}
pkg-types@1.3.1:
dependencies:
confbox: 0.1.8
mlly: 1.7.4
pathe: 2.0.3
pkg-types@2.1.0:
dependencies:
confbox: 0.2.1
exsolve: 1.0.4
pathe: 2.0.3
postcss@8.4.41:
dependencies:
nanoid: 3.3.7
@@ -2491,6 +2703,8 @@ snapshots:
punycode.js@2.3.1: {}
quansync@0.2.8: {}
readable-stream@3.6.2:
dependencies:
inherits: 2.0.4
@@ -2551,6 +2765,8 @@ snapshots:
immutable: 4.3.7
source-map-js: 1.2.0
scule@1.3.0: {}
seemly@0.3.8: {}
semver@6.3.1:
@@ -2597,6 +2813,10 @@ snapshots:
dependencies:
ansi-regex: 5.0.1
strip-literal@3.0.0:
dependencies:
js-tokens: 9.0.1
style-mod@4.1.2: {}
tar@6.2.1:
@@ -2609,6 +2829,11 @@ snapshots:
yallist: 4.0.0
optional: true
tinyglobby@0.2.12:
dependencies:
fdir: 6.4.3(picomatch@4.0.2)
picomatch: 4.0.2
to-fast-properties@2.0.0: {}
to-regex-range@5.0.1:
@@ -2628,8 +2853,62 @@ snapshots:
uc.micro@2.1.0: {}
ufo@1.5.4: {}
undici-types@6.13.0: {}
unimport@4.1.2:
dependencies:
acorn: 8.14.1
escape-string-regexp: 5.0.0
estree-walker: 3.0.3
local-pkg: 1.1.1
magic-string: 0.30.17
mlly: 1.7.4
pathe: 2.0.3
picomatch: 4.0.2
pkg-types: 1.3.1
scule: 1.3.0
strip-literal: 3.0.0
tinyglobby: 0.2.12
unplugin: 2.2.0
unplugin-utils: 0.2.4
unplugin-auto-import@19.1.1:
dependencies:
local-pkg: 1.1.1
magic-string: 0.30.17
picomatch: 4.0.2
unimport: 4.1.2
unplugin: 2.2.0
unplugin-utils: 0.2.4
unplugin-utils@0.2.4:
dependencies:
pathe: 2.0.3
picomatch: 4.0.2
unplugin-vue-components@28.4.1(@babel/parser@7.25.3)(vue@3.5.0(typescript@5.5.4)):
dependencies:
chokidar: 3.6.0
debug: 4.4.0
local-pkg: 1.1.1
magic-string: 0.30.17
mlly: 1.7.4
tinyglobby: 0.2.12
unplugin: 2.2.0
unplugin-utils: 0.2.4
vue: 3.5.0(typescript@5.5.4)
optionalDependencies:
'@babel/parser': 7.25.3
transitivePeerDependencies:
- supports-color
unplugin@2.2.0:
dependencies:
acorn: 8.14.1
webpack-virtual-modules: 0.6.2
util-deprecate@1.0.2:
optional: true
@@ -2707,6 +2986,8 @@ snapshots:
webidl-conversions@3.0.1:
optional: true
webpack-virtual-modules@0.6.2: {}
whatwg-url@5.0.0:
dependencies:
tr46: 0.0.3
+4
View File
@@ -111,6 +111,10 @@ class VirtualDirectory {
return `${this.parent.path}${this.name}/`
}
get url(): string {
return `${config.api.get}${this.path}/`
}
/**
* 获取从根目录开始的按顺序的文件夹
* @returns {VirtualDirectory[]}
+516
View File
@@ -0,0 +1,516 @@
<template>
<div class="chat-container">
<div v-if="loading" class="loading-container">
<div class="loading-spinner"></div>
<div class="progress-container">
<div class="progress-bar" :style="{ width: loadingProgress + '%' }"></div>
</div>
<p class="loading-percentage">{{ loadingProgress }}%</p>
<p>{{ loadingState }}</p>
</div>
<div v-else class="chat-main">
<div class="chat-history" ref="chatHistoryRef">
<div v-for="(message, index) in chatHistory" :key="index"
:class="['message', message.role === 'user' ? 'user-message' : 'ai-message']">
<div class="message-content" v-if="message.role === 'user'">{{ message.content }}</div>
<div class="message-content markdown-content" v-else v-html="renderMarkdown(message.content)"></div>
</div>
<!-- 实时生成的内容 -->
<div v-if="generatingMessage" class="message ai-message">
<div class="message-content markdown-content" v-html="renderMarkdown(currentGeneratedText)"></div>
<div class="generating-indicator"><span>.</span><span>.</span><span>.</span></div>
</div>
</div>
<div class="chat-input">
<textarea v-model="userInput" @keydown.enter.prevent="sendMessage" placeholder="输入消息..."
:disabled="processing"></textarea>
<button @click="sendMessage" :disabled="processing || !userInput.trim()">
发送
</button>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import VirtualDirectory, { VirtualFile } from '@/class/VirtualDirectory';
import { ref, watch, nextTick, onUnmounted } from 'vue';
import MarkdownIt from 'markdown-it';
import { FilesetResolver, LlmInference } from '@mediapipe/tasks-genai';
import VirtualPage from '@/class/VirtualPage';
const currentFiles = defineModel<VirtualFile[]>('currentFiles', {
required: true
});
const currentDirectories = defineModel<VirtualDirectory[]>(
'currentDirectories',
{
required: true
}
)
const props = defineProps<{
view: VirtualPage
}>()
// 初始化MarkdownIt
const md = new MarkdownIt({
html: true,
linkify: true,
typographer: true,
breaks: true
});
// 状态管理
const loading = ref(true);
const loadingState = ref('正在初始化模型...');
const loadingProgress = ref(0);
const processing = ref(false);
const userInput = ref('');
const chatHistory = ref<{ role: string, content: string }[]>([]);
const chatHistoryRef = ref<HTMLElement | null>(null);
const llmInstance = ref<any>(null);
// 实时生成内容管理
const generatingMessage = ref(false);
const currentGeneratedText = ref('');
// 更新加载进度
const updateLoadingProgress = (progress: number, state: string) => {
loadingProgress.value = progress;
loadingState.value = state;
};
// 渲染Markdown为HTML
const renderMarkdown = (text: string) => {
return md.render(text);
};
// 初始化模型
const initializeModel = async () => {
if (!currentFiles.value || currentFiles.value.length === 0) {
loadingState.value = '错误:未找到模型文件';
return;
}
try {
const modelFile = currentFiles.value[0];
const modelName = modelFile.name.split('.model.bin')[0];
// 第一阶段:开始加载
updateLoadingProgress(5, '正在加载MediaPipe LLM引擎...');
// 模拟下载进度
const simulateDownloadProgress = () => {
const totalTime = 2000; // 2秒
const interval = 100; // 每100毫秒更新一次
const steps = totalTime / interval;
const incrementPerStep = 15 / steps; // 从5%到20%
let currentStep = 0;
const timer = setInterval(() => {
if (currentStep >= steps) {
clearInterval(timer);
return;
}
loadingProgress.value = Math.min(20, 5 + currentStep * incrementPerStep);
currentStep++;
}, interval);
};
simulateDownloadProgress();
// 初始化FilesetResolver
updateLoadingProgress(20, '正在初始化MediaPipe文件解析器...');
const genai = await FilesetResolver.forGenAiTasks(
currentDirectories.value[0].url ?? "https://cdn.jsdelivr.net/npm/@mediapipe/tasks-genai@latest/wasm"
);
// 第二阶段:文件解析器加载完成
updateLoadingProgress(40, '正在加载模型文件...');
// 模拟模型文件加载进度
const simulateModelLoadingProgress = () => {
const totalTime = 3000; // 3秒
const interval = 100; // 每100毫秒更新一次
const steps = totalTime / interval;
const incrementPerStep = 45 / steps; // 从40%到85%
let currentStep = 0;
const timer = setInterval(() => {
if (currentStep >= steps) {
clearInterval(timer);
return;
}
loadingProgress.value = Math.min(85, 40 + currentStep * incrementPerStep);
currentStep++;
}, interval);
};
simulateModelLoadingProgress();
// 创建LLM推理实例
llmInstance.value = await LlmInference.createFromOptions(genai, {
baseOptions: {
modelAssetPath: modelFile.url
},
maxTokens: 1024,
temperature: 0.7
});
// 第三阶段:模型加载完成
updateLoadingProgress(90, '初始化对话...');
// 短暂延迟以显示最终阶段
await new Promise(resolve => setTimeout(resolve, 500));
// 加载完成
updateLoadingProgress(100, '加载完成!');
// 短暂延迟以显示100%完成状态
await new Promise(resolve => setTimeout(resolve, 300));
loading.value = false;
chatHistory.value.push({
role: 'assistant',
content: `您好!我是${modelName},有什么可以帮助您的?`
});
} catch (error) {
console.error('模型加载失败:', error);
loadingState.value = `模型加载失败: ${error instanceof Error ? error.message : String(error)}`;
loadingProgress.value = 0;
}
};
// 发送消息
const sendMessage = async () => {
if (!userInput.value.trim() || processing.value) return;
const userMessage = userInput.value.trim();
userInput.value = '';
// 添加用户消息到历史
chatHistory.value.push({
role: 'user',
content: userMessage
});
processing.value = true;
generatingMessage.value = true;
currentGeneratedText.value = '';
try {
// 滚动到底部
await nextTick();
scrollToBottom();
// 构建提示词(包含历史对话)
// const history = chatHistory.value
// .map(msg => `${msg.role === 'user' ? '用户' : 'AI'}:${msg.content}`)
// .join('\n\n');
// const prompt = history + '\n\n用户:' + userMessage + '\n\nAI:';
// 使用MediaPipe生成回复
llmInstance.value.generateResponse(
userMessage,
null, // 不使用LoRA模型
(partialResult: string, done: boolean) => {
// 累加部分结果并更新UI
currentGeneratedText.value += partialResult;
// 滚动到底部以显示最新内容
nextTick().then(scrollToBottom);
if (done) {
// 添加完整回复到历史
chatHistory.value.push({
role: 'assistant',
content: currentGeneratedText.value
});
// 重置生成状态
generatingMessage.value = false;
processing.value = false;
currentGeneratedText.value = '';
nextTick().then(scrollToBottom);
}
}
);
} catch (error) {
console.error('生成回复出错:', error);
// 添加错误信息
chatHistory.value.push({
role: 'assistant',
content: `抱歉,生成回复时出现错误: ${error instanceof Error ? error.message : String(error)}`
});
// 重置状态
generatingMessage.value = false;
processing.value = false;
currentGeneratedText.value = '';
nextTick().then(scrollToBottom);
}
};
// 滚动到聊天历史底部
const scrollToBottom = () => {
if (chatHistoryRef.value) {
chatHistoryRef.value.scrollTop = chatHistoryRef.value.scrollHeight;
}
};
// 清理资源函数
const cleanupResources = () => {
// 如果正在进行推理,停止推理过程
if (processing.value || generatingMessage.value) {
// 标记处理完成,停止状态更新
processing.value = false;
generatingMessage.value = false;
console.log('组件卸载:已停止正在进行的文本生成');
}
// 释放LLM实例资源
if (llmInstance.value) {
try {
// 调用模型实例的清理方法(如果有)
if (typeof llmInstance.value.close === 'function') {
llmInstance.value.close();
}
// 置空实例引用
llmInstance.value = null;
console.log('组件卸载:已释放模型资源');
} catch (error) {
console.error('释放模型资源时出错:', error);
}
}
};
onMounted(() => {
props.view.setTitle('LLM Demo')
})
// 在组件卸载时清理资源
onUnmounted(() => {
cleanupResources();
});
// 监听模型文件变化
watch(() => currentFiles.value, () => {
if (currentFiles.value && currentFiles.value.length > 0) {
initializeModel();
}
}, { immediate: true });
</script>
<style scoped>
.chat-container {
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
background-color: #f5f5f5;
border-radius: 8px;
overflow: hidden;
}
.loading-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100%;
width: 100%;
padding: 20px;
}
.loading-spinner {
width: 40px;
height: 40px;
border: 4px solid rgba(0, 0, 0, 0.1);
border-radius: 50%;
border-top-color: #3498db;
animation: spin 1s ease-in-out infinite;
margin-bottom: 20px;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.progress-container {
width: 80%;
height: 20px;
background-color: #f0f0f0;
border-radius: 10px;
overflow: hidden;
margin-bottom: 10px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.progress-bar {
height: 100%;
background: linear-gradient(90deg, #1e88e5, #64b5f6);
transition: width 0.3s ease;
border-radius: 10px;
}
.loading-percentage {
font-size: 18px;
font-weight: bold;
color: #1e88e5;
margin-bottom: 10px;
}
.chat-main {
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
}
.chat-history {
flex: 1;
overflow-y: auto;
padding: 20px;
display: flex;
flex-direction: column;
gap: 12px;
}
.message {
max-width: 85%;
padding: 10px 16px;
border-radius: 18px;
word-break: break-word;
}
.user-message {
align-self: flex-end;
background-color: #1e88e5;
color: white;
border-bottom-right-radius: 4px;
}
.ai-message {
align-self: flex-start;
background-color: #e0e0e0;
color: #333;
border-bottom-left-radius: 4px;
}
.markdown-content :deep(p) {
margin: 0.5em 0;
}
.markdown-content :deep(pre) {
background-color: #f0f0f0;
padding: 8px;
border-radius: 4px;
overflow-x: auto;
}
.markdown-content :deep(code) {
background-color: #f0f0f0;
padding: 2px 4px;
border-radius: 3px;
font-family: monospace;
}
.markdown-content :deep(ul),
.markdown-content :deep(ol) {
margin: 0.5em 0;
padding-left: 1.5em;
}
.markdown-content :deep(table) {
border-collapse: collapse;
margin: 1em 0;
}
.markdown-content :deep(th),
.markdown-content :deep(td) {
border: 1px solid #ddd;
padding: 4px 8px;
}
.markdown-content :deep(blockquote) {
margin: 0.5em 0;
padding-left: 1em;
border-left: 4px solid #ddd;
color: #555;
}
.generating-indicator {
display: inline-block;
margin-left: 5px;
}
.generating-indicator span {
display: inline-block;
animation: blink 1.4s infinite both;
}
.generating-indicator span:nth-child(2) {
animation-delay: 0.2s;
}
.generating-indicator span:nth-child(3) {
animation-delay: 0.4s;
}
@keyframes blink {
0% {
opacity: 0.2;
}
20% {
opacity: 1;
}
100% {
opacity: 0.2;
}
}
.chat-input {
display: flex;
padding: 10px;
background-color: white;
border-top: 1px solid #e0e0e0;
}
.chat-input textarea {
flex: 1;
border: 1px solid #e0e0e0;
border-radius: 4px;
padding: 10px;
resize: none;
height: 60px;
font-family: inherit;
font-size: 14px;
}
.chat-input button {
margin-left: 10px;
background-color: #1e88e5;
color: white;
border: none;
border-radius: 4px;
padding: 0 20px;
cursor: pointer;
font-weight: bold;
}
.chat-input button:disabled {
background-color: #cccccc;
cursor: not-allowed;
}
</style>
+35
View File
@@ -0,0 +1,35 @@
import { registerComponentOption } from '@/class/PluginCenter'
import PlayCircle from '@vicons/ionicons5/es/PlayCircle'
const pluginConfig: registerComponentOption = {
key: 'browser_llm',
name: 'Browser_LLM',
icon: PlayCircle,
getComponent: async () =>
(await import('@/plugins/browser_llm/browser_llm.vue')).default,
filter: (vDirs, vFiles) => {
if (vDirs.length > 1) {
return false
}
if (vDirs.length === 1 && vDirs[0].name !== 'tasks_genai_wasm') {
return false
}
if (vFiles.length === 0) {
return false
}
for (let i of vFiles) {
for (let s of ['model.bin']) {
if (i.name.endsWith(s)) {
return true
}
}
}
return false
},
priority: 6
}
export default pluginConfig
import { PluginExportType } from '@/types/type'
export const type = PluginExportType.component
+1 -1
View File
@@ -24,5 +24,5 @@
"@/*": ["src/*"]
}
},
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "auto-imports.d.ts", "components.d.ts"]
}
+23 -1
View File
@@ -3,13 +3,32 @@ import vue from "@vitejs/plugin-vue";
import { resolve } from "path";
// import { viteSingleFile } from 'vite-plugin-singlefile'
import { visualizer } from 'rollup-plugin-visualizer'
import AutoImport from 'unplugin-auto-import/vite'
import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'
import Components from 'unplugin-vue-components/vite'
// https://vitejs.dev/config/
export default defineConfig({
base: './',
plugins: [
vue(),
visualizer()
visualizer(),
AutoImport({
imports: [
'vue',
{
'naive-ui': [
'useDialog',
'useMessage',
'useNotification',
'useLoadingBar'
]
}
]
}),
Components({
resolvers: [NaiveUiResolver()]
})
// viteSingleFile()
],
resolve: {
@@ -42,5 +61,8 @@ export default defineConfig({
}
}
}
},
// 支持Web Worker
worker: {
}
})